[ewg] [PATCH] iscsi backport for OFED1.4
Doron Shoham
dorons at voltaire.com
Sun Jul 6 06:18:24 PDT 2008
Hi Vlad,
This is the patch for RedHat4[u4-u6]
Thanks,
Doron
Signed-off-by: Doron Shoham <dorons at voltaire.com>
---
...iscsi_01_sync_kernel_code_with_ofed_1_2_5.patch | 7858 ++++++++++++++++++++
.../backport/2.6.9_U4/iscsi_02_add_to_2_6_9.patch | 180 +
.../2.6.9_U4/iscsi_03_add_session_wq.patch | 76 +
.../2.6.9_U4/iscsi_04_inet_sock_to_opt.patch | 13 +
.../iscsi_05_release_host_lock_before_eh.patch | 60 +
.../backport/2.6.9_U4/iscsi_06_scsi_addons.patch | 75 +
..._da9c0c770e775e655e3f77c96d91ee557b117adb.patch | 44 +
..._d8196ed2181b4595eaf464a5bcbddb6c28649a39.patch | 12 +
..._1548271ece9e9312fd5feb41fd58773b56a71d39.patch | 74 +
..._77a23c21aaa723f6b0ffc4a701be8c8e5a32346d.patch | 38 +
..._b2c6416736b847b91950bd43cc5153e11a1f83ee.patch | 18 +
..._857ae0bdb72999936a28ce621e38e2e288c485da.patch | 16 +
..._8ad5781ae9702a8f95cfdf30967752e4297613ee.patch | 14 +
..._0801c242a33426fddc005c2f559a3d2fa6fca7eb.patch | 22 +
.../2.6.9_U4/iser_09_fix_inclusion_order.patch | 13 +
.../iser_10_fix_struct_scsi_host_template.patch | 31 +
.../2.6.9_U4/iser_11_add_fmr_unalign_cnt.patch | 25 +
.../backport/2.6.9_U4/iser_12_remove_hdr_max.patch | 25 +
.../iser_13_fix_netlink_kernel_create.patch | 26 +
...4_sync_attribute_container.c_from_ofed1.3.patch | 394 +
.../iser_15_fix_iscsi_free_mgmt_task.patch | 28 +
...iscsi_01_sync_kernel_code_with_ofed_1_2_5.patch | 7858 ++++++++++++++++++++
.../backport/2.6.9_U5/iscsi_02_add_to_2_6_9.patch | 180 +
.../2.6.9_U5/iscsi_03_add_session_wq.patch | 76 +
.../2.6.9_U5/iscsi_04_inet_sock_to_opt.patch | 13 +
.../iscsi_05_release_host_lock_before_eh.patch | 60 +
.../backport/2.6.9_U5/iscsi_06_scsi_addons.patch | 75 +
..._da9c0c770e775e655e3f77c96d91ee557b117adb.patch | 44 +
..._d8196ed2181b4595eaf464a5bcbddb6c28649a39.patch | 12 +
..._1548271ece9e9312fd5feb41fd58773b56a71d39.patch | 74 +
..._77a23c21aaa723f6b0ffc4a701be8c8e5a32346d.patch | 38 +
..._b2c6416736b847b91950bd43cc5153e11a1f83ee.patch | 18 +
..._857ae0bdb72999936a28ce621e38e2e288c485da.patch | 16 +
..._8ad5781ae9702a8f95cfdf30967752e4297613ee.patch | 14 +
..._0801c242a33426fddc005c2f559a3d2fa6fca7eb.patch | 22 +
.../2.6.9_U5/iser_09_fix_inclusion_order.patch | 13 +
.../iser_10_fix_struct_scsi_host_template.patch | 31 +
.../2.6.9_U5/iser_11_add_fmr_unalign_cnt.patch | 25 +
.../backport/2.6.9_U5/iser_12_remove_hdr_max.patch | 25 +
.../iser_13_fix_netlink_kernel_create.patch | 26 +
...4_sync_attribute_container.c_from_ofed1.3.patch | 394 +
.../iser_15_fix_iscsi_free_mgmt_task.patch | 28 +
...iscsi_01_sync_kernel_code_with_ofed_1_2_5.patch | 7858 ++++++++++++++++++++
.../backport/2.6.9_U6/iscsi_02_add_to_2_6_9.patch | 180 +
.../2.6.9_U6/iscsi_03_add_session_wq.patch | 76 +
.../2.6.9_U6/iscsi_04_inet_sock_to_opt.patch | 13 +
.../iscsi_05_release_host_lock_before_eh.patch | 60 +
.../backport/2.6.9_U6/iscsi_06_scsi_addons.patch | 75 +
..._da9c0c770e775e655e3f77c96d91ee557b117adb.patch | 44 +
..._d8196ed2181b4595eaf464a5bcbddb6c28649a39.patch | 12 +
..._1548271ece9e9312fd5feb41fd58773b56a71d39.patch | 74 +
..._77a23c21aaa723f6b0ffc4a701be8c8e5a32346d.patch | 38 +
..._b2c6416736b847b91950bd43cc5153e11a1f83ee.patch | 18 +
..._857ae0bdb72999936a28ce621e38e2e288c485da.patch | 16 +
..._8ad5781ae9702a8f95cfdf30967752e4297613ee.patch | 14 +
..._0801c242a33426fddc005c2f559a3d2fa6fca7eb.patch | 22 +
.../2.6.9_U6/iser_09_fix_inclusion_order.patch | 13 +
.../iser_10_fix_struct_scsi_host_template.patch | 31 +
.../2.6.9_U6/iser_11_add_fmr_unalign_cnt.patch | 25 +
.../backport/2.6.9_U6/iser_12_remove_hdr_max.patch | 25 +
.../iser_13_fix_netlink_kernel_create.patch | 26 +
...4_sync_attribute_container.c_from_ofed1.3.patch | 394 +
.../iser_15_fix_iscsi_free_mgmt_task.patch | 28 +
63 files changed, 27126 insertions(+), 0 deletions(-)
create mode 100644 kernel_patches/backport/2.6.9_U4/iscsi_01_sync_kernel_code_with_ofed_1_2_5.patch
create mode 100644 kernel_patches/backport/2.6.9_U4/iscsi_02_add_to_2_6_9.patch
create mode 100644 kernel_patches/backport/2.6.9_U4/iscsi_03_add_session_wq.patch
create mode 100644 kernel_patches/backport/2.6.9_U4/iscsi_04_inet_sock_to_opt.patch
create mode 100644 kernel_patches/backport/2.6.9_U4/iscsi_05_release_host_lock_before_eh.patch
create mode 100644 kernel_patches/backport/2.6.9_U4/iscsi_06_scsi_addons.patch
create mode 100644 kernel_patches/backport/2.6.9_U4/iser_01_revert_da9c0c770e775e655e3f77c96d91ee557b117adb.patch
create mode 100644 kernel_patches/backport/2.6.9_U4/iser_02_revert_d8196ed2181b4595eaf464a5bcbddb6c28649a39.patch
create mode 100644 kernel_patches/backport/2.6.9_U4/iser_03_revert_1548271ece9e9312fd5feb41fd58773b56a71d39.patch
create mode 100644 kernel_patches/backport/2.6.9_U4/iser_04_revert_77a23c21aaa723f6b0ffc4a701be8c8e5a32346d.patch
create mode 100644 kernel_patches/backport/2.6.9_U4/iser_05_revert_b2c6416736b847b91950bd43cc5153e11a1f83ee.patch
create mode 100644 kernel_patches/backport/2.6.9_U4/iser_06_revert_857ae0bdb72999936a28ce621e38e2e288c485da.patch
create mode 100644 kernel_patches/backport/2.6.9_U4/iser_07_revert_8ad5781ae9702a8f95cfdf30967752e4297613ee.patch
create mode 100644 kernel_patches/backport/2.6.9_U4/iser_08_revert_0801c242a33426fddc005c2f559a3d2fa6fca7eb.patch
create mode 100644 kernel_patches/backport/2.6.9_U4/iser_09_fix_inclusion_order.patch
create mode 100644 kernel_patches/backport/2.6.9_U4/iser_10_fix_struct_scsi_host_template.patch
create mode 100644 kernel_patches/backport/2.6.9_U4/iser_11_add_fmr_unalign_cnt.patch
create mode 100644 kernel_patches/backport/2.6.9_U4/iser_12_remove_hdr_max.patch
create mode 100644 kernel_patches/backport/2.6.9_U4/iser_13_fix_netlink_kernel_create.patch
create mode 100644 kernel_patches/backport/2.6.9_U4/iser_14_sync_attribute_container.c_from_ofed1.3.patch
create mode 100644 kernel_patches/backport/2.6.9_U4/iser_15_fix_iscsi_free_mgmt_task.patch
create mode 100644 kernel_patches/backport/2.6.9_U5/iscsi_01_sync_kernel_code_with_ofed_1_2_5.patch
create mode 100644 kernel_patches/backport/2.6.9_U5/iscsi_02_add_to_2_6_9.patch
create mode 100644 kernel_patches/backport/2.6.9_U5/iscsi_03_add_session_wq.patch
create mode 100644 kernel_patches/backport/2.6.9_U5/iscsi_04_inet_sock_to_opt.patch
create mode 100644 kernel_patches/backport/2.6.9_U5/iscsi_05_release_host_lock_before_eh.patch
create mode 100644 kernel_patches/backport/2.6.9_U5/iscsi_06_scsi_addons.patch
create mode 100644 kernel_patches/backport/2.6.9_U5/iser_01_revert_da9c0c770e775e655e3f77c96d91ee557b117adb.patch
create mode 100644 kernel_patches/backport/2.6.9_U5/iser_02_revert_d8196ed2181b4595eaf464a5bcbddb6c28649a39.patch
create mode 100644 kernel_patches/backport/2.6.9_U5/iser_03_revert_1548271ece9e9312fd5feb41fd58773b56a71d39.patch
create mode 100644 kernel_patches/backport/2.6.9_U5/iser_04_revert_77a23c21aaa723f6b0ffc4a701be8c8e5a32346d.patch
create mode 100644 kernel_patches/backport/2.6.9_U5/iser_05_revert_b2c6416736b847b91950bd43cc5153e11a1f83ee.patch
create mode 100644 kernel_patches/backport/2.6.9_U5/iser_06_revert_857ae0bdb72999936a28ce621e38e2e288c485da.patch
create mode 100644 kernel_patches/backport/2.6.9_U5/iser_07_revert_8ad5781ae9702a8f95cfdf30967752e4297613ee.patch
create mode 100644 kernel_patches/backport/2.6.9_U5/iser_08_revert_0801c242a33426fddc005c2f559a3d2fa6fca7eb.patch
create mode 100644 kernel_patches/backport/2.6.9_U5/iser_09_fix_inclusion_order.patch
create mode 100644 kernel_patches/backport/2.6.9_U5/iser_10_fix_struct_scsi_host_template.patch
create mode 100644 kernel_patches/backport/2.6.9_U5/iser_11_add_fmr_unalign_cnt.patch
create mode 100644 kernel_patches/backport/2.6.9_U5/iser_12_remove_hdr_max.patch
create mode 100644 kernel_patches/backport/2.6.9_U5/iser_13_fix_netlink_kernel_create.patch
create mode 100644 kernel_patches/backport/2.6.9_U5/iser_14_sync_attribute_container.c_from_ofed1.3.patch
create mode 100644 kernel_patches/backport/2.6.9_U5/iser_15_fix_iscsi_free_mgmt_task.patch
create mode 100644 kernel_patches/backport/2.6.9_U6/iscsi_01_sync_kernel_code_with_ofed_1_2_5.patch
create mode 100644 kernel_patches/backport/2.6.9_U6/iscsi_02_add_to_2_6_9.patch
create mode 100644 kernel_patches/backport/2.6.9_U6/iscsi_03_add_session_wq.patch
create mode 100644 kernel_patches/backport/2.6.9_U6/iscsi_04_inet_sock_to_opt.patch
create mode 100644 kernel_patches/backport/2.6.9_U6/iscsi_05_release_host_lock_before_eh.patch
create mode 100644 kernel_patches/backport/2.6.9_U6/iscsi_06_scsi_addons.patch
create mode 100644 kernel_patches/backport/2.6.9_U6/iser_01_revert_da9c0c770e775e655e3f77c96d91ee557b117adb.patch
create mode 100644 kernel_patches/backport/2.6.9_U6/iser_02_revert_d8196ed2181b4595eaf464a5bcbddb6c28649a39.patch
create mode 100644 kernel_patches/backport/2.6.9_U6/iser_03_revert_1548271ece9e9312fd5feb41fd58773b56a71d39.patch
create mode 100644 kernel_patches/backport/2.6.9_U6/iser_04_revert_77a23c21aaa723f6b0ffc4a701be8c8e5a32346d.patch
create mode 100644 kernel_patches/backport/2.6.9_U6/iser_05_revert_b2c6416736b847b91950bd43cc5153e11a1f83ee.patch
create mode 100644 kernel_patches/backport/2.6.9_U6/iser_06_revert_857ae0bdb72999936a28ce621e38e2e288c485da.patch
create mode 100644 kernel_patches/backport/2.6.9_U6/iser_07_revert_8ad5781ae9702a8f95cfdf30967752e4297613ee.patch
create mode 100644 kernel_patches/backport/2.6.9_U6/iser_08_revert_0801c242a33426fddc005c2f559a3d2fa6fca7eb.patch
create mode 100644 kernel_patches/backport/2.6.9_U6/iser_09_fix_inclusion_order.patch
create mode 100644 kernel_patches/backport/2.6.9_U6/iser_10_fix_struct_scsi_host_template.patch
create mode 100644 kernel_patches/backport/2.6.9_U6/iser_11_add_fmr_unalign_cnt.patch
create mode 100644 kernel_patches/backport/2.6.9_U6/iser_12_remove_hdr_max.patch
create mode 100644 kernel_patches/backport/2.6.9_U6/iser_13_fix_netlink_kernel_create.patch
create mode 100644 kernel_patches/backport/2.6.9_U6/iser_14_sync_attribute_container.c_from_ofed1.3.patch
create mode 100644 kernel_patches/backport/2.6.9_U6/iser_15_fix_iscsi_free_mgmt_task.patch
diff --git a/kernel_patches/backport/2.6.9_U4/iscsi_01_sync_kernel_code_with_ofed_1_2_5.patch b/kernel_patches/backport/2.6.9_U4/iscsi_01_sync_kernel_code_with_ofed_1_2_5.patch
new file mode 100644
index 0000000..aeacec6
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U4/iscsi_01_sync_kernel_code_with_ofed_1_2_5.patch
@@ -0,0 +1,7858 @@
+From a2ee7540d79a7a287205fd9b75311a0cfeb64e38 Mon Sep 17 00:00:00 2001
+From: Doron Shoham <dorons at voltaire.com>
+Date: Thu, 3 Jul 2008 13:12:22 +0300
+Subject: [PATCH] sync_kernel_code_with_ofed_1_2_5
+
+Signed-off-by: Doron Shoham <dorons at voltaire.com>
+---
+ drivers/scsi/iscsi_tcp.c | 2313 +++++++++++++++++++----------------
+ drivers/scsi/iscsi_tcp.h | 131 ++-
+ drivers/scsi/libiscsi.c | 1800 +++++++++------------------
+ drivers/scsi/scsi_transport_iscsi.c | 869 +++++---------
+ include/scsi/iscsi_if.h | 54 -
+ include/scsi/iscsi_proto.h | 22 +-
+ include/scsi/libiscsi.h | 188 +---
+ include/scsi/scsi_transport_iscsi.h | 65 +-
+ 8 files changed, 2301 insertions(+), 3141 deletions(-)
+
+diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c
+index 72b9b2a..c9a3abf 100644
+--- a/drivers/scsi/iscsi_tcp.c
++++ b/drivers/scsi/iscsi_tcp.c
+@@ -29,15 +29,14 @@
+ #include <linux/types.h>
+ #include <linux/list.h>
+ #include <linux/inet.h>
+-#include <linux/file.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_device.h>
+ #include <scsi/scsi_host.h>
+ #include <scsi/scsi.h>
+ #include <scsi/scsi_transport_iscsi.h>
+@@ -48,7 +47,7 @@ MODULE_AUTHOR("Dmitry Yusupov <dmitry_yus at yahoo.com>, "
+ "Alex Aizman <itn780 at yahoo.com>");
+ MODULE_DESCRIPTION("iSCSI/TCP data-path");
+ MODULE_LICENSE("GPL");
+-#undef DEBUG_TCP
++/* #define DEBUG_TCP */
+ #define DEBUG_ASSERT
+
+ #ifdef DEBUG_TCP
+@@ -67,429 +66,118 @@ MODULE_LICENSE("GPL");
+ static unsigned int iscsi_max_lun = 512;
+ module_param_named(max_lun, iscsi_max_lun, uint, S_IRUGO);
+
+-static int iscsi_tcp_hdr_recv_done(struct iscsi_tcp_conn *tcp_conn,
+- struct iscsi_segment *segment);
+-
+-/*
+- * Scatterlist handling: inside the iscsi_segment, we
+- * remember an index into the scatterlist, and set data/size
+- * to the current scatterlist entry. For highmem pages, we
+- * kmap as needed.
+- *
+- * Note that the page is unmapped when we return from
+- * TCP's data_ready handler, so we may end up mapping and
+- * unmapping the same page repeatedly. The whole reason
+- * for this is that we shouldn't keep the page mapped
+- * outside the softirq.
+- */
+-
+-/**
+- * iscsi_tcp_segment_init_sg - init indicated scatterlist entry
+- * @segment: the buffer object
+- * @sg: scatterlist
+- * @offset: byte offset into that sg entry
+- *
+- * This function sets up the segment so that subsequent
+- * data is copied to the indicated sg entry, at the given
+- * offset.
+- */
+ static inline void
+-iscsi_tcp_segment_init_sg(struct iscsi_segment *segment,
+- struct scatterlist *sg, unsigned int offset)
++iscsi_buf_init_iov(struct iscsi_buf *ibuf, char *vbuf, int size)
+ {
+- segment->sg = sg;
+- segment->sg_offset = offset;
+- segment->size = min(sg->length - offset,
+- segment->total_size - segment->total_copied);
+- segment->data = NULL;
++ 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;
+ }
+
+-/**
+- * iscsi_tcp_segment_map - map the current S/G page
+- * @segment: iscsi_segment
+- * @recv: 1 if called from recv path
+- *
+- * We only need to possibly kmap data if scatter lists are being used,
+- * because the iscsi passthrough and internal IO paths will never use high
+- * mem pages.
+- */
+ static inline void
+-iscsi_tcp_segment_map(struct iscsi_segment *segment, int recv)
++iscsi_buf_init_sg(struct iscsi_buf *ibuf, struct scatterlist *sg)
+ {
+- struct scatterlist *sg;
+-
+- if (segment->data != NULL || !segment->sg)
+- return;
+-
+- sg = segment->sg;
+- BUG_ON(segment->sg_mapped);
+- BUG_ON(sg->length == 0);
+-
++ ibuf->sg.page = sg->page;
++ ibuf->sg.offset = sg->offset;
++ ibuf->sg.length = sg->length;
+ /*
+- * If the page count is greater than one it is ok to send
+- * to the network layer's zero copy send path. If not we
+- * have to go the slow sendmsg path. We always map for the
+- * recv path.
++ * Fastpath: sg element fits into single page
+ */
+- if (page_count(sg_page(sg)) >= 1 && !recv)
+- return;
+-
+- debug_tcp("iscsi_tcp_segment_map %s %p\n", recv ? "recv" : "xmit",
+- segment);
+- segment->sg_mapped = kmap_atomic(sg_page(sg), KM_SOFTIRQ0);
+- segment->data = segment->sg_mapped + sg->offset + segment->sg_offset;
++ if (sg->length + sg->offset <= PAGE_SIZE && !PageSlab(sg->page))
++ ibuf->use_sendmsg = 0;
++ else
++ ibuf->use_sendmsg = 1;
++ ibuf->sent = 0;
+ }
+
+-static inline void
+-iscsi_tcp_segment_unmap(struct iscsi_segment *segment)
++static inline int
++iscsi_buf_left(struct iscsi_buf *ibuf)
+ {
+- debug_tcp("iscsi_tcp_segment_unmap %p\n", segment);
++ int rc;
+
+- if (segment->sg_mapped) {
+- debug_tcp("iscsi_tcp_segment_unmap valid\n");
+- kunmap_atomic(segment->sg_mapped, KM_SOFTIRQ0);
+- segment->sg_mapped = NULL;
+- segment->data = NULL;
+- }
++ rc = ibuf->sg.length - ibuf->sent;
++ BUG_ON(rc < 0);
++ return rc;
+ }
+
+-/*
+- * Splice the digest buffer into the buffer
+- */
+ static inline void
+-iscsi_tcp_segment_splice_digest(struct iscsi_segment *segment, void *digest)
++iscsi_hdr_digest(struct iscsi_conn *conn, struct iscsi_buf *buf,
++ u8* crc)
+ {
+- segment->data = digest;
+- segment->digest_len = ISCSI_DIGEST_SIZE;
+- segment->total_size += ISCSI_DIGEST_SIZE;
+- segment->size = ISCSI_DIGEST_SIZE;
+- segment->copied = 0;
+- segment->sg = NULL;
+- segment->hash = NULL;
++ 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;
+ }
+
+-/**
+- * iscsi_tcp_segment_done - check whether the segment is complete
+- * @segment: iscsi segment to check
+- * @recv: set to one of this is called from the recv path
+- * @copied: number of bytes copied
+- *
+- * Check if we're done receiving this segment. If the receive
+- * buffer is full but we expect more data, move on to the
+- * next entry in the scatterlist.
+- *
+- * If the amount of data we received isn't a multiple of 4,
+- * we will transparently receive the pad bytes, too.
+- *
+- * This function must be re-entrant.
+- */
+ static inline int
+-iscsi_tcp_segment_done(struct iscsi_segment *segment, int recv, unsigned copied)
++iscsi_hdr_extract(struct iscsi_tcp_conn *tcp_conn)
+ {
+- static unsigned char padbuf[ISCSI_PAD_LEN];
+- struct scatterlist sg;
+- unsigned int pad;
++ struct sk_buff *skb = tcp_conn->in.skb;
++
++ tcp_conn->in.zero_copy_hdr = 0;
+
+- debug_tcp("copied %u %u size %u %s\n", segment->copied, copied,
+- segment->size, recv ? "recv" : "xmit");
+- if (segment->hash && copied) {
++ if (tcp_conn->in.copy >= tcp_conn->hdr_size &&
++ tcp_conn->in_progress == IN_PROGRESS_WAIT_HEADER) {
+ /*
+- * If a segment is kmapd we must unmap it before sending
+- * to the crypto layer since that will try to kmap it again.
++ * Zero-copy PDU Header: using connection context
++ * to store header pointer.
+ */
+- iscsi_tcp_segment_unmap(segment);
+-
+- if (!segment->data) {
+- sg_init_table(&sg, 1);
+- sg_set_page(&sg, sg_page(segment->sg), copied,
+- segment->copied + segment->sg_offset +
+- segment->sg->offset);
+- } else
+- sg_init_one(&sg, segment->data + segment->copied,
+- copied);
+- crypto_hash_update(segment->hash, &sg, copied);
+- }
+-
+- segment->copied += copied;
+- if (segment->copied < segment->size) {
+- iscsi_tcp_segment_map(segment, recv);
+- return 0;
+- }
+-
+- segment->total_copied += segment->copied;
+- segment->copied = 0;
+- segment->size = 0;
+-
+- /* Unmap the current scatterlist page, if there is one. */
+- iscsi_tcp_segment_unmap(segment);
+-
+- /* Do we have more scatterlist entries? */
+- debug_tcp("total copied %u total size %u\n", segment->total_copied,
+- segment->total_size);
+- if (segment->total_copied < segment->total_size) {
+- /* Proceed to the next entry in the scatterlist. */
+- iscsi_tcp_segment_init_sg(segment, sg_next(segment->sg),
+- 0);
+- iscsi_tcp_segment_map(segment, recv);
+- BUG_ON(segment->size == 0);
+- return 0;
+- }
+-
+- /* Do we need to handle padding? */
+- pad = iscsi_padding(segment->total_copied);
+- if (pad != 0) {
+- debug_tcp("consume %d pad bytes\n", pad);
+- segment->total_size += pad;
+- segment->size = pad;
+- segment->data = padbuf;
+- return 0;
+- }
+-
+- /*
+- * Set us up for transferring the data digest. hdr digest
+- * is completely handled in hdr done function.
+- */
+- if (segment->hash) {
+- crypto_hash_final(segment->hash, segment->digest);
+- iscsi_tcp_segment_splice_digest(segment,
+- recv ? segment->recv_digest : segment->digest);
+- return 0;
+- }
+-
+- return 1;
+-}
+-
+-/**
+- * iscsi_tcp_xmit_segment - transmit segment
+- * @tcp_conn: the iSCSI TCP connection
+- * @segment: the buffer to transmnit
+- *
+- * This function transmits as much of the buffer as
+- * the network layer will accept, and returns the number of
+- * bytes transmitted.
+- *
+- * If CRC hashing is enabled, the function will compute the
+- * hash as it goes. When the entire segment has been transmitted,
+- * it will retrieve the hash value and send it as well.
+- */
+-static int
+-iscsi_tcp_xmit_segment(struct iscsi_tcp_conn *tcp_conn,
+- struct iscsi_segment *segment)
+-{
+- struct socket *sk = tcp_conn->sock;
+- unsigned int copied = 0;
+- int r = 0;
+-
+- while (!iscsi_tcp_segment_done(segment, 0, r)) {
+- struct scatterlist *sg;
+- unsigned int offset, copy;
+- int flags = 0;
+-
+- r = 0;
+- offset = segment->copied;
+- copy = segment->size - offset;
+-
+- if (segment->total_copied + segment->size < segment->total_size)
+- flags |= MSG_MORE;
+-
+- /* Use sendpage if we can; else fall back to sendmsg */
+- if (!segment->data) {
+- sg = segment->sg;
+- offset += segment->sg_offset + sg->offset;
+- r = tcp_conn->sendpage(sk, sg_page(sg), offset, copy,
+- flags);
++ 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 {
+- struct msghdr msg = { .msg_flags = flags };
+- struct kvec iov = {
+- .iov_base = segment->data + offset,
+- .iov_len = copy
+- };
+-
+- r = kernel_sendmsg(sk, &msg, &iov, 1, copy);
+- }
+-
+- if (r < 0) {
+- iscsi_tcp_segment_unmap(segment);
+- if (copied || r == -EAGAIN)
+- break;
+- return r;
++ /* 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;
+ }
+- copied += r;
+- }
+- return copied;
+-}
+-
+-/**
+- * iscsi_tcp_segment_recv - copy data to segment
+- * @tcp_conn: the iSCSI TCP connection
+- * @segment: the buffer to copy to
+- * @ptr: data pointer
+- * @len: amount of data available
+- *
+- * This function copies up to @len bytes to the
+- * given buffer, and returns the number of bytes
+- * consumed, which can actually be less than @len.
+- *
+- * If hash digest is enabled, the function will update the
+- * hash while copying.
+- * Combining these two operations doesn't buy us a lot (yet),
+- * but in the future we could implement combined copy+crc,
+- * just way we do for network layer checksums.
+- */
+-static int
+-iscsi_tcp_segment_recv(struct iscsi_tcp_conn *tcp_conn,
+- struct iscsi_segment *segment, const void *ptr,
+- unsigned int len)
+-{
+- unsigned int copy = 0, copied = 0;
+-
+- while (!iscsi_tcp_segment_done(segment, 1, copy)) {
+- if (copied == len) {
+- debug_tcp("iscsi_tcp_segment_recv copied %d bytes\n",
+- len);
+- break;
+- }
+-
+- copy = min(len - copied, segment->size - segment->copied);
+- debug_tcp("iscsi_tcp_segment_recv copying %d\n", copy);
+- memcpy(segment->data + segment->copied, ptr + copied, copy);
+- copied += copy;
+- }
+- return copied;
+-}
+-
+-static inline void
+-iscsi_tcp_dgst_header(struct hash_desc *hash, const void *hdr, size_t hdrlen,
+- unsigned char digest[ISCSI_DIGEST_SIZE])
+-{
+- struct scatterlist sg;
++ tcp_conn->in.offset += tcp_conn->hdr_size;
++ tcp_conn->in.copy -= tcp_conn->hdr_size;
++ } else {
++ int hdr_remains;
++ int copylen;
+
+- sg_init_one(&sg, hdr, hdrlen);
+- crypto_hash_digest(hash, &sg, hdrlen, digest);
+-}
++ /*
++ * PDU header scattered across SKB's,
++ * copying it... This'll happen quite rarely.
++ */
+
+-static inline int
+-iscsi_tcp_dgst_verify(struct iscsi_tcp_conn *tcp_conn,
+- struct iscsi_segment *segment)
+-{
+- if (!segment->digest_len)
+- return 1;
++ if (tcp_conn->in_progress == IN_PROGRESS_WAIT_HEADER)
++ tcp_conn->in.hdr_offset = 0;
+
+- if (memcmp(segment->recv_digest, segment->digest,
+- segment->digest_len)) {
+- debug_scsi("digest mismatch\n");
+- return 0;
+- }
++ hdr_remains = tcp_conn->hdr_size - tcp_conn->in.hdr_offset;
++ BUG_ON(hdr_remains <= 0);
+
+- return 1;
+-}
++ 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);
+
+-/*
+- * Helper function to set up segment buffer
+- */
+-static inline void
+-__iscsi_segment_init(struct iscsi_segment *segment, size_t size,
+- iscsi_segment_done_fn_t *done, struct hash_desc *hash)
+-{
+- memset(segment, 0, sizeof(*segment));
+- segment->total_size = size;
+- segment->done = done;
+-
+- if (hash) {
+- segment->hash = hash;
+- crypto_hash_init(hash);
+- }
+-}
++ 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);
+
+-static inline void
+-iscsi_segment_init_linear(struct iscsi_segment *segment, void *data,
+- size_t size, iscsi_segment_done_fn_t *done,
+- struct hash_desc *hash)
+-{
+- __iscsi_segment_init(segment, size, done, hash);
+- segment->data = data;
+- segment->size = size;
+-}
+-
+-static inline int
+-iscsi_segment_seek_sg(struct iscsi_segment *segment,
+- struct scatterlist *sg_list, unsigned int sg_count,
+- unsigned int offset, size_t size,
+- iscsi_segment_done_fn_t *done, struct hash_desc *hash)
+-{
+- struct scatterlist *sg;
+- unsigned int i;
+-
+- debug_scsi("iscsi_segment_seek_sg offset %u size %llu\n",
+- offset, size);
+- __iscsi_segment_init(segment, size, done, hash);
+- for_each_sg(sg_list, sg, sg_count, i) {
+- debug_scsi("sg %d, len %u offset %u\n", i, sg->length,
+- sg->offset);
+- if (offset < sg->length) {
+- iscsi_tcp_segment_init_sg(segment, sg, offset);
+- return 0;
++ 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;
+ }
+- offset -= sg->length;
++ tcp_conn->in.hdr = &tcp_conn->hdr;
++ tcp_conn->discontiguous_hdr_cnt++;
++ tcp_conn->in_progress = IN_PROGRESS_WAIT_HEADER;
+ }
+
+- return ISCSI_ERR_DATA_OFFSET;
+-}
+-
+-/**
+- * iscsi_tcp_hdr_recv_prep - prep segment for hdr reception
+- * @tcp_conn: iscsi connection to prep for
+- *
+- * This function always passes NULL for the hash argument, because when this
+- * function is called we do not yet know the final size of the header and want
+- * to delay the digest processing until we know that.
+- */
+-static void
+-iscsi_tcp_hdr_recv_prep(struct iscsi_tcp_conn *tcp_conn)
+-{
+- debug_tcp("iscsi_tcp_hdr_recv_prep(%p%s)\n", tcp_conn,
+- tcp_conn->iscsi_conn->hdrdgst_en ? ", digest enabled" : "");
+- iscsi_segment_init_linear(&tcp_conn->in.segment,
+- tcp_conn->in.hdr_buf, sizeof(struct iscsi_hdr),
+- iscsi_tcp_hdr_recv_done, NULL);
+-}
+-
+-/*
+- * Handle incoming reply to any other type of command
+- */
+-static int
+-iscsi_tcp_data_recv_done(struct iscsi_tcp_conn *tcp_conn,
+- struct iscsi_segment *segment)
+-{
+- struct iscsi_conn *conn = tcp_conn->iscsi_conn;
+- int rc = 0;
+-
+- if (!iscsi_tcp_dgst_verify(tcp_conn, segment))
+- return ISCSI_ERR_DATA_DGST;
+-
+- rc = iscsi_complete_pdu(conn, tcp_conn->in.hdr,
+- conn->data, tcp_conn->in.datalen);
+- if (rc)
+- return rc;
+-
+- iscsi_tcp_hdr_recv_prep(tcp_conn);
+ return 0;
+ }
+
+-static void
+-iscsi_tcp_data_recv_prep(struct iscsi_tcp_conn *tcp_conn)
+-{
+- struct iscsi_conn *conn = tcp_conn->iscsi_conn;
+- struct hash_desc *rx_hash = NULL;
+-
+- if (conn->datadgst_en)
+- rx_hash = &tcp_conn->rx_hash;
+-
+- iscsi_segment_init_linear(&tcp_conn->in.segment,
+- conn->data, tcp_conn->in.datalen,
+- iscsi_tcp_data_recv_done, rx_hash);
+-}
+-
+ /*
+ * must be called with session lock
+ */
+@@ -498,6 +186,7 @@ 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*))) {
+@@ -506,12 +195,12 @@ iscsi_tcp_cleanup_ctask(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
+ debug_scsi("iscsi_tcp_cleanup_ctask pending r2t dropped\n");
+ }
+
+- r2t = tcp_ctask->r2t;
+- if (r2t != NULL) {
+- __kfifo_put(tcp_ctask->r2tpool.queue, (void*)&r2t,
+- sizeof(void*));
+- tcp_ctask->r2t = NULL;
+- }
++ sc = ctask->sc;
++ if (unlikely(!sc))
++ return;
++
++ tcp_ctask->xmstate = XMSTATE_IDLE;
++ tcp_ctask->r2t = NULL;
+ }
+
+ /**
+@@ -522,49 +211,52 @@ iscsi_tcp_cleanup_ctask(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
+ 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;
+- struct scsi_cmnd *sc = ctask->sc;
+ int datasn = be32_to_cpu(rhdr->datasn);
+- unsigned total_in_length = scsi_in(sc)->length;
+
+- iscsi_update_cmdsn(session, (struct iscsi_nopin*)rhdr);
++ 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 (tcp_ctask->exp_datasn != datasn) {
+- debug_tcp("%s: ctask->exp_datasn(%d) != rhdr->datasn(%d)\n",
+- __FUNCTION__, tcp_ctask->exp_datasn, datasn);
++ if (ctask->datasn != datasn)
+ return ISCSI_ERR_DATASN;
+- }
+
+- tcp_ctask->exp_datasn++;
++ ctask->datasn++;
+
+ tcp_ctask->data_offset = be32_to_cpu(rhdr->offset);
+- if (tcp_ctask->data_offset + tcp_conn->in.datalen > total_in_length) {
+- debug_tcp("%s: data_offset(%d) + data_len(%d) > total_length_in(%d)\n",
+- __FUNCTION__, tcp_ctask->data_offset,
+- tcp_conn->in.datalen, total_in_length);
++ if (tcp_ctask->data_offset + tcp_conn->in.datalen > ctask->total_length)
+ return ISCSI_ERR_DATA_OFFSET;
+- }
+
+ if (rhdr->flags & ISCSI_FLAG_DATA_STATUS) {
+- sc->result = (DID_OK << 16) | rhdr->cmd_status;
++ struct scsi_cmnd *sc = ctask->sc;
++
+ conn->exp_statsn = be32_to_cpu(rhdr->statsn) + 1;
+- if (rhdr->flags & (ISCSI_FLAG_DATA_UNDERFLOW |
+- ISCSI_FLAG_DATA_OVERFLOW)) {
++ if (rhdr->flags & ISCSI_FLAG_DATA_UNDERFLOW) {
+ int res_count = be32_to_cpu(rhdr->residual_count);
+
+ if (res_count > 0 &&
+- (rhdr->flags & ISCSI_FLAG_CMD_OVERFLOW ||
+- res_count <= total_in_length))
+- scsi_in(sc)->resid = res_count;
+- else
++ 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++;
+@@ -588,6 +280,7 @@ 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));
+@@ -611,6 +304,43 @@ iscsi_solicit_data_init(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
+ 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;
++ }
+ }
+
+ /**
+@@ -630,25 +360,27 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
+ int rc;
+
+ if (tcp_conn->in.datalen) {
+- iscsi_conn_printk(KERN_ERR, conn,
+- "invalid R2t with datalen %d\n",
+- 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_datasn != r2tsn){
+- debug_tcp("%s: ctask->exp_datasn(%d) != rhdr->r2tsn(%d)\n",
+- __FUNCTION__, tcp_ctask->exp_datasn, r2tsn);
++ if (tcp_ctask->exp_r2tsn && tcp_ctask->exp_r2tsn != r2tsn)
+ return ISCSI_ERR_R2TSN;
+- }
+
+- /* fill-in new R2T associated with the task */
+- iscsi_update_cmdsn(session, (struct iscsi_nopin*)rhdr);
++ rc = iscsi_check_assign_cmdsn(session, (struct iscsi_nopin*)rhdr);
++ if (rc)
++ return rc;
+
+- if (!ctask->sc || session->state != ISCSI_STATE_LOGGED_IN) {
+- iscsi_conn_printk(KERN_INFO, conn,
+- "dropping R2T itt %d in recovery.\n",
+- ctask->itt);
++ /* 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;
+ }
+
+@@ -658,10 +390,8 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
+ r2t->exp_statsn = rhdr->statsn;
+ r2t->data_length = be32_to_cpu(rhdr->data_length);
+ if (r2t->data_length == 0) {
+- iscsi_conn_printk(KERN_ERR, conn,
+- "invalid R2T with zero data len\n");
+- __kfifo_put(tcp_ctask->r2tpool.queue, (void*)&r2t,
+- sizeof(void*));
++ printk(KERN_ERR "iscsi_tcp: invalid R2T with zero data len\n");
++ spin_unlock(&session->lock);
+ return ISCSI_ERR_DATALEN;
+ }
+
+@@ -671,13 +401,11 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
+ r2t->data_length, session->max_burst);
+
+ r2t->data_offset = be32_to_cpu(rhdr->data_offset);
+- if (r2t->data_offset + r2t->data_length > scsi_out(ctask->sc)->length) {
+- iscsi_conn_printk(KERN_ERR, conn,
+- "invalid R2T with data len %u at offset %u "
+- "and total length %d\n", r2t->data_length,
+- r2t->data_offset, scsi_out(ctask->sc)->length);
+- __kfifo_put(tcp_ctask->r2tpool.queue, (void*)&r2t,
+- sizeof(void*));
++ 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;
+ }
+
+@@ -686,134 +414,108 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
+
+ iscsi_solicit_data_init(conn, ctask, r2t);
+
+- tcp_ctask->exp_datasn = r2tsn + 1;
++ tcp_ctask->exp_r2tsn = r2tsn + 1;
+ __kfifo_put(tcp_ctask->r2tqueue, (void*)&r2t, sizeof(void*));
+- conn->r2t_pdus_cnt++;
++ tcp_ctask->xmstate |= XMSTATE_SOL_HDR;
++ list_move_tail(&ctask->running, &conn->xmitqueue);
+
+- iscsi_requeue_ctask(ctask);
+- return 0;
+-}
+-
+-/*
+- * Handle incoming reply to DataIn command
+- */
+-static int
+-iscsi_tcp_process_data_in(struct iscsi_tcp_conn *tcp_conn,
+- struct iscsi_segment *segment)
+-{
+- struct iscsi_conn *conn = tcp_conn->iscsi_conn;
+- struct iscsi_hdr *hdr = tcp_conn->in.hdr;
+- int rc;
+-
+- if (!iscsi_tcp_dgst_verify(tcp_conn, segment))
+- return ISCSI_ERR_DATA_DGST;
+-
+- /* check for non-exceptional status */
+- if (hdr->flags & ISCSI_FLAG_DATA_STATUS) {
+- rc = iscsi_complete_pdu(conn, tcp_conn->in.hdr, NULL, 0);
+- if (rc)
+- return rc;
+- }
++ scsi_queue_work(session->host, &conn->xmitwork);
++ conn->r2t_pdus_cnt++;
++ spin_unlock(&session->lock);
+
+- iscsi_tcp_hdr_recv_prep(tcp_conn);
+ return 0;
+ }
+
+-/**
+- * iscsi_tcp_hdr_dissect - process PDU header
+- * @conn: iSCSI connection
+- * @hdr: PDU header
+- *
+- * This function analyzes the header of the PDU received,
+- * and performs several sanity checks. If the PDU is accompanied
+- * by data, the receive buffer is set up to copy the incoming data
+- * to the correct location.
+- */
+ static int
+-iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
++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;
+- struct iscsi_cmd_task *ctask;
+- uint32_t itt;
++ 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) {
+- iscsi_conn_printk(KERN_ERR, conn,
+- "iscsi_tcp: datalen %d > %d\n",
+- 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;
+
+- /* Additional header segments. So far, we don't
+- * process additional headers.
+- */
++ /* 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)
++ 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 ahslen %d datalen %d\n",
+- opcode, ahslen, tcp_conn->in.datalen);
++ 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:
+- ctask = session->cmds[itt];
+- spin_lock(&conn->session->lock);
+- rc = iscsi_data_rsp(conn, ctask);
+- spin_unlock(&conn->session->lock);
++ tcp_conn->in.ctask = session->cmds[itt];
++ rc = iscsi_data_rsp(conn, tcp_conn->in.ctask);
+ if (rc)
+ return rc;
+- if (tcp_conn->in.datalen) {
+- struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+- struct hash_desc *rx_hash = NULL;
+- struct scsi_data_buffer *sdb = scsi_in(ctask->sc);
+-
+- /*
+- * Setup copy of Data-In into the Scsi_Cmnd
+- * Scatterlist case:
+- * We set up the iscsi_segment to point to the next
+- * scatterlist entry to copy to. As we go along,
+- * we move on to the next scatterlist entry and
+- * update the digest per-entry.
+- */
+- if (conn->datadgst_en)
+- rx_hash = &tcp_conn->rx_hash;
+-
+- debug_tcp("iscsi_tcp_begin_data_in(%p, offset=%d, "
+- "datalen=%d)\n", tcp_conn,
+- tcp_ctask->data_offset,
+- tcp_conn->in.datalen);
+- return iscsi_segment_seek_sg(&tcp_conn->in.segment,
+- sdb->table.sgl,
+- sdb->table.nents,
+- tcp_ctask->data_offset,
+- tcp_conn->in.datalen,
+- iscsi_tcp_process_data_in,
+- rx_hash);
+- }
+ /* fall through */
+ case ISCSI_OP_SCSI_CMD_RSP:
+- if (tcp_conn->in.datalen) {
+- iscsi_tcp_data_recv_prep(tcp_conn);
+- return 0;
+- }
+- rc = iscsi_complete_pdu(conn, hdr, NULL, 0);
++ 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:
+- ctask = session->cmds[itt];
++ tcp_conn->in.ctask = session->cmds[itt];
+ if (ahslen)
+ rc = ISCSI_ERR_AHSLEN;
+- else if (ctask->sc->sc_data_direction == DMA_TO_DEVICE) {
+- spin_lock(&session->lock);
+- rc = iscsi_r2t_rsp(conn, ctask);
+- spin_unlock(&session->lock);
+- } else
++ 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:
+@@ -825,24 +527,18 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
+ * 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) {
+- iscsi_conn_printk(KERN_ERR, conn,
+- "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);
++ 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 there's data coming in with the response,
+- * receive it to the connection's buffer.
+- */
+- if (tcp_conn->in.datalen) {
+- iscsi_tcp_data_recv_prep(tcp_conn);
+- return 0;
+- }
++ if (tcp_conn->in.datalen)
++ goto copy_hdr;
+ /* fall through */
+ case ISCSI_OP_LOGOUT_RSP:
+ case ISCSI_OP_NOOP_IN:
+@@ -854,161 +550,457 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
+ break;
+ }
+
+- if (rc == 0) {
+- /* Anything that comes with data should have
+- * been handled above. */
+- if (tcp_conn->in.datalen)
+- return ISCSI_ERR_PROTO;
+- iscsi_tcp_hdr_recv_prep(tcp_conn);
++ 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;
++}
+
+- return rc;
++/**
++ * 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_hdr_recv_done - process PDU header
++ * iscsi_tcp_copy - copy skb bits to the destanation buffer
++ * @conn: iscsi tcp connection
+ *
+- * This is the callback invoked when the PDU header has
+- * been received. If the header is followed by additional
+- * header segments, we go back for more data.
+- */
+-static int
+-iscsi_tcp_hdr_recv_done(struct iscsi_tcp_conn *tcp_conn,
+- struct iscsi_segment *segment)
++ * 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_conn *conn = tcp_conn->iscsi_conn;
+- struct iscsi_hdr *hdr;
++ 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);
++}
+
+- /* Check if there are additional header segments
+- * *prior* to computing the digest, because we
+- * may need to go back to the caller for more.
++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
+ */
+- hdr = (struct iscsi_hdr *) tcp_conn->in.hdr_buf;
+- if (segment->copied == sizeof(struct iscsi_hdr) && hdr->hlength) {
+- /* Bump the header length - the caller will
+- * just loop around and get the AHS for us, and
+- * call again. */
+- unsigned int ahslen = hdr->hlength << 2;
+-
+- /* Make sure we don't overflow */
+- if (sizeof(*hdr) + ahslen > sizeof(tcp_conn->in.hdr_buf))
+- return ISCSI_ERR_AHSLEN;
+-
+- segment->total_size += ahslen;
+- segment->size += ahslen;
+- return 0;
++ 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;
+ }
+
+- /* We're done processing the header. See if we're doing
+- * header digests; if so, set up the recv_digest buffer
+- * and go back for more. */
+- if (conn->hdrdgst_en) {
+- if (segment->digest_len == 0) {
+- iscsi_tcp_segment_splice_digest(segment,
+- segment->recv_digest);
+- return 0;
++ 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++;
+ }
+- iscsi_tcp_dgst_header(&tcp_conn->rx_hash, hdr,
+- segment->total_copied - ISCSI_DIGEST_SIZE,
+- segment->digest);
+
+- if (!iscsi_tcp_dgst_verify(tcp_conn, segment))
+- return ISCSI_ERR_HDR_DGST;
++ 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);
+ }
+
+- tcp_conn->in.hdr = hdr;
+- return iscsi_tcp_hdr_dissect(conn, hdr);
++ 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_recv - TCP receive in sendfile fashion
++ * 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_recv(read_descriptor_t *rd_desc, struct sk_buff *skb,
+- unsigned int offset, size_t len)
++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;
+- struct iscsi_segment *segment = &tcp_conn->in.segment;
+- struct skb_seq_state seq;
+- unsigned int consumed = 0;
+- int rc = 0;
++ int processed;
++ char pad[ISCSI_PAD_LEN];
++ struct scatterlist sg;
+
+- debug_tcp("in %d bytes\n", skb->len - offset);
++ /*
++ * 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;
+ }
+
+- skb_prepare_seq_read(skb, offset, skb->len, &seq);
+- while (1) {
+- unsigned int avail;
+- const u8 *ptr;
++ 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;
++ }
++ }
+
+- avail = skb_seq_read(consumed, &ptr, &seq);
+- if (avail == 0) {
+- debug_tcp("no more data avail. Consumed %d\n",
+- consumed);
+- break;
++ /*
++ * 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;
+ }
+- BUG_ON(segment->copied >= segment->size);
+-
+- debug_tcp("skb %p ptr=%p avail=%u\n", skb, ptr, avail);
+- rc = iscsi_tcp_segment_recv(tcp_conn, segment, ptr, avail);
+- BUG_ON(rc == 0);
+- consumed += rc;
+-
+- if (segment->total_copied >= segment->total_size) {
+- debug_tcp("segment done\n");
+- rc = segment->done(tcp_conn, segment);
+- if (rc != 0) {
+- skb_abort_seq_read(&seq);
+- goto error;
+- }
++ }
+
+- /* The done() functions sets up the
+- * next segment. */
++ 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;
+ }
+ }
+- skb_abort_seq_read(&seq);
+- conn->rxdata_octets += consumed;
+- return consumed;
+
+-error:
+- debug_tcp("Error receiving PDU, errno=%d\n", rc);
+- iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
+- return 0;
++ 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;
+- struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ read_descriptor_t rd_desc;
+
+ read_lock(&sk->sk_callback_lock);
+
+ /*
+- * Use rd_desc to pass 'conn' to iscsi_tcp_recv.
++ * 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_recv
++ * 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_recv);
++ tcp_read_sock(sk, &rd_desc, iscsi_tcp_data_recv);
+
+ read_unlock(&sk->sk_callback_lock);
+-
+- /* If we had to (atomically) map a highmem page,
+- * unmap it now. */
+- iscsi_tcp_segment_unmap(&tcp_conn->in.segment);
+ }
+
+ static void
+@@ -1088,173 +1080,121 @@ iscsi_conn_restore_callbacks(struct iscsi_tcp_conn *tcp_conn)
+ }
+
+ /**
+- * iscsi_xmit - TCP transmit
+- **/
+-static int
+-iscsi_xmit(struct iscsi_conn *conn)
++ * 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 iscsi_segment *segment = &tcp_conn->out.segment;
+- unsigned int consumed = 0;
+- int rc = 0;
+-
+- while (1) {
+- rc = iscsi_tcp_xmit_segment(tcp_conn, segment);
+- if (rc < 0)
+- goto error;
+- if (rc == 0)
+- break;
+-
+- consumed += rc;
++ struct socket *sk = tcp_conn->sock;
++ int offset = buf->sg.offset + buf->sent, res;
+
+- if (segment->total_copied >= segment->total_size) {
+- if (segment->done != NULL) {
+- rc = segment->done(tcp_conn, segment);
+- if (rc < 0)
+- goto error;
+- }
+- }
++ /*
++ * 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;
+ }
+
+- debug_tcp("xmit %d bytes\n", consumed);
+-
+- conn->txdata_octets += consumed;
+- return consumed;
+-
+-error:
+- /* Transmit error. We could initiate error recovery
+- * here. */
+- debug_tcp("Error sending PDU, errno=%d\n", rc);
+- iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
+- return rc;
++ tcp_conn->sendpage_failures_cnt++;
++ if (res == -EAGAIN)
++ res = -ENOBUFS;
++ else
++ iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
++ return res;
+ }
+
+ /**
+- * iscsi_tcp_xmit_qlen - return the number of bytes queued for xmit
+- */
+-static inline int
+-iscsi_tcp_xmit_qlen(struct iscsi_conn *conn)
+-{
+- struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+- struct iscsi_segment *segment = &tcp_conn->out.segment;
+-
+- return segment->total_copied - segment->total_size;
+-}
+-
++ * 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_tcp_flush(struct iscsi_conn *conn)
++iscsi_sendhdr(struct iscsi_conn *conn, struct iscsi_buf *buf, int datalen)
+ {
+- int rc;
+-
+- while (iscsi_tcp_xmit_qlen(conn)) {
+- rc = iscsi_xmit(conn);
+- if (rc == 0)
++ 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;
+- if (rc < 0)
+- return rc;
++ return 0;
+ }
+
+- return 0;
+-}
+-
+-/*
+- * This is called when we're done sending the header.
+- * Simply copy the data_segment to the send segment, and return.
+- */
+-static int
+-iscsi_tcp_send_hdr_done(struct iscsi_tcp_conn *tcp_conn,
+- struct iscsi_segment *segment)
+-{
+- tcp_conn->out.segment = tcp_conn->out.data_segment;
+- debug_tcp("Header done. Next segment size %u total_size %u\n",
+- tcp_conn->out.segment.size, tcp_conn->out.segment.total_size);
+- return 0;
++ return res;
+ }
+
+-static void
+-iscsi_tcp_send_hdr_prep(struct iscsi_conn *conn, void *hdr, size_t hdrlen)
++/**
++ * 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)
+ {
+- struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+-
+- debug_tcp("%s(%p%s)\n", __FUNCTION__, tcp_conn,
+- conn->hdrdgst_en? ", digest enabled" : "");
+-
+- /* Clear the data segment - needs to be filled in by the
+- * caller using iscsi_tcp_send_data_prep() */
+- memset(&tcp_conn->out.data_segment, 0, sizeof(struct iscsi_segment));
+-
+- /* If header digest is enabled, compute the CRC and
+- * place the digest into the same buffer. We make
+- * sure that both iscsi_tcp_ctask and mtask have
+- * sufficient room.
+- */
+- if (conn->hdrdgst_en) {
+- iscsi_tcp_dgst_header(&tcp_conn->tx_hash, hdr, hdrlen,
+- hdr + hdrlen);
+- hdrlen += ISCSI_DIGEST_SIZE;
++ 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;
+ }
+
+- /* Remember header pointer for later, when we need
+- * to decide whether there's a payload to go along
+- * with the header. */
+- tcp_conn->out.hdr = hdr;
+-
+- iscsi_segment_init_linear(&tcp_conn->out.segment, hdr, hdrlen,
+- iscsi_tcp_send_hdr_done, NULL);
+-}
+-
+-/*
+- * Prepare the send buffer for the payload data.
+- * Padding and checksumming will all be taken care
+- * of by the iscsi_segment routines.
+- */
+-static int
+-iscsi_tcp_send_data_prep(struct iscsi_conn *conn, struct scatterlist *sg,
+- unsigned int count, unsigned int offset,
+- unsigned int len)
+-{
+- struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+- struct hash_desc *tx_hash = NULL;
+- unsigned int hdr_spec_len;
+-
+- debug_tcp("%s(%p, offset=%d, datalen=%d%s)\n", __FUNCTION__,
+- tcp_conn, offset, len,
+- conn->datadgst_en? ", digest enabled" : "");
+-
+- /* Make sure the datalen matches what the caller
+- said he would send. */
+- hdr_spec_len = ntoh24(tcp_conn->out.hdr->dlength);
+- WARN_ON(iscsi_padded(len) != iscsi_padded(hdr_spec_len));
+-
+- if (conn->datadgst_en)
+- tx_hash = &tcp_conn->tx_hash;
+-
+- return iscsi_segment_seek_sg(&tcp_conn->out.data_segment,
+- sg, count, offset, len,
+- NULL, tx_hash);
++ return res;
+ }
+
+-static void
+-iscsi_tcp_send_linear_data_prepare(struct iscsi_conn *conn, void *data,
+- size_t len)
++static inline void
++iscsi_data_digest_init(struct iscsi_tcp_conn *tcp_conn,
++ struct iscsi_tcp_cmd_task *tcp_ctask)
+ {
+- struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+- struct hash_desc *tx_hash = NULL;
+- unsigned int hdr_spec_len;
+-
+- debug_tcp("%s(%p, datalen=%d%s)\n", __FUNCTION__, tcp_conn, len,
+- conn->datadgst_en? ", digest enabled" : "");
+-
+- /* Make sure the datalen matches what the caller
+- said he would send. */
+- hdr_spec_len = ntoh24(tcp_conn->out.hdr->dlength);
+- WARN_ON(iscsi_padded(len) != iscsi_padded(hdr_spec_len));
+-
+- if (conn->datadgst_en)
+- tx_hash = &tcp_conn->tx_hash;
+-
+- iscsi_segment_init_linear(&tcp_conn->out.data_segment,
+- data, len, NULL, tx_hash);
++ crypto_hash_init(&tcp_conn->tx_hash);
++ tcp_ctask->digest_count = 4;
+ }
+
+ /**
+@@ -1270,17 +1210,13 @@ iscsi_tcp_send_linear_data_prepare(struct iscsi_conn *conn, void *data,
+ *
+ * Called under connection lock.
+ **/
+-static int
++static void
+ iscsi_solicit_data_cont(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
+- struct iscsi_r2t_info *r2t)
++ struct iscsi_r2t_info *r2t, int left)
+ {
+ struct iscsi_data *hdr;
+- int new_offset, left;
+-
+- BUG_ON(r2t->data_length - r2t->sent < 0);
+- left = r2t->data_length - r2t->sent;
+- if (left == 0)
+- return 0;
++ struct scsi_cmnd *sc = ctask->sc;
++ int new_offset;
+
+ hdr = &r2t->dtask.hdr;
+ memset(hdr, 0, sizeof(struct iscsi_data));
+@@ -1301,47 +1237,81 @@ iscsi_solicit_data_cont(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
+ r2t->data_count = left;
+ hdr->flags = ISCSI_FLAG_CMD_FINAL;
+ }
+-
+ conn->dataout_pdus_cnt++;
+- return 1;
++
++ 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_ctask - Initialize iSCSI SCSI_READ or SCSI_WRITE commands
++ * iscsi_tcp_cmd_init - Initialize iSCSI SCSI_READ or SCSI_WRITE commands
+ * @conn: iscsi connection
+ * @ctask: scsi command task
+ * @sc: scsi command
+ **/
+-static int
+-iscsi_tcp_ctask_init(struct iscsi_cmd_task *ctask)
++static void
++iscsi_tcp_cmd_init(struct iscsi_cmd_task *ctask)
+ {
+- struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+- struct iscsi_conn *conn = ctask->conn;
+ struct scsi_cmnd *sc = ctask->sc;
+- int err;
++ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+
+ BUG_ON(__kfifo_len(tcp_ctask->r2tqueue));
++
+ tcp_ctask->sent = 0;
+- tcp_ctask->exp_datasn = 0;
++ tcp_ctask->sg_count = 0;
+
+- /* Prepare PDU, optionally w/ immediate data */
+- debug_scsi("ctask deq [cid %d itt 0x%x imm %d unsol %d]\n",
+- conn->id, ctask->itt, ctask->imm_count,
+- ctask->unsol_count);
+- iscsi_tcp_send_hdr_prep(conn, ctask->hdr, ctask->hdr_len);
++ 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 (!ctask->imm_count)
+- return 0;
++ if (sc->use_sg) {
++ struct scatterlist *sg = sc->request_buffer;
+
+- /* If we have immediate data, attach a payload */
+- err = iscsi_tcp_send_data_prep(conn, scsi_out(sc)->table.sgl,
+- scsi_out(sc)->table.nents,
+- 0, ctask->imm_count);
+- if (err)
+- return err;
+- tcp_ctask->sent += ctask->imm_count;
+- ctask->imm_count = 0;
+- return 0;
++ 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));
+ }
+
+ /**
+@@ -1353,130 +1323,428 @@ iscsi_tcp_ctask_init(struct iscsi_cmd_task *ctask)
+ * 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;
+
+- /* Flush any pending data first. */
+- rc = iscsi_tcp_flush(conn);
+- if (rc < 0)
+- return 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);
+- iscsi_free_mgmt_task(conn, mtask);
++ 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;
+ }
+
+-/*
+- * iscsi_tcp_ctask_xmit - xmit normal PDU task
+- * @conn: iscsi connection
+- * @ctask: iscsi command task
+- *
+- * We're expected to return 0 when everything was transmitted succesfully,
+- * -EAGAIN if there's still data in the queue, or != 0 for any other kind
+- * of error.
+- */
+ static int
+-iscsi_tcp_ctask_xmit(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
++iscsi_send_padding(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
+ {
+ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+- struct scsi_cmnd *sc = ctask->sc;
+- struct scsi_data_buffer *sdb = scsi_out(sc);
+- int rc = 0;
++ 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;
+
+-flush:
+- /* Flush any pending data first. */
+- rc = iscsi_tcp_flush(conn);
+- if (rc < 0)
+- return rc;
++ 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;
++}
+
+- /* Are we done already? */
+- if (sc->sc_data_direction != DMA_TO_DEVICE)
++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;
+
+- if (ctask->unsol_count != 0) {
+- struct iscsi_data *hdr = &tcp_ctask->unsol_dtask.hdr;
++ tcp_ctask = ctask->dd_data;
++ tcp_conn = conn->dd_data;
+
+- /* Prepare a header for the unsolicited PDU.
+- * The amount of data we want to send will be
+- * in ctask->data_count.
+- * FIXME: return the data count instead.
+- */
+- iscsi_prep_unsolicit_data_pdu(ctask, hdr);
++ 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;
++}
+
+- debug_tcp("unsol dout [itt 0x%x doff %d dlen %d]\n",
+- ctask->itt, tcp_ctask->sent, ctask->data_count);
++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;
++ }
+
+- iscsi_tcp_send_hdr_prep(conn, hdr, sizeof(*hdr));
+- rc = iscsi_tcp_send_data_prep(conn, sdb->table.sgl,
+- sdb->table.nents, tcp_ctask->sent,
+- ctask->data_count);
+ if (rc)
+- goto fail;
+- tcp_ctask->sent += ctask->data_count;
+- ctask->unsol_count -= ctask->data_count;
+- goto flush;
+- } else {
+- struct iscsi_session *session = conn->session;
+- struct iscsi_r2t_info *r2t;
++ 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;
++}
+
+- /* All unsolicited PDUs sent. Check for solicited PDUs.
++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.
+ */
+- spin_lock_bh(&session->lock);
+- r2t = tcp_ctask->r2t;
+- if (r2t != NULL) {
+- /* Continue with this R2T? */
+- if (!iscsi_solicit_data_cont(conn, ctask, r2t)) {
+- debug_scsi(" done with r2t %p\n", r2t);
+-
+- __kfifo_put(tcp_ctask->r2tpool.queue,
+- (void*)&r2t, sizeof(void*));
+- tcp_ctask->r2t = r2t = NULL;
+- }
++ if (ctask->unsol_count) {
++ debug_scsi("sending more uns\n");
++ tcp_ctask->xmstate |= XMSTATE_UNS_INIT;
++ goto send_hdr;
+ }
++ }
++ return 0;
++}
+
+- if (r2t == NULL) {
++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*));
+- r2t = tcp_ctask->r2t;
++ 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;
+ }
+- spin_unlock_bh(&session->lock);
+
+- /* Waiting for more R2Ts to arrive. */
+- if (r2t == NULL) {
+- debug_tcp("no R2Ts yet\n");
+- return 0;
++ if (conn->datadgst_en) {
++ iscsi_data_digest_init(conn->dd_data, tcp_ctask);
++ dtask->digest = 0;
+ }
+
+- debug_scsi("sol dout %p [dsn %d itt 0x%x doff %d dlen %d]\n",
+- r2t, r2t->solicit_datasn - 1, ctask->itt,
+- r2t->data_offset + r2t->sent, r2t->data_count);
++ 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);
++ }
+
+- iscsi_tcp_send_hdr_prep(conn, &r2t->dtask.hdr,
+- sizeof(struct iscsi_hdr));
++ if (tcp_ctask->xmstate & XMSTATE_SOL_DATA) {
++ r2t = tcp_ctask->r2t;
++ dtask = &r2t->dtask;
+
+- rc = iscsi_tcp_send_data_prep(conn, sdb->table.sgl,
+- sdb->table.nents,
+- r2t->data_offset + r2t->sent,
+- r2t->data_count);
++ rc = iscsi_send_data(ctask, &r2t->sendbuf, &r2t->sg,
++ &r2t->sent, &r2t->data_count,
++ &dtask->digestbuf, &dtask->digest);
+ if (rc)
+- goto fail;
+- tcp_ctask->sent += r2t->data_count;
+- r2t->sent += r2t->data_count;
+- goto flush;
++ 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;
+-fail:
+- iscsi_conn_failure(conn, rc);
+- return -EIO;
++}
++
++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 *
+@@ -1502,29 +1770,37 @@ iscsi_tcp_conn_create(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
+
+ 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))
++ 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))
++ 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:
+- iscsi_conn_printk(KERN_ERR, conn,
+- "Could not create connection due to crc32c "
+- "loading error. Make sure the crc32c "
+- "module is built as a module or into the "
+- "kernel\n");
+ kfree(tcp_conn);
+ tcp_conn_alloc_fail:
+ iscsi_conn_teardown(cls_conn);
+@@ -1534,22 +1810,18 @@ tcp_conn_alloc_fail:
+ static void
+ iscsi_tcp_release_conn(struct iscsi_conn *conn)
+ {
+- struct iscsi_session *session = conn->session;
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+- struct socket *sock = tcp_conn->sock;
+
+- if (!sock)
++ if (!tcp_conn->sock)
+ return;
+
+- sock_hold(sock->sk);
++ sock_hold(tcp_conn->sock->sk);
+ iscsi_conn_restore_callbacks(tcp_conn);
+- sock_put(sock->sk);
++ sock_put(tcp_conn->sock->sk);
+
+- spin_lock_bh(&session->lock);
++ sock_release(tcp_conn->sock);
+ tcp_conn->sock = NULL;
+ conn->recv_lock = NULL;
+- spin_unlock_bh(&session->lock);
+- sockfd_put(sock);
+ }
+
+ static void
+@@ -1573,49 +1845,11 @@ 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);
+-}
+-
+-static int iscsi_tcp_get_addr(struct iscsi_conn *conn, struct socket *sock,
+- char *buf, int *port,
+- int (*getname)(struct socket *, struct sockaddr *,
+- int *addrlen))
+-{
+- struct sockaddr_storage *addr;
+- struct sockaddr_in6 *sin6;
+- struct sockaddr_in *sin;
+- int rc = 0, len;
+-
+- addr = kmalloc(sizeof(*addr), GFP_KERNEL);
+- if (!addr)
+- return -ENOMEM;
+-
+- if (getname(sock, (struct sockaddr *) addr, &len)) {
+- rc = -ENODEV;
+- goto free_addr;
+- }
+-
+- switch (addr->ss_family) {
+- case AF_INET:
+- sin = (struct sockaddr_in *)addr;
+- spin_lock_bh(&conn->session->lock);
+- sprintf(buf, NIPQUAD_FMT, NIPQUAD(sin->sin_addr.s_addr));
+- *port = be16_to_cpu(sin->sin_port);
+- spin_unlock_bh(&conn->session->lock);
+- break;
+- case AF_INET6:
+- sin6 = (struct sockaddr_in6 *)addr;
+- spin_lock_bh(&conn->session->lock);
+- sprintf(buf, NIP6_FMT, NIP6(sin6->sin6_addr));
+- *port = be16_to_cpu(sin6->sin6_port);
+- spin_unlock_bh(&conn->session->lock);
+- break;
+- }
+-free_addr:
+- kfree(addr);
+- return rc;
++ tcp_conn->hdr_size = sizeof(struct iscsi_hdr);
+ }
+
+ static int
+@@ -1632,28 +1866,13 @@ iscsi_tcp_conn_bind(struct iscsi_cls_session *cls_session,
+ /* lookup for existing socket */
+ sock = sockfd_lookup((int)transport_eph, &err);
+ if (!sock) {
+- iscsi_conn_printk(KERN_ERR, conn,
+- "sockfd_lookup failed %d\n", err);
++ printk(KERN_ERR "iscsi_tcp: sockfd_lookup failed %d\n", err);
+ return -EEXIST;
+ }
+- /*
+- * copy these values now because if we drop the session
+- * userspace may still want to query the values since we will
+- * be using them for the reconnect
+- */
+- err = iscsi_tcp_get_addr(conn, sock, conn->portal_address,
+- &conn->portal_port, kernel_getpeername);
+- if (err)
+- goto free_socket;
+-
+- err = iscsi_tcp_get_addr(conn, sock, conn->local_address,
+- &conn->local_port, kernel_getsockname);
+- if (err)
+- goto free_socket;
+
+ err = iscsi_conn_bind(cls_session, cls_conn, is_leading);
+ if (err)
+- goto free_socket;
++ return err;
+
+ /* bind iSCSI connection and socket */
+ tcp_conn->sock = sock;
+@@ -1676,27 +1895,26 @@ iscsi_tcp_conn_bind(struct iscsi_cls_session *cls_session,
+ /*
+ * set receive state machine into initial state
+ */
+- iscsi_tcp_hdr_recv_prep(tcp_conn);
+- return 0;
++ tcp_conn->in_progress = IN_PROGRESS_WAIT_HEADER;
+
+-free_socket:
+- sockfd_put(sock);
+- return err;
++ return 0;
+ }
+
+ /* called with host lock */
+ static void
+-iscsi_tcp_mtask_init(struct iscsi_conn *conn, struct iscsi_mgmt_task *mtask)
++iscsi_tcp_mgmt_init(struct iscsi_conn *conn, struct iscsi_mgmt_task *mtask,
++ char *data, uint32_t data_size)
+ {
+- debug_scsi("mtask deq [cid %d itt 0x%x]\n", conn->id, mtask->itt);
++ struct iscsi_tcp_mgmt_task *tcp_mtask = mtask->dd_data;
+
+- /* Prepare PDU, optionally w/ immediate data */
+- iscsi_tcp_send_hdr_prep(conn, mtask->hdr, sizeof(*mtask->hdr));
++ 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 we have immediate data, attach a payload */
+ if (mtask->data_count)
+- iscsi_tcp_send_linear_data_prepare(conn, mtask->data,
+- mtask->data_count);
++ iscsi_buf_init_iov(&tcp_mtask->sendbuf, (char*)mtask->data,
++ mtask->data_count);
+ }
+
+ static int
+@@ -1719,7 +1937,8 @@ iscsi_r2tpool_alloc(struct iscsi_session *session)
+ */
+
+ /* R2T pool */
+- if (iscsi_pool_init(&tcp_ctask->r2tpool, session->max_r2t * 4, NULL,
++ if (iscsi_pool_init(&tcp_ctask->r2tpool, session->max_r2t * 4,
++ (void***)&tcp_ctask->r2ts,
+ sizeof(struct iscsi_r2t_info))) {
+ goto r2t_alloc_fail;
+ }
+@@ -1728,7 +1947,8 @@ iscsi_r2tpool_alloc(struct iscsi_session *session)
+ 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);
++ iscsi_pool_free(&tcp_ctask->r2tpool,
++ (void**)tcp_ctask->r2ts);
+ goto r2t_alloc_fail;
+ }
+ }
+@@ -1741,7 +1961,8 @@ r2t_alloc_fail:
+ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+
+ kfifo_free(tcp_ctask->r2tqueue);
+- iscsi_pool_free(&tcp_ctask->r2tpool);
++ iscsi_pool_free(&tcp_ctask->r2tpool,
++ (void**)tcp_ctask->r2ts);
+ }
+ return -ENOMEM;
+ }
+@@ -1756,7 +1977,8 @@ iscsi_r2tpool_free(struct iscsi_session *session)
+ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+
+ kfifo_free(tcp_ctask->r2tqueue);
+- iscsi_pool_free(&tcp_ctask->r2tpool);
++ iscsi_pool_free(&tcp_ctask->r2tpool,
++ (void**)tcp_ctask->r2ts);
+ }
+ }
+
+@@ -1772,6 +1994,9 @@ iscsi_conn_set_param(struct iscsi_cls_conn *cls_conn, enum iscsi_param param,
+ 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);
+@@ -1780,12 +2005,12 @@ iscsi_conn_set_param(struct iscsi_cls_conn *cls_conn, enum iscsi_param param,
+ break;
+ case ISCSI_PARAM_MAX_R2T:
+ sscanf(buf, "%d", &value);
+- if (value <= 0 || !is_power_of_2(value))
+- return -EINVAL;
+- if (session->max_r2t == 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;
+@@ -1801,18 +2026,41 @@ 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:
+- spin_lock_bh(&conn->session->lock);
+- len = sprintf(buf, "%hu\n", conn->portal_port);
+- spin_unlock_bh(&conn->session->lock);
++ 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:
+- spin_lock_bh(&conn->session->lock);
+- len = sprintf(buf, "%s\n", conn->portal_address);
+- spin_unlock_bh(&conn->session->lock);
++ 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);
+@@ -1821,29 +2069,6 @@ iscsi_tcp_conn_get_param(struct iscsi_cls_conn *cls_conn,
+ return len;
+ }
+
+-static int
+-iscsi_tcp_host_get_param(struct Scsi_Host *shost, enum iscsi_host_param param,
+- char *buf)
+-{
+- struct iscsi_session *session = iscsi_hostdata(shost->hostdata);
+- int len;
+-
+- switch (param) {
+- case ISCSI_HOST_PARAM_IPADDRESS:
+- spin_lock_bh(&session->lock);
+- if (!session->leadconn)
+- len = -ENODEV;
+- else
+- len = sprintf(buf, "%s\n",
+- session->leadconn->local_address);
+- spin_unlock_bh(&session->lock);
+- break;
+- default:
+- return iscsi_host_get_param(shost, param, buf);
+- }
+- return len;
+-}
+-
+ static void
+ iscsi_conn_get_stats(struct iscsi_cls_conn *cls_conn, struct iscsi_stats *stats)
+ {
+@@ -1871,7 +2096,6 @@ iscsi_conn_get_stats(struct iscsi_cls_conn *cls_conn, struct iscsi_stats *stats)
+ static struct iscsi_cls_session *
+ iscsi_tcp_session_create(struct iscsi_transport *iscsit,
+ struct scsi_transport_template *scsit,
+- uint16_t cmds_max, uint16_t qdepth,
+ uint32_t initial_cmdsn, uint32_t *hostno)
+ {
+ struct iscsi_cls_session *cls_session;
+@@ -1879,7 +2103,7 @@ iscsi_tcp_session_create(struct iscsi_transport *iscsit,
+ uint32_t hn;
+ int cmd_i;
+
+- cls_session = iscsi_session_setup(iscsit, scsit, cmds_max, qdepth,
++ cls_session = iscsi_session_setup(iscsit, scsit,
+ sizeof(struct iscsi_tcp_cmd_task),
+ sizeof(struct iscsi_tcp_mgmt_task),
+ initial_cmdsn, &hn);
+@@ -1892,15 +2116,14 @@ iscsi_tcp_session_create(struct iscsi_transport *iscsit,
+ struct iscsi_cmd_task *ctask = session->cmds[cmd_i];
+ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+
+- ctask->hdr = &tcp_ctask->hdr.cmd_hdr;
+- ctask->hdr_max = sizeof(tcp_ctask->hdr) - ISCSI_DIGEST_SIZE;
++ 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 = (struct iscsi_hdr *) &tcp_mtask->hdr;
++ mtask->hdr = &tcp_mtask->hdr;
+ }
+
+ if (iscsi_r2tpool_alloc(class_to_transport_session(cls_session)))
+@@ -1919,27 +2142,17 @@ static void iscsi_tcp_session_destroy(struct iscsi_cls_session *cls_session)
+ iscsi_session_teardown(cls_session);
+ }
+
+-static int iscsi_tcp_slave_configure(struct scsi_device *sdev)
+-{
+- blk_queue_bounce_limit(sdev->request_queue, BLK_BOUNCE_ANY);
+- blk_queue_dma_alignment(sdev->request_queue, 0);
+- return 0;
+-}
+-
+ static struct scsi_host_template iscsi_sht = {
+- .module = THIS_MODULE,
+ .name = "iSCSI Initiator over TCP/IP",
+ .queuecommand = iscsi_queuecommand,
+ .change_queue_depth = iscsi_change_queue_depth,
+- .can_queue = ISCSI_DEF_XMIT_CMDS_MAX - 1,
+- .sg_tablesize = 4096,
++ .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_device_reset_handler= iscsi_eh_device_reset,
+ .eh_host_reset_handler = iscsi_eh_host_reset,
+ .use_clustering = DISABLE_CLUSTERING,
+- .slave_configure = iscsi_tcp_slave_configure,
+ .proc_name = "iscsi_tcp",
+ .this_id = -1,
+ };
+@@ -1966,19 +2179,12 @@ static struct iscsi_transport iscsi_tcp_transport = {
+ ISCSI_EXP_STATSN |
+ ISCSI_PERSISTENT_PORT |
+ ISCSI_PERSISTENT_ADDRESS |
+- ISCSI_TARGET_NAME | ISCSI_TPGT |
+- ISCSI_USERNAME | ISCSI_PASSWORD |
+- ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN |
+- ISCSI_FAST_ABORT | ISCSI_ABORT_TMO |
+- ISCSI_LU_RESET_TMO |
+- ISCSI_PING_TMO | ISCSI_RECV_TMO,
+- .host_param_mask = ISCSI_HOST_HWADDRESS | ISCSI_HOST_IPADDRESS |
+- ISCSI_HOST_INITIATOR_NAME |
+- ISCSI_HOST_NETDEV_NAME,
++ ISCSI_TARGET_NAME |
++ ISCSI_TPGT,
+ .host_template = &iscsi_sht,
+ .conndata_size = sizeof(struct iscsi_conn),
+ .max_conn = 1,
+- .max_cmd_len = 16,
++ .max_cmd_len = ISCSI_TCP_MAX_CMD_LEN,
+ /* session management */
+ .create_session = iscsi_tcp_session_create,
+ .destroy_session = iscsi_tcp_session_destroy,
+@@ -1991,14 +2197,11 @@ static struct iscsi_transport iscsi_tcp_transport = {
+ .get_session_param = iscsi_session_get_param,
+ .start_conn = iscsi_conn_start,
+ .stop_conn = iscsi_tcp_conn_stop,
+- /* iscsi host params */
+- .get_host_param = iscsi_tcp_host_get_param,
+- .set_host_param = iscsi_host_set_param,
+ /* IO */
+ .send_pdu = iscsi_conn_send_pdu,
+ .get_stats = iscsi_conn_get_stats,
+- .init_cmd_task = iscsi_tcp_ctask_init,
+- .init_mgmt_task = iscsi_tcp_mtask_init,
++ .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,
+diff --git a/drivers/scsi/iscsi_tcp.h b/drivers/scsi/iscsi_tcp.h
+index ed0b991..3273683 100644
+--- a/drivers/scsi/iscsi_tcp.h
++++ b/drivers/scsi/iscsi_tcp.h
+@@ -24,61 +24,68 @@
+
+ #include <scsi/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;
+-struct iscsi_tcp_conn;
+-struct iscsi_segment;
+-
+-typedef int iscsi_segment_done_fn_t(struct iscsi_tcp_conn *,
+- struct iscsi_segment *);
+-
+-struct iscsi_segment {
+- unsigned char *data;
+- unsigned int size;
+- unsigned int copied;
+- unsigned int total_size;
+- unsigned int total_copied;
+-
+- struct hash_desc *hash;
+- unsigned char recv_digest[ISCSI_DIGEST_SIZE];
+- unsigned char digest[ISCSI_DIGEST_SIZE];
+- unsigned int digest_len;
+-
+- struct scatterlist *sg;
+- void *sg_mapped;
+- unsigned int sg_offset;
+-
+- iscsi_segment_done_fn_t *done;
+-};
+
+ /* Socket connection recieve helper */
+ struct iscsi_tcp_recv {
+ struct iscsi_hdr *hdr;
+- struct iscsi_segment segment;
+-
+- /* Allocate buffer for BHS + AHS */
+- uint32_t hdr_buf[64];
++ 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;
+-};
+-
+-/* Socket connection send helper */
+-struct iscsi_tcp_send {
+- struct iscsi_hdr *hdr;
+- struct iscsi_segment segment;
+- struct iscsi_segment data_segment;
++ 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 */
+- struct iscsi_tcp_send out; /* TCP send context */
++ int in_progress; /* connection state machine */
+
+ /* old values for socket callbacks */
+ void (*old_data_ready)(struct sock *, int);
+@@ -93,19 +100,29 @@ struct iscsi_tcp_conn {
+ uint32_t sendpage_failures_cnt;
+ uint32_t discontiguous_hdr_cnt;
+
+- int error;
+-
+ 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[ISCSI_DIGEST_SIZE];/* Header-Digest */
++ 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[ISCSI_DIGEST_SIZE]; /* Header-Digest */
++ 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 {
+@@ -113,26 +130,38 @@ struct iscsi_r2t_info {
+ __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; /* Data-Out header buf */
++ struct iscsi_data_task dtask; /* which data task */
+ };
+
+ struct iscsi_tcp_cmd_task {
+- struct iscsi_hdr_buff {
+- struct iscsi_cmd cmd_hdr;
+- char hdrextbuf[ISCSI_MAX_AHS_SIZE +
+- ISCSI_DIGEST_SIZE];
+- } hdr;
+-
++ 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;
+- uint32_t exp_datasn; /* expected target's R2TSN/DataSN */
++ 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_pool r2tpool;
++ struct iscsi_r2t_info *r2t; /* in progress R2T */
++ struct iscsi_queue r2tpool;
+ struct kfifo *r2tqueue;
+- struct iscsi_data_task unsol_dtask; /* Data-Out header buf */
++ 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 */
+diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
+index b43bf1d..3f5b9b4 100644
+--- a/drivers/scsi/libiscsi.c
++++ b/drivers/scsi/libiscsi.c
+@@ -22,9 +22,9 @@
+ * 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 <linux/log2.h>
+ #include <asm/unaligned.h>
+ #include <net/tcp.h>
+ #include <scsi/scsi_cmnd.h>
+@@ -46,53 +46,27 @@ class_to_transport_session(struct iscsi_cls_session *cls_session)
+ }
+ EXPORT_SYMBOL_GPL(class_to_transport_session);
+
+-/* Serial Number Arithmetic, 32 bits, less than, RFC1982 */
+-#define SNA32_CHECK 2147483648UL
++#define INVALID_SN_DELTA 0xffff
+
+-static int iscsi_sna_lt(u32 n1, u32 n2)
+-{
+- return n1 != n2 && ((n1 < n2 && (n2 - n1 < SNA32_CHECK)) ||
+- (n1 > n2 && (n2 - n1 < SNA32_CHECK)));
+-}
+-
+-/* Serial Number Arithmetic, 32 bits, less than, RFC1982 */
+-static int iscsi_sna_lte(u32 n1, u32 n2)
+-{
+- return n1 == n2 || ((n1 < n2 && (n2 - n1 < SNA32_CHECK)) ||
+- (n1 > n2 && (n2 - n1 < SNA32_CHECK)));
+-}
+-
+-void
+-iscsi_update_cmdsn(struct iscsi_session *session, struct iscsi_nopin *hdr)
++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);
+
+- /*
+- * standard specifies this check for when to update expected and
+- * max sequence numbers
+- */
+- if (iscsi_sna_lt(max_cmdsn, exp_cmdsn - 1))
+- return;
+-
+- if (exp_cmdsn != session->exp_cmdsn &&
+- !iscsi_sna_lt(exp_cmdsn, session->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;
+
+- if (max_cmdsn != session->max_cmdsn &&
+- !iscsi_sna_lt(max_cmdsn, session->max_cmdsn)) {
+- session->max_cmdsn = max_cmdsn;
+- /*
+- * if the window closed with IO queued, then kick the
+- * xmit thread
+- */
+- if (!list_empty(&session->leadconn->xmitqueue) ||
+- !list_empty(&session->leadconn->mgmtqueue))
+- scsi_queue_work(session->host,
+- &session->leadconn->xmitwork);
+- }
++ return 0;
+ }
+-EXPORT_SYMBOL_GPL(iscsi_update_cmdsn);
++EXPORT_SYMBOL_GPL(iscsi_check_assign_cmdsn);
+
+ void iscsi_prep_unsolicit_data_pdu(struct iscsi_cmd_task *ctask,
+ struct iscsi_data *hdr)
+@@ -123,84 +97,6 @@ void iscsi_prep_unsolicit_data_pdu(struct iscsi_cmd_task *ctask,
+ }
+ EXPORT_SYMBOL_GPL(iscsi_prep_unsolicit_data_pdu);
+
+-static int iscsi_add_hdr(struct iscsi_cmd_task *ctask, unsigned len)
+-{
+- unsigned exp_len = ctask->hdr_len + len;
+-
+- if (exp_len > ctask->hdr_max) {
+- WARN_ON(1);
+- return -EINVAL;
+- }
+-
+- WARN_ON(len & (ISCSI_PAD_LEN - 1)); /* caller must pad the AHS */
+- ctask->hdr_len = exp_len;
+- return 0;
+-}
+-
+-/*
+- * make an extended cdb AHS
+- */
+-static int iscsi_prep_ecdb_ahs(struct iscsi_cmd_task *ctask)
+-{
+- struct scsi_cmnd *cmd = ctask->sc;
+- unsigned rlen, pad_len;
+- unsigned short ahslength;
+- struct iscsi_ecdb_ahdr *ecdb_ahdr;
+- int rc;
+-
+- ecdb_ahdr = iscsi_next_hdr(ctask);
+- rlen = cmd->cmd_len - ISCSI_CDB_SIZE;
+-
+- BUG_ON(rlen > sizeof(ecdb_ahdr->ecdb));
+- ahslength = rlen + sizeof(ecdb_ahdr->reserved);
+-
+- pad_len = iscsi_padding(rlen);
+-
+- rc = iscsi_add_hdr(ctask, sizeof(ecdb_ahdr->ahslength) +
+- sizeof(ecdb_ahdr->ahstype) + ahslength + pad_len);
+- if (rc)
+- return rc;
+-
+- if (pad_len)
+- memset(&ecdb_ahdr->ecdb[rlen], 0, pad_len);
+-
+- ecdb_ahdr->ahslength = cpu_to_be16(ahslength);
+- ecdb_ahdr->ahstype = ISCSI_AHSTYPE_CDB;
+- ecdb_ahdr->reserved = 0;
+- memcpy(ecdb_ahdr->ecdb, cmd->cmnd + ISCSI_CDB_SIZE, rlen);
+-
+- debug_scsi("iscsi_prep_ecdb_ahs: varlen_cdb_len %d "
+- "rlen %d pad_len %d ahs_length %d iscsi_headers_size %u\n",
+- cmd->cmd_len, rlen, pad_len, ahslength, ctask->hdr_len);
+-
+- return 0;
+-}
+-
+-static int iscsi_prep_bidi_ahs(struct iscsi_cmd_task *ctask)
+-{
+- struct scsi_cmnd *sc = ctask->sc;
+- struct iscsi_rlength_ahdr *rlen_ahdr;
+- int rc;
+-
+- rlen_ahdr = iscsi_next_hdr(ctask);
+- rc = iscsi_add_hdr(ctask, sizeof(*rlen_ahdr));
+- if (rc)
+- return rc;
+-
+- rlen_ahdr->ahslength =
+- cpu_to_be16(sizeof(rlen_ahdr->read_length) +
+- sizeof(rlen_ahdr->reserved));
+- rlen_ahdr->ahstype = ISCSI_AHSTYPE_RLENGTH;
+- rlen_ahdr->reserved = 0;
+- rlen_ahdr->read_length = cpu_to_be32(scsi_in(sc)->length);
+-
+- debug_scsi("bidi-in rlen_ahdr->read_length(%d) "
+- "rlen_ahdr->ahslength(%d)\n",
+- be32_to_cpu(rlen_ahdr->read_length),
+- be16_to_cpu(rlen_ahdr->ahslength));
+- return 0;
+-}
+-
+ /**
+ * iscsi_prep_scsi_cmd_pdu - prep iscsi scsi cmd pdu
+ * @ctask: iscsi cmd task
+@@ -208,47 +104,26 @@ static int iscsi_prep_bidi_ahs(struct iscsi_cmd_task *ctask)
+ * 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 int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)
++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;
+- unsigned hdrlength, cmd_len;
+- int rc;
+-
+- ctask->hdr_len = 0;
+- rc = iscsi_add_hdr(ctask, sizeof(*hdr));
+- if (rc)
+- return rc;
+- 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, session->age);
+- hdr->cmdsn = cpu_to_be32(session->cmdsn);
+- session->cmdsn++;
+- hdr->exp_statsn = cpu_to_be32(conn->exp_statsn);
+- cmd_len = sc->cmd_len;
+- if (cmd_len < ISCSI_CDB_SIZE)
+- memset(&hdr->cdb[cmd_len], 0, ISCSI_CDB_SIZE - cmd_len);
+- else if (cmd_len > ISCSI_CDB_SIZE) {
+- rc = iscsi_prep_ecdb_ahs(ctask);
+- if (rc)
+- return rc;
+- cmd_len = ISCSI_CDB_SIZE;
+- }
+- memcpy(hdr->cdb, sc->cmnd, cmd_len);
+
+- ctask->imm_count = 0;
+- if (scsi_bidi_cmnd(sc)) {
+- hdr->flags |= ISCSI_FLAG_CMD_READ;
+- rc = iscsi_prep_bidi_ahs(ctask);
+- if (rc)
+- return rc;
+- }
++ 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) {
+- unsigned out_len = scsi_out(sc)->length;
+- hdr->data_length = cpu_to_be32(out_len);
+ hdr->flags |= ISCSI_FLAG_CMD_WRITE;
+ /*
+ * Write counters:
+@@ -264,61 +139,43 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)
+ *
+ * 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 (out_len >= session->first_burst)
++ if (ctask->total_length >= session->first_burst)
+ ctask->imm_count = min(session->first_burst,
+ conn->max_xmit_dlength);
+ else
+- ctask->imm_count = min(out_len,
++ ctask->imm_count = min(ctask->total_length,
+ conn->max_xmit_dlength);
+- hton24(hdr->dlength, ctask->imm_count);
++ hton24(ctask->hdr->dlength, ctask->imm_count);
+ } else
+- zero_data(hdr->dlength);
++ zero_data(ctask->hdr->dlength);
+
+ if (!session->initial_r2t_en) {
+- ctask->unsol_count = min(session->first_burst, out_len)
+- - ctask->imm_count;
++ 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 */
+- hdr->flags |= ISCSI_FLAG_CMD_FINAL;
++ ctask->hdr->flags |= ISCSI_FLAG_CMD_FINAL;
+ } else {
++ ctask->datasn = 0;
+ hdr->flags |= ISCSI_FLAG_CMD_FINAL;
+ zero_data(hdr->dlength);
+- hdr->data_length = cpu_to_be32(scsi_in(sc)->length);
+
+ if (sc->sc_data_direction == DMA_FROM_DEVICE)
+ hdr->flags |= ISCSI_FLAG_CMD_READ;
+ }
+
+- /* calculate size of additional header segments (AHSs) */
+- hdrlength = ctask->hdr_len - sizeof(*hdr);
+-
+- WARN_ON(hdrlength & (ISCSI_PAD_LEN-1));
+- hdrlength /= ISCSI_PAD_LEN;
+-
+- WARN_ON(hdrlength >= 256);
+- hdr->hlength = hdrlength & 0xFF;
+-
+- if (conn->session->tt->init_cmd_task(conn->ctask))
+- return EIO;
+-
+ conn->scsicmd_pdus_cnt++;
+- debug_scsi("iscsi prep [%s cid %d sc %p cdb 0x%x itt 0x%x "
+- "len %d bidi_len %d cmdsn %d win %d]\n",
+- scsi_bidi_cmnd(sc) ? "bidirectional" :
+- sc->sc_data_direction == DMA_TO_DEVICE ? "write" : "read",
+- conn->id, sc, sc->cmnd[0], ctask->itt,
+- scsi_bufflen(sc), scsi_bidi_cmnd(sc) ? scsi_in(sc)->length : 0,
+- session->cmdsn, session->max_cmdsn - session->exp_cmdsn + 1);
+- return 0;
+ }
++EXPORT_SYMBOL_GPL(iscsi_prep_scsi_cmd_pdu);
+
+ /**
+ * iscsi_complete_command - return command back to scsi-ml
+@@ -330,16 +187,13 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)
+ */
+ static void iscsi_complete_command(struct iscsi_cmd_task *ctask)
+ {
+- struct iscsi_conn *conn = ctask->conn;
+- struct iscsi_session *session = conn->session;
++ 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;
+- if (conn->ctask == ctask)
+- conn->ctask = NULL;
+ list_del_init(&ctask->running);
+ __kfifo_put(session->cmdpool.queue, (void*)&ctask, sizeof(void*));
+ sc->scsi_done(sc);
+@@ -350,124 +204,27 @@ 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);
+ }
+
+-/*
+- * session lock must be held
+- */
+-static void fail_command(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
+- int err)
++static void iscsi_put_ctask(struct iscsi_cmd_task *ctask)
+ {
+- struct scsi_cmnd *sc;
+-
+- sc = ctask->sc;
+- if (!sc)
+- return;
+-
+- if (ctask->state == ISCSI_TASK_PENDING)
+- /*
+- * cmd never made it to the xmit thread, so we should not count
+- * the cmd in the sequencing
+- */
+- conn->session->queued_cmdsn--;
+- else
+- conn->session->tt->cleanup_cmd_task(conn, ctask);
+-
+- sc->result = err;
+- if (!scsi_bidi_cmnd(sc))
+- scsi_set_resid(sc, scsi_bufflen(sc));
+- else {
+- scsi_out(sc)->resid = scsi_out(sc)->length;
+- scsi_in(sc)->resid = scsi_in(sc)->length;
+- }
+- if (conn->ctask == ctask)
+- conn->ctask = NULL;
+- /* release ref from queuecommand */
++ spin_lock_bh(&ctask->conn->session->lock);
+ __iscsi_put_ctask(ctask);
++ spin_unlock_bh(&ctask->conn->session->lock);
+ }
+
+ /**
+- * iscsi_free_mgmt_task - return mgmt task back to pool
+- * @conn: iscsi connection
+- * @mtask: mtask
+- *
+- * Must be called with session lock.
+- */
+-void iscsi_free_mgmt_task(struct iscsi_conn *conn,
+- struct iscsi_mgmt_task *mtask)
+-{
+- list_del_init(&mtask->running);
+- if (conn->login_mtask == mtask)
+- return;
+-
+- if (conn->ping_mtask == mtask)
+- conn->ping_mtask = NULL;
+- __kfifo_put(conn->session->mgmtpool.queue,
+- (void*)&mtask, sizeof(void*));
+-}
+-EXPORT_SYMBOL_GPL(iscsi_free_mgmt_task);
+-
+-static struct iscsi_mgmt_task *
+-__iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+- char *data, uint32_t data_size)
+-{
+- struct iscsi_session *session = conn->session;
+- struct iscsi_mgmt_task *mtask;
+-
+- if (session->state == ISCSI_STATE_TERMINATE)
+- return NULL;
+-
+- 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);
+-
+- if (!__kfifo_get(session->mgmtpool.queue,
+- (void*)&mtask, sizeof(void*)))
+- return NULL;
+- }
+-
+- if (data_size) {
+- memcpy(mtask->data, data, data_size);
+- mtask->data_count = data_size;
+- } else
+- mtask->data_count = 0;
+-
+- memcpy(mtask->hdr, hdr, sizeof(struct iscsi_hdr));
+- INIT_LIST_HEAD(&mtask->running);
+- list_add_tail(&mtask->running, &conn->mgmtqueue);
+- return mtask;
+-}
+-
+-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;
+- struct iscsi_session *session = conn->session;
+- int err = 0;
+-
+- spin_lock_bh(&session->lock);
+- if (!__iscsi_conn_send_pdu(conn, hdr, data, data_size))
+- err = -EPERM;
+- spin_unlock_bh(&session->lock);
+- scsi_queue_work(session->host, &conn->xmitwork);
+- return err;
+-}
+-EXPORT_SYMBOL_GPL(iscsi_conn_send_pdu);
+-
+-/**
+ * iscsi_cmd_rsp - SCSI Command Response processing
+ * @conn: iscsi connection
+ * @hdr: iscsi header
+@@ -478,15 +235,21 @@ EXPORT_SYMBOL_GPL(iscsi_conn_send_pdu);
+ * iscsi_cmd_rsp sets up the scsi_cmnd fields based on the PDU and
+ * then completes the command and task.
+ **/
+-static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+- struct iscsi_cmd_task *ctask, char *data,
+- int datalen)
++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;
+
+- iscsi_update_cmdsn(session, (struct iscsi_nopin*)rhdr);
++ 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;
+@@ -501,9 +264,8 @@ static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+
+ if (datalen < 2) {
+ invalid_datalen:
+- iscsi_conn_printk(KERN_ERR, conn,
+- "Got CHECK_CONDITION but invalid data "
+- "buffer size of %d\n", 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;
+ }
+@@ -518,36 +280,28 @@ invalid_datalen:
+ min_t(uint16_t, senselen, SCSI_SENSE_BUFFERSIZE));
+ }
+
+- if (rhdr->flags & (ISCSI_FLAG_CMD_BIDI_UNDERFLOW |
+- ISCSI_FLAG_CMD_BIDI_OVERFLOW)) {
+- int res_count = be32_to_cpu(rhdr->bi_residual_count);
+-
+- if (scsi_bidi_cmnd(sc) && res_count > 0 &&
+- (rhdr->flags & ISCSI_FLAG_CMD_BIDI_OVERFLOW ||
+- res_count <= scsi_in(sc)->length))
+- scsi_in(sc)->resid = res_count;
+- else
+- sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status;
+- }
++ if (sc->sc_data_direction == DMA_TO_DEVICE)
++ goto out;
+
+- if (rhdr->flags & (ISCSI_FLAG_CMD_UNDERFLOW |
+- ISCSI_FLAG_CMD_OVERFLOW)) {
++ if (rhdr->flags & ISCSI_FLAG_CMD_UNDERFLOW) {
+ int res_count = be32_to_cpu(rhdr->residual_count);
+
+- if (res_count > 0 &&
+- (rhdr->flags & ISCSI_FLAG_CMD_OVERFLOW ||
+- res_count <= scsi_bufflen(sc)))
+- /* write side for bidi or uni-io set_resid */
+- scsi_set_resid(sc, res_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)
+@@ -557,51 +311,18 @@ static void iscsi_tmf_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
+ conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1;
+ conn->tmfrsp_pdus_cnt++;
+
+- if (conn->tmf_state != TMF_QUEUED)
++ if (conn->tmabort_state != TMABORT_INITIAL)
+ return;
+
+ if (tmf->response == ISCSI_TMF_RSP_COMPLETE)
+- conn->tmf_state = TMF_SUCCESS;
++ conn->tmabort_state = TMABORT_SUCCESS;
+ else if (tmf->response == ISCSI_TMF_RSP_NO_TASK)
+- conn->tmf_state = TMF_NOT_FOUND;
++ conn->tmabort_state = TMABORT_NOT_FOUND;
+ else
+- conn->tmf_state = TMF_FAILED;
++ conn->tmabort_state = TMABORT_FAILED;
+ wake_up(&conn->ehwait);
+ }
+
+-static void iscsi_send_nopout(struct iscsi_conn *conn, struct iscsi_nopin *rhdr)
+-{
+- struct iscsi_nopout hdr;
+- struct iscsi_mgmt_task *mtask;
+-
+- if (!rhdr && conn->ping_mtask)
+- return;
+-
+- memset(&hdr, 0, sizeof(struct iscsi_nopout));
+- hdr.opcode = ISCSI_OP_NOOP_OUT | ISCSI_OP_IMMEDIATE;
+- hdr.flags = ISCSI_FLAG_CMD_FINAL;
+-
+- if (rhdr) {
+- memcpy(hdr.lun, rhdr->lun, 8);
+- hdr.ttt = rhdr->ttt;
+- hdr.itt = RESERVED_ITT;
+- } else
+- hdr.ttt = RESERVED_ITT;
+-
+- mtask = __iscsi_conn_send_pdu(conn, (struct iscsi_hdr *)&hdr, NULL, 0);
+- if (!mtask) {
+- iscsi_conn_printk(KERN_ERR, conn, "Could not send nopout\n");
+- return;
+- }
+-
+- /* only track our nops */
+- if (!rhdr) {
+- conn->ping_mtask = mtask;
+- conn->last_ping = jiffies;
+- }
+- scsi_queue_work(conn->session->host, &conn->xmitwork);
+-}
+-
+ static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ char *data, int datalen)
+ {
+@@ -618,10 +339,9 @@ static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ if (ntoh24(reject->dlength) >= sizeof(struct iscsi_hdr)) {
+ memcpy(&rejected_pdu, data, sizeof(struct iscsi_hdr));
+ itt = get_itt(rejected_pdu.itt);
+- iscsi_conn_printk(KERN_ERR, conn,
+- "itt 0x%x had pdu (op 0x%x) rejected "
+- "due to DataDigest error.\n", itt,
+- rejected_pdu.opcode);
++ printk(KERN_ERR "itt 0x%x had pdu (op 0x%x) rejected "
++ "due to DataDigest error.\n", itt,
++ rejected_pdu.opcode);
+ }
+ }
+ return 0;
+@@ -638,8 +358,8 @@ static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ * queuecommand or send generic. session lock must be held and verify
+ * itt must have been called.
+ */
+-static int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+- char *data, int datalen)
++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;
+@@ -647,7 +367,6 @@ static int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ struct iscsi_mgmt_task *mtask;
+ uint32_t itt;
+
+- conn->last_recv = jiffies;
+ if (hdr->itt != RESERVED_ITT)
+ itt = get_itt(hdr->itt);
+ else
+@@ -662,8 +381,8 @@ static int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ switch(opcode) {
+ case ISCSI_OP_SCSI_CMD_RSP:
+ BUG_ON((void*)ctask != ctask->sc->SCp.ptr);
+- iscsi_scsi_cmd_rsp(conn, hdr, ctask, data,
+- datalen);
++ 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);
+@@ -686,7 +405,11 @@ static int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ debug_scsi("immrsp [op 0x%x cid %d itt 0x%x len %d]\n",
+ opcode, conn->id, mtask->itt, datalen);
+
+- iscsi_update_cmdsn(session, (struct iscsi_nopin*)hdr);
++ rc = iscsi_check_assign_cmdsn(session,
++ (struct iscsi_nopin*)hdr);
++ if (rc)
++ goto done;
++
+ switch(opcode) {
+ case ISCSI_OP_LOGOUT_RSP:
+ if (datalen) {
+@@ -703,7 +426,10 @@ static int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ */
+ if (iscsi_recv_pdu(conn->cls_conn, hdr, data, datalen))
+ rc = ISCSI_ERR_CONN_FAILED;
+- iscsi_free_mgmt_task(conn, mtask);
++ 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) {
+@@ -712,35 +438,30 @@ static int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ }
+
+ iscsi_tmf_rsp(conn, hdr);
+- iscsi_free_mgmt_task(conn, mtask);
+ break;
+ case ISCSI_OP_NOOP_IN:
+- if (hdr->ttt != cpu_to_be32(ISCSI_RESERVED_TAG) ||
+- datalen) {
++ 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 (conn->ping_mtask != mtask) {
+- /*
+- * If this is not in response to one of our
+- * nops then it must be from userspace.
+- */
+- if (iscsi_recv_pdu(conn->cls_conn, hdr, data,
+- datalen))
+- rc = ISCSI_ERR_CONN_FAILED;
+- } else
+- mod_timer(&conn->transport_timer,
+- jiffies + conn->recv_timeout);
+- iscsi_free_mgmt_task(conn, mtask);
++ 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) {
+- iscsi_update_cmdsn(session, (struct iscsi_nopin*)hdr);
++ rc = iscsi_check_assign_cmdsn(session,
++ (struct iscsi_nopin*)hdr);
++ if (rc)
++ goto done;
+
+ switch(opcode) {
+ case ISCSI_OP_NOOP_IN:
+@@ -752,7 +473,8 @@ static int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ if (hdr->ttt == cpu_to_be32(ISCSI_RESERVED_TAG))
+ break;
+
+- iscsi_send_nopout(conn, (struct iscsi_nopin*)hdr);
++ 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);
+@@ -769,8 +491,10 @@ static int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ } 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)
+@@ -795,13 +519,18 @@ int iscsi_verify_itt(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ if (hdr->itt != RESERVED_ITT) {
+ if (((__force u32)hdr->itt & ISCSI_AGE_MASK) !=
+ (session->age << ISCSI_AGE_SHIFT)) {
+- iscsi_conn_printk(KERN_ERR, conn,
+- "received itt %x expected session "
+- "age (%x)\n", (__force u32)hdr->itt,
+- session->age & ISCSI_AGE_MASK);
++ 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;
+@@ -810,17 +539,16 @@ int iscsi_verify_itt(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ ctask = session->cmds[itt];
+
+ if (!ctask->sc) {
+- iscsi_conn_printk(KERN_INFO, conn, "dropping ctask "
+- "with itt 0x%x\n", ctask->itt);
++ 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) {
+- iscsi_conn_printk(KERN_ERR, conn,
+- "iscsi: ctask's session age %d, "
+- "expected %d\n", 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;
+ }
+ }
+@@ -850,110 +578,30 @@ void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err)
+ }
+ EXPORT_SYMBOL_GPL(iscsi_conn_failure);
+
+-static void iscsi_prep_mtask(struct iscsi_conn *conn,
+- struct iscsi_mgmt_task *mtask)
+-{
+- struct iscsi_session *session = conn->session;
+- struct iscsi_hdr *hdr = mtask->hdr;
+- struct iscsi_nopout *nop = (struct iscsi_nopout *)hdr;
+-
+- if (hdr->opcode != (ISCSI_OP_LOGIN | ISCSI_OP_IMMEDIATE) &&
+- hdr->opcode != (ISCSI_OP_TEXT | ISCSI_OP_IMMEDIATE))
+- nop->exp_statsn = cpu_to_be32(conn->exp_statsn);
+- /*
+- * pre-format CmdSN for outgoing PDU.
+- */
+- nop->cmdsn = cpu_to_be32(session->cmdsn);
+- if (hdr->itt != RESERVED_ITT) {
+- hdr->itt = build_itt(mtask->itt, session->age);
+- /*
+- * TODO: We always use immediate, so we never hit this.
+- * If we start to send tmfs or nops as non-immediate then
+- * we should start checking the cmdsn numbers for mgmt tasks.
+- */
+- if (conn->c_stage == ISCSI_CONN_STARTED &&
+- !(hdr->opcode & ISCSI_OP_IMMEDIATE)) {
+- session->queued_cmdsn++;
+- session->cmdsn++;
+- }
+- }
+-
+- if (session->tt->init_mgmt_task)
+- session->tt->init_mgmt_task(conn, mtask);
+-
+- debug_scsi("mgmtpdu [op 0x%x hdr->itt 0x%x datalen %d]\n",
+- hdr->opcode & ISCSI_OPCODE_MASK, hdr->itt,
+- mtask->data_count);
+-}
+-
+ static int iscsi_xmit_mtask(struct iscsi_conn *conn)
+ {
+ struct iscsi_hdr *hdr = conn->mtask->hdr;
+- int rc;
+-
+- if ((hdr->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_LOGOUT)
+- conn->session->state = ISCSI_STATE_LOGGING_OUT;
+- spin_unlock_bh(&conn->session->lock);
++ 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);
+- spin_lock_bh(&conn->session->lock);
+ if (rc)
+ return rc;
+
+ /* done with this in-progress mtask */
+ conn->mtask = NULL;
+- return 0;
+-}
+-
+-static int iscsi_check_cmdsn_window_closed(struct iscsi_conn *conn)
+-{
+- struct iscsi_session *session = conn->session;
+
+- /*
+- * Check for iSCSI window and take care of CmdSN wrap-around
+- */
+- if (!iscsi_sna_lte(session->queued_cmdsn, session->max_cmdsn)) {
+- debug_scsi("iSCSI CmdSN closed. ExpCmdSn %u MaxCmdSN %u "
+- "CmdSN %u/%u\n", session->exp_cmdsn,
+- session->max_cmdsn, session->cmdsn,
+- session->queued_cmdsn);
+- return -ENOSPC;
++ if (was_logout) {
++ set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
++ return -ENODATA;
+ }
+ return 0;
+ }
+
+-static int iscsi_xmit_ctask(struct iscsi_conn *conn)
+-{
+- struct iscsi_cmd_task *ctask = conn->ctask;
+- int rc;
+-
+- __iscsi_get_ctask(ctask);
+- spin_unlock_bh(&conn->session->lock);
+- rc = conn->session->tt->xmit_cmd_task(conn, ctask);
+- spin_lock_bh(&conn->session->lock);
+- __iscsi_put_ctask(ctask);
+- if (!rc)
+- /* done with this ctask */
+- conn->ctask = NULL;
+- return rc;
+-}
+-
+-/**
+- * iscsi_requeue_ctask - requeue ctask to run from session workqueue
+- * @ctask: ctask to requeue
+- *
+- * LLDs that need to run a ctask from the session workqueue should call
+- * this. The session lock must be held.
+- */
+-void iscsi_requeue_ctask(struct iscsi_cmd_task *ctask)
+-{
+- struct iscsi_conn *conn = ctask->conn;
+-
+- list_move_tail(&ctask->running, &conn->requeue);
+- scsi_queue_work(conn->session->host, &conn->xmitwork);
+-}
+-EXPORT_SYMBOL_GPL(iscsi_requeue_ctask);
+-
+ /**
+ * iscsi_data_xmit - xmit any command into the scheduled connection
+ * @conn: iscsi connection
+@@ -965,106 +613,106 @@ EXPORT_SYMBOL_GPL(iscsi_requeue_ctask);
+ **/
+ static int iscsi_data_xmit(struct iscsi_conn *conn)
+ {
++ struct iscsi_transport *tt;
+ int rc = 0;
+
+- spin_lock_bh(&conn->session->lock);
+ if (unlikely(conn->suspend_tx)) {
+ debug_scsi("conn %d Tx suspended!\n", conn->id);
+- spin_unlock_bh(&conn->session->lock);
+ 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) {
+- rc = iscsi_xmit_ctask(conn);
++ 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 mgmt pdus like nops before commands since we should
+- * only have one nop-out as a ping from us and targets should not
+- * overflow us with nop-ins
+- */
+-check_mgmt:
+- while (!list_empty(&conn->mgmtqueue)) {
+- conn->mtask = list_entry(conn->mgmtqueue.next,
+- struct iscsi_mgmt_task, running);
+- if (conn->session->state == ISCSI_STATE_LOGGING_OUT) {
+- iscsi_free_mgmt_task(conn, conn->mtask);
+- conn->mtask = NULL;
+- continue;
+- }
+-
+- iscsi_prep_mtask(conn, conn->mtask);
+- list_move_tail(conn->mgmtqueue.next, &conn->mgmt_run_list);
+- 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 pending command queue */
++ /* process command queue */
++ spin_lock_bh(&conn->session->lock);
+ while (!list_empty(&conn->xmitqueue)) {
+- if (conn->tmf_state == TMF_QUEUED)
+- break;
+-
++ /*
++ * 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);
+- if (conn->session->state == ISCSI_STATE_LOGGING_OUT) {
+- fail_command(conn, conn->ctask, DID_IMM_RETRY << 16);
+- continue;
+- }
+- if (iscsi_prep_scsi_cmd_pdu(conn->ctask)) {
+- fail_command(conn, conn->ctask, DID_ABORT << 16);
+- continue;
+- }
+-
+ conn->ctask->state = ISCSI_TASK_RUNNING;
+ list_move_tail(conn->xmitqueue.next, &conn->run_list);
+- rc = iscsi_xmit_ctask(conn);
+- if (rc)
+- goto again;
+- /*
+- * we could continuously get new ctask requests so
+- * we need to check the mgmt queue for nops that need to
+- * be sent to aviod starvation
+- */
+- if (!list_empty(&conn->mgmtqueue))
+- goto check_mgmt;
+- }
++ __iscsi_get_ctask(conn->ctask);
++ spin_unlock_bh(&conn->session->lock);
+
+- while (!list_empty(&conn->requeue)) {
+- if (conn->session->fast_abort && conn->tmf_state != TMF_INITIAL)
+- break;
++ rc = tt->xmit_cmd_task(conn, conn->ctask);
+
+- /*
+- * we always do fastlogout - conn stop code will clean up.
+- */
+- if (conn->session->state == ISCSI_STATE_LOGGING_OUT)
+- break;
+-
+- conn->ctask = list_entry(conn->requeue.next,
+- struct iscsi_cmd_task, running);
+- conn->ctask->state = ISCSI_TASK_RUNNING;
+- list_move_tail(conn->requeue.next, &conn->run_list);
+- rc = iscsi_xmit_ctask(conn);
+- if (rc)
++ spin_lock_bh(&conn->session->lock);
++ __iscsi_put_ctask(conn->ctask);
++ if (rc) {
++ spin_unlock_bh(&conn->session->lock);
+ goto again;
+- if (!list_empty(&conn->mgmtqueue))
+- goto check_mgmt;
++ }
+ }
+ 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))
+- rc = -ENODATA;
+- spin_unlock_bh(&conn->session->lock);
++ return -ENODATA;
++
+ return rc;
+ }
+
+@@ -1076,9 +724,11 @@ static void iscsi_xmitworker(struct work_struct *work)
+ /*
+ * 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 {
+@@ -1090,8 +740,6 @@ enum {
+ FAILURE_SESSION_TERMINATE,
+ FAILURE_SESSION_IN_RECOVERY,
+ FAILURE_SESSION_RECOVERY_TIMEOUT,
+- FAILURE_SESSION_LOGGING_OUT,
+- FAILURE_SESSION_NOT_READY,
+ };
+
+ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
+@@ -1107,16 +755,9 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
+ sc->SCp.ptr = NULL;
+
+ host = sc->device->host;
+- spin_unlock(host->host_lock);
+-
+ session = iscsi_hostdata(host->hostdata);
+- spin_lock(&session->lock);
+
+- reason = iscsi_session_chkready(session_to_cls(session));
+- if (reason) {
+- sc->result = reason;
+- goto fault;
+- }
++ spin_lock(&session->lock);
+
+ /*
+ * ISCSI_STATE_FAILED is a temp. state. The recovery
+@@ -1131,82 +772,77 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
+ * be entering our queuecommand while a block is starting
+ * up because the block code is not locked)
+ */
+- switch (session->state) {
+- case ISCSI_STATE_IN_RECOVERY:
++ if (session->state == ISCSI_STATE_IN_RECOVERY) {
+ reason = FAILURE_SESSION_IN_RECOVERY;
+- sc->result = DID_IMM_RETRY << 16;
+- break;
+- case ISCSI_STATE_LOGGING_OUT:
+- reason = FAILURE_SESSION_LOGGING_OUT;
+- sc->result = DID_IMM_RETRY << 16;
+- break;
+- case ISCSI_STATE_RECOVERY_FAILED:
++ goto reject;
++ }
++
++ if (session->state == ISCSI_STATE_RECOVERY_FAILED)
+ reason = FAILURE_SESSION_RECOVERY_TIMEOUT;
+- sc->result = DID_NO_CONNECT << 16;
+- break;
+- case ISCSI_STATE_TERMINATE:
++ else if (session->state == ISCSI_STATE_TERMINATE)
+ reason = FAILURE_SESSION_TERMINATE;
+- sc->result = DID_NO_CONNECT << 16;
+- break;
+- default:
++ else
+ reason = FAILURE_SESSION_FREED;
+- sc->result = DID_NO_CONNECT << 16;
+- }
+ 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;
+- sc->result = DID_NO_CONNECT << 16;
+ goto fault;
+ }
+
+- if (iscsi_check_cmdsn_window_closed(conn)) {
+- reason = FAILURE_WINDOW_CLOSED;
+- goto reject;
+- }
+-
+ if (!__kfifo_get(session->cmdpool.queue, (void*)&ctask,
+ sizeof(void*))) {
+ reason = FAILURE_OOM;
+ goto reject;
+ }
+- session->queued_cmdsn++;
+-
+ 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);
+- spin_lock(host->host_lock);
+ return 0;
+
+ reject:
+ spin_unlock(&session->lock);
+ debug_scsi("cmd 0x%x rejected (%d)\n", sc->cmnd[0], reason);
+- spin_lock(host->host_lock);
+ return SCSI_MLQUEUE_HOST_BUSY;
+
+ fault:
+ spin_unlock(&session->lock);
+- debug_scsi("iscsi: cmd 0x%x is not queued (%d)\n", sc->cmnd[0], reason);
+- if (!scsi_bidi_cmnd(sc))
+- scsi_set_resid(sc, scsi_bufflen(sc));
+- else {
+- scsi_out(sc)->resid = scsi_out(sc)->length;
+- scsi_in(sc)->resid = scsi_in(sc)->length;
+- }
++ 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);
+- spin_lock(host->host_lock);
+ return 0;
+ }
+ EXPORT_SYMBOL_GPL(iscsi_queuecommand);
+@@ -1220,15 +856,106 @@ int iscsi_change_queue_depth(struct scsi_device *sdev, int 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 (session->leadconn)
+- wake_up(&session->leadconn->ehwait);
++ if (conn)
++ wake_up(&conn->ehwait);
+ }
+ spin_unlock_bh(&session->lock);
+ }
+@@ -1239,25 +966,30 @@ 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;
+
+- mutex_lock(&session->eh_mutex);
+ 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);
+- mutex_unlock(&session->eh_mutex);
+ 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);
+- mutex_unlock(&session->eh_mutex);
++
+ /*
+ * we drop the lock here but the leadconn cannot be destoyed while
+ * we are in the scsi eh
+ */
+- iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
++ 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,
+@@ -1267,496 +999,324 @@ failed:
+ if (signal_pending(current))
+ flush_signals(current);
+
+- mutex_lock(&session->eh_mutex);
+ spin_lock_bh(&session->lock);
+ if (session->state == ISCSI_STATE_LOGGED_IN)
+- iscsi_session_printk(KERN_INFO, session,
+- "host reset succeeded\n");
++ printk(KERN_INFO "iscsi: host reset succeeded\n");
+ else
+ goto failed;
+ spin_unlock_bh(&session->lock);
+- mutex_unlock(&session->eh_mutex);
++
+ return SUCCESS;
+ }
+ EXPORT_SYMBOL_GPL(iscsi_eh_host_reset);
+
+-static void iscsi_tmf_timedout(unsigned long data)
++static void iscsi_tmabort_timedout(unsigned long data)
+ {
+- struct iscsi_conn *conn = (struct iscsi_conn *)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->tmf_state == TMF_QUEUED) {
+- conn->tmf_state = TMF_TIMEDOUT;
+- debug_scsi("tmf timedout\n");
++ 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);
+ }
+
+-static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn,
+- struct iscsi_tm *hdr, int age,
+- int timeout)
++/* 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_mgmt_task *mtask;
++ struct iscsi_tm *hdr = &conn->tmhdr;
++ int rc;
+
+- mtask = __iscsi_conn_send_pdu(conn, (struct iscsi_hdr *)hdr,
+- NULL, 0);
+- if (!mtask) {
+- spin_unlock_bh(&session->lock);
++ /*
++ * 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);
+- spin_lock_bh(&session->lock);
+- debug_scsi("tmf exec failure\n");
+- return -EPERM;
++ debug_scsi("abort sent failure [itt 0x%x] %d\n", ctask->itt,
++ rc);
++ return rc;
+ }
+- conn->tmfcmd_pdus_cnt++;
+- conn->tmf_timer.expires = timeout * HZ + jiffies;
+- conn->tmf_timer.function = iscsi_tmf_timedout;
+- conn->tmf_timer.data = (unsigned long)conn;
+- add_timer(&conn->tmf_timer);
+- debug_scsi("tmf set timeout\n");
+
++ 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(&session->eh_mutex);
+- scsi_queue_work(session->host, &conn->xmitwork);
++ mutex_unlock(&conn->xmitmutex);
+
+ /*
+ * block eh thread until:
+ *
+- * 1) tmf response
+- * 2) tmf timeout
++ * 1) abort response
++ * 2) abort timeout
+ * 3) session is terminated or restarted or userspace has
+ * given up on recovery
+ */
+- wait_event_interruptible(conn->ehwait, age != session->age ||
++ wait_event_interruptible(conn->ehwait,
++ sc->SCp.phase != session->age ||
+ session->state != ISCSI_STATE_LOGGED_IN ||
+- conn->tmf_state != TMF_QUEUED);
++ conn->tmabort_state != TMABORT_INITIAL);
+ if (signal_pending(current))
+ flush_signals(current);
+- del_timer_sync(&conn->tmf_timer);
++ del_timer_sync(&conn->tmabort_timer);
+
+- mutex_lock(&session->eh_mutex);
+- spin_lock_bh(&session->lock);
+- /* if the session drops it will clean up the mtask */
+- if (age != session->age ||
+- session->state != ISCSI_STATE_LOGGED_IN)
+- return -ENOTCONN;
++ mutex_lock(&conn->xmitmutex);
+ return 0;
+ }
+
+ /*
+- * Fail commands. session lock held and recv side suspended and xmit
+- * thread flushed
++ * xmit mutex and session lock must be held
+ */
+-static void fail_all_commands(struct iscsi_conn *conn, unsigned lun,
+- int error)
++static struct iscsi_mgmt_task *
++iscsi_remove_mgmt_task(struct kfifo *fifo, uint32_t itt)
+ {
+- struct iscsi_cmd_task *ctask, *tmp;
++ int i, nr_tasks = __kfifo_len(fifo) / sizeof(void*);
++ struct iscsi_mgmt_task *task;
+
+- if (conn->ctask && (conn->ctask->sc->device->lun == lun || lun == -1))
+- conn->ctask = NULL;
++ debug_scsi("searching %d tasks\n", nr_tasks);
+
+- /* flush pending */
+- list_for_each_entry_safe(ctask, tmp, &conn->xmitqueue, running) {
+- if (lun == ctask->sc->device->lun || lun == -1) {
+- debug_scsi("failing pending sc %p itt 0x%x\n",
+- ctask->sc, ctask->itt);
+- fail_command(conn, ctask, error << 16);
+- }
+- }
++ for (i = 0; i < nr_tasks; i++) {
++ __kfifo_get(fifo, (void*)&task, sizeof(void*));
++ debug_scsi("check task %u\n", task->itt);
+
+- list_for_each_entry_safe(ctask, tmp, &conn->requeue, running) {
+- if (lun == ctask->sc->device->lun || lun == -1) {
+- debug_scsi("failing requeued sc %p itt 0x%x\n",
+- ctask->sc, ctask->itt);
+- fail_command(conn, ctask, error << 16);
++ if (task->itt == itt) {
++ debug_scsi("matched task\n");
++ return task;
+ }
+- }
+
+- /* fail all other running */
+- list_for_each_entry_safe(ctask, tmp, &conn->run_list, running) {
+- if (lun == ctask->sc->device->lun || lun == -1) {
+- debug_scsi("failing in progress sc %p itt 0x%x\n",
+- ctask->sc, ctask->itt);
+- fail_command(conn, ctask, DID_BUS_BUSY << 16);
+- }
++ __kfifo_put(fifo, (void*)&task, sizeof(void*));
+ }
++ return NULL;
+ }
+
+-static void iscsi_suspend_tx(struct iscsi_conn *conn)
+-{
+- set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
+- scsi_flush_work(conn->session->host);
+-}
+-
+-static void iscsi_start_tx(struct iscsi_conn *conn)
+-{
+- clear_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
+- scsi_queue_work(conn->session->host, &conn->xmitwork);
+-}
+-
+-static enum scsi_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *scmd)
++static int iscsi_ctask_mtask_cleanup(struct iscsi_cmd_task *ctask)
+ {
+- struct iscsi_cls_session *cls_session;
+- struct iscsi_session *session;
+- struct iscsi_conn *conn;
+- enum scsi_eh_timer_return rc = EH_NOT_HANDLED;
+-
+- cls_session = starget_to_session(scsi_target(scmd->device));
+- session = class_to_transport_session(cls_session);
+-
+- debug_scsi("scsi cmd %p timedout\n", scmd);
+-
+- spin_lock(&session->lock);
+- if (session->state != ISCSI_STATE_LOGGED_IN) {
+- /*
+- * We are probably in the middle of iscsi recovery so let
+- * that complete and handle the error.
+- */
+- rc = EH_RESET_TIMER;
+- goto done;
+- }
++ struct iscsi_conn *conn = ctask->conn;
++ struct iscsi_session *session = conn->session;
+
+- conn = session->leadconn;
+- if (!conn) {
+- /* In the middle of shuting down */
+- rc = EH_RESET_TIMER;
+- goto done;
+- }
++ if (!ctask->mtask)
++ return -EINVAL;
+
+- if (!conn->recv_timeout && !conn->ping_timeout)
+- goto done;
+- /*
+- * if the ping timedout then we are in the middle of cleaning up
+- * and can let the iscsi eh handle it
+- */
+- if (time_before_eq(conn->last_recv + (conn->recv_timeout * HZ) +
+- (conn->ping_timeout * HZ), jiffies))
+- rc = EH_RESET_TIMER;
+- /*
+- * if we are about to check the transport then give the command
+- * more time
+- */
+- if (time_before_eq(conn->last_recv + (conn->recv_timeout * HZ),
+- jiffies))
+- rc = EH_RESET_TIMER;
+- /* if in the middle of checking the transport then give us more time */
+- if (conn->ping_mtask)
+- rc = EH_RESET_TIMER;
+-done:
+- spin_unlock(&session->lock);
+- debug_scsi("return %s\n", rc == EH_RESET_TIMER ? "timer reset" : "nh");
+- return rc;
++ 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;
+ }
+
+-static void iscsi_check_transport_timeouts(unsigned long data)
++/*
++ * session lock and xmitmutex must be held
++ */
++static void fail_command(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
++ int err)
+ {
+- struct iscsi_conn *conn = (struct iscsi_conn *)data;
+- struct iscsi_session *session = conn->session;
+- unsigned long recv_timeout, next_timeout = 0, last_recv;
++ struct scsi_cmnd *sc;
+
+- spin_lock(&session->lock);
+- if (session->state != ISCSI_STATE_LOGGED_IN)
+- goto done;
+-
+- recv_timeout = conn->recv_timeout;
+- if (!recv_timeout)
+- goto done;
+-
+- recv_timeout *= HZ;
+- last_recv = conn->last_recv;
+- if (conn->ping_mtask &&
+- time_before_eq(conn->last_ping + (conn->ping_timeout * HZ),
+- jiffies)) {
+- iscsi_conn_printk(KERN_ERR, conn, "ping timeout of %d secs "
+- "expired, last rx %lu, last ping %lu, "
+- "now %lu\n", conn->ping_timeout, last_recv,
+- conn->last_ping, jiffies);
+- spin_unlock(&session->lock);
+- iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
++ sc = ctask->sc;
++ if (!sc)
+ return;
+- }
+-
+- if (time_before_eq(last_recv + recv_timeout, jiffies)) {
+- /* send a ping to try to provoke some traffic */
+- debug_scsi("Sending nopout as ping on conn %p\n", conn);
+- iscsi_send_nopout(conn, NULL);
+- next_timeout = conn->last_ping + (conn->ping_timeout * HZ);
+- } else
+- next_timeout = last_recv + recv_timeout;
+
+- debug_scsi("Setting next tmo %lu\n", next_timeout);
+- mod_timer(&conn->transport_timer, next_timeout);
+-done:
+- spin_unlock(&session->lock);
+-}
++ conn->session->tt->cleanup_cmd_task(conn, ctask);
++ iscsi_ctask_mtask_cleanup(ctask);
+
+-static void iscsi_prep_abort_task_pdu(struct iscsi_cmd_task *ctask,
+- struct iscsi_tm *hdr)
+-{
+- memset(hdr, 0, sizeof(*hdr));
+- hdr->opcode = ISCSI_OP_SCSI_TMFUNC | ISCSI_OP_IMMEDIATE;
+- hdr->flags = ISCSI_TM_FUNC_ABORT_TASK & ISCSI_FLAG_TM_FUNC_MASK;
+- 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;
++ 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 Scsi_Host *host = sc->device->host;
+- struct iscsi_session *session = iscsi_hostdata(host->hostdata);
+- struct iscsi_conn *conn;
+ struct iscsi_cmd_task *ctask;
+- struct iscsi_tm *hdr;
+- int rc, age;
++ struct iscsi_conn *conn;
++ struct iscsi_session *session;
++ int rc;
+
+- mutex_lock(&session->eh_mutex);
+- spin_lock_bh(&session->lock);
+ /*
+ * 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");
+- spin_unlock_bh(&session->lock);
+- mutex_unlock(&session->eh_mutex);
+ 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->leadconn || session->state != ISCSI_STATE_LOGGED_IN ||
+- sc->SCp.phase != session->age) {
+- spin_unlock_bh(&session->lock);
+- mutex_unlock(&session->eh_mutex);
+- return FAILED;
+- }
+-
+- conn = session->leadconn;
+- conn->eh_abort_cnt++;
+- age = session->age;
+-
+- ctask = (struct iscsi_cmd_task *)sc->SCp.ptr;
+- debug_scsi("aborting [sc %p itt 0x%x]\n", sc, ctask->itt);
++ 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;
++ goto success_rel_mutex;
+ }
+
+- if (ctask->state == ISCSI_TASK_PENDING) {
+- fail_command(conn, ctask, DID_ABORT << 16);
+- goto success;
++ /* 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;
+ }
+
+- /* only have one tmf outstanding at a time */
+- if (conn->tmf_state != TMF_INITIAL)
+- goto failed;
+- conn->tmf_state = TMF_QUEUED;
++ if (ctask->state == ISCSI_TASK_PENDING)
++ goto success_cleanup;
+
+- hdr = &conn->tmhdr;
+- iscsi_prep_abort_task_pdu(ctask, hdr);
++ conn->tmabort_state = TMABORT_INITIAL;
+
+- if (iscsi_exec_task_mgmt_fn(conn, hdr, age, session->abort_timeout)) {
+- rc = FAILED;
++ 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->tmf_state) {
+- case TMF_SUCCESS:
+- spin_unlock_bh(&session->lock);
+- iscsi_suspend_tx(conn);
+- /*
+- * clean up task if aborted. grab the recv lock as a writer
+- */
+- write_lock_bh(conn->recv_lock);
+- spin_lock(&session->lock);
+- fail_command(conn, ctask, DID_ABORT << 16);
+- conn->tmf_state = TMF_INITIAL;
+- spin_unlock(&session->lock);
+- write_unlock_bh(conn->recv_lock);
+- iscsi_start_tx(conn);
+- goto success_unlocked;
+- case TMF_TIMEDOUT:
+- spin_unlock_bh(&session->lock);
+- iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
+- goto failed_unlocked;
+- case TMF_NOT_FOUND:
+- if (!sc->SCp.ptr) {
+- conn->tmf_state = TMF_INITIAL;
++ 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;
++ goto success_rel_mutex;
+ }
+ /* fall through */
+ default:
+- conn->tmf_state = TMF_INITIAL;
++ /* timedout or failed */
++ spin_unlock_bh(&session->lock);
++ iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
++ spin_lock_bh(&session->lock);
+ goto failed;
+ }
+
+-success:
+- spin_unlock_bh(&session->lock);
+-success_unlocked:
++success_cleanup:
+ debug_scsi("abort success [sc %lx itt 0x%x]\n", (long)sc, ctask->itt);
+- mutex_unlock(&session->eh_mutex);
+- return SUCCESS;
+-
+-failed:
+ spin_unlock_bh(&session->lock);
+-failed_unlocked:
+- debug_scsi("abort failed [sc %p itt 0x%x]\n", sc,
+- ctask ? ctask->itt : 0);
+- mutex_unlock(&session->eh_mutex);
+- return FAILED;
+-}
+-EXPORT_SYMBOL_GPL(iscsi_eh_abort);
+
+-static void iscsi_prep_lun_reset_pdu(struct scsi_cmnd *sc, struct iscsi_tm *hdr)
+-{
+- memset(hdr, 0, sizeof(*hdr));
+- hdr->opcode = ISCSI_OP_SCSI_TMFUNC | ISCSI_OP_IMMEDIATE;
+- hdr->flags = ISCSI_TM_FUNC_LOGICAL_UNIT_RESET & ISCSI_FLAG_TM_FUNC_MASK;
+- hdr->flags |= ISCSI_FLAG_CMD_FINAL;
+- int_to_scsilun(sc->device->lun, (struct scsi_lun *)hdr->lun);
+- hdr->rtt = RESERVED_ITT;
+-}
+-
+-int iscsi_eh_device_reset(struct scsi_cmnd *sc)
+-{
+- struct Scsi_Host *host = sc->device->host;
+- struct iscsi_session *session = iscsi_hostdata(host->hostdata);
+- struct iscsi_conn *conn;
+- struct iscsi_tm *hdr;
+- int rc = FAILED;
+-
+- debug_scsi("LU Reset [sc %p lun %u]\n", sc, sc->device->lun);
+-
+- mutex_lock(&session->eh_mutex);
+- spin_lock_bh(&session->lock);
+ /*
+- * Just check if we are not logged in. We cannot check for
+- * the phase because the reset could come from a ioctl.
++ * clean up task if aborted. we have the xmitmutex so grab
++ * the recv lock as a writer
+ */
+- if (!session->leadconn || session->state != ISCSI_STATE_LOGGED_IN)
+- goto unlock;
+- conn = session->leadconn;
+-
+- /* only have one tmf outstanding at a time */
+- if (conn->tmf_state != TMF_INITIAL)
+- goto unlock;
+- conn->tmf_state = TMF_QUEUED;
+-
+- hdr = &conn->tmhdr;
+- iscsi_prep_lun_reset_pdu(sc, hdr);
+-
+- if (iscsi_exec_task_mgmt_fn(conn, hdr, session->age,
+- session->lu_reset_timeout)) {
+- rc = FAILED;
+- goto unlock;
+- }
+-
+- switch (conn->tmf_state) {
+- case TMF_SUCCESS:
+- break;
+- case TMF_TIMEDOUT:
+- spin_unlock_bh(&session->lock);
+- iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
+- goto done;
+- default:
+- conn->tmf_state = TMF_INITIAL;
+- goto unlock;
+- }
+-
+- rc = SUCCESS;
+- spin_unlock_bh(&session->lock);
+-
+- iscsi_suspend_tx(conn);
+- /* need to grab the recv lock then session lock */
+ write_lock_bh(conn->recv_lock);
+ spin_lock(&session->lock);
+- fail_all_commands(conn, sc->device->lun, DID_ERROR);
+- conn->tmf_state = TMF_INITIAL;
++ fail_command(conn, ctask, DID_ABORT << 16);
+ spin_unlock(&session->lock);
+ write_unlock_bh(conn->recv_lock);
+
+- iscsi_start_tx(conn);
+- goto done;
++success_rel_mutex:
++ mutex_unlock(&conn->xmitmutex);
++ return SUCCESS;
+
+-unlock:
++failed:
+ spin_unlock_bh(&session->lock);
+-done:
+- debug_scsi("iscsi_eh_device_reset %s\n",
+- rc == SUCCESS ? "SUCCESS" : "FAILED");
+- mutex_unlock(&session->eh_mutex);
+- return rc;
++ 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_device_reset);
++EXPORT_SYMBOL_GPL(iscsi_eh_abort);
+
+-/*
+- * Pre-allocate a pool of @max items of @item_size. By default, the pool
+- * should be accessed via kfifo_{get,put} on q->queue.
+- * Optionally, the caller can obtain the array of object pointers
+- * by passing in a non-NULL @items pointer
+- */
+ int
+-iscsi_pool_init(struct iscsi_pool *q, int max, void ***items, int item_size)
++iscsi_pool_init(struct iscsi_queue *q, int max, void ***items, int item_size)
+ {
+- int i, num_arrays = 1;
++ int i;
+
+- memset(q, 0, sizeof(*q));
++ *items = kmalloc(max * sizeof(void*), GFP_KERNEL);
++ if (*items == NULL)
++ return -ENOMEM;
+
+ q->max = max;
+-
+- /* If the user passed an items pointer, he wants a copy of
+- * the array. */
+- if (items)
+- num_arrays++;
+- q->pool = kzalloc(num_arrays * max * sizeof(void*), GFP_KERNEL);
+- if (q->pool == NULL)
+- goto enomem;
++ 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))
+- goto enomem;
++ if (q->queue == ERR_PTR(-ENOMEM)) {
++ kfree(q->pool);
++ kfree(*items);
++ return -ENOMEM;
++ }
+
+ for (i = 0; i < max; i++) {
+- q->pool[i] = kzalloc(item_size, GFP_KERNEL);
++ q->pool[i] = kmalloc(item_size, GFP_KERNEL);
+ if (q->pool[i] == NULL) {
+- q->max = i;
+- goto enomem;
++ 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*));
+ }
+-
+- if (items) {
+- *items = q->pool + max;
+- memcpy(*items, q->pool, max * sizeof(void *));
+- }
+-
+ return 0;
+-
+-enomem:
+- iscsi_pool_free(q);
+- return -ENOMEM;
+ }
+ EXPORT_SYMBOL_GPL(iscsi_pool_init);
+
+-void iscsi_pool_free(struct iscsi_pool *q)
++void iscsi_pool_free(struct iscsi_queue *q, void **items)
+ {
+ int i;
+
+ for (i = 0; i < q->max; i++)
+- kfree(q->pool[i]);
+- if (q->pool)
+- kfree(q->pool);
++ kfree(items[i]);
++ kfree(q->pool);
++ kfree(items);
+ }
+ EXPORT_SYMBOL_GPL(iscsi_pool_free);
+
+@@ -1779,10 +1339,6 @@ EXPORT_SYMBOL_GPL(iscsi_pool_free);
+ * iscsi_session_setup - create iscsi cls session and host and session
+ * @scsit: scsi transport template
+ * @iscsit: iscsi transport template
+- * @cmds_max: scsi host can queue
+- * @qdepth: scsi host cmds per lun
+- * @cmd_task_size: LLD ctask private data size
+- * @mgmt_task_size: LLD mtask private data size
+ * @initial_cmdsn: initial CmdSN
+ * @hostno: host no allocated
+ *
+@@ -1792,7 +1348,6 @@ EXPORT_SYMBOL_GPL(iscsi_pool_free);
+ struct iscsi_cls_session *
+ iscsi_session_setup(struct iscsi_transport *iscsit,
+ struct scsi_transport_template *scsit,
+- uint16_t cmds_max, uint16_t qdepth,
+ int cmd_task_size, int mgmt_task_size,
+ uint32_t initial_cmdsn, uint32_t *hostno)
+ {
+@@ -1801,56 +1356,30 @@ iscsi_session_setup(struct iscsi_transport *iscsit,
+ struct iscsi_cls_session *cls_session;
+ int cmd_i;
+
+- if (qdepth > ISCSI_MAX_CMD_PER_LUN || qdepth < 1) {
+- if (qdepth != 0)
+- printk(KERN_ERR "iscsi: invalid queue depth of %d. "
+- "Queue depth must be between 1 and %d.\n",
+- qdepth, ISCSI_MAX_CMD_PER_LUN);
+- qdepth = ISCSI_DEF_CMD_PER_LUN;
+- }
+-
+- if (!is_power_of_2(cmds_max) || cmds_max >= ISCSI_MGMT_ITT_OFFSET ||
+- cmds_max < 2) {
+- if (cmds_max != 0)
+- printk(KERN_ERR "iscsi: invalid can_queue of %d. "
+- "can_queue must be a power of 2 and between "
+- "2 and %d - setting to %d.\n", cmds_max,
+- ISCSI_MGMT_ITT_OFFSET, ISCSI_DEF_XMIT_CMDS_MAX);
+- cmds_max = ISCSI_DEF_XMIT_CMDS_MAX;
+- }
+-
+ shost = scsi_host_alloc(iscsit->host_template,
+ hostdata_privsize(sizeof(*session)));
+ if (!shost)
+ return NULL;
+
+- /* the iscsi layer takes one task for reserve */
+- shost->can_queue = cmds_max - 1;
+- shost->cmd_per_lun = qdepth;
+ 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;
+- shost->transportt->eh_timed_out = iscsi_eh_cmd_timed_out;
+ *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->fast_abort = 1;
+- session->lu_reset_timeout = 15;
+- session->abort_timeout = 10;
+ session->mgmtpool_max = ISCSI_MGMT_CMDS_MAX;
+- session->cmds_max = cmds_max;
+- session->queued_cmdsn = session->cmdsn = initial_cmdsn;
++ 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;
+- mutex_init(&session->eh_mutex);
+
+ /* initialize SCSI PDU commands pool */
+ if (iscsi_pool_init(&session->cmdpool, session->cmds_max,
+@@ -1905,9 +1434,9 @@ module_put:
+ cls_session_fail:
+ scsi_remove_host(shost);
+ add_host_fail:
+- iscsi_pool_free(&session->mgmtpool);
++ iscsi_pool_free(&session->mgmtpool, (void**)session->mgmt_cmds);
+ mgmtpool_alloc_fail:
+- iscsi_pool_free(&session->cmdpool);
++ iscsi_pool_free(&session->cmdpool, (void**)session->cmds);
+ cmdpool_alloc_fail:
+ scsi_host_put(shost);
+ return NULL;
+@@ -1927,22 +1456,14 @@ void iscsi_session_teardown(struct iscsi_cls_session *cls_session)
+ struct iscsi_session *session = iscsi_hostdata(shost->hostdata);
+ struct module *owner = cls_session->transport->owner;
+
+- iscsi_remove_session(cls_session);
+ scsi_remove_host(shost);
+
+- iscsi_pool_free(&session->mgmtpool);
+- iscsi_pool_free(&session->cmdpool);
++ iscsi_pool_free(&session->mgmtpool, (void**)session->mgmt_cmds);
++ iscsi_pool_free(&session->cmdpool, (void**)session->cmds);
+
+- kfree(session->password);
+- kfree(session->password_in);
+- kfree(session->username);
+- kfree(session->username_in);
+ kfree(session->targetname);
+- kfree(session->netdev);
+- kfree(session->hwaddress);
+- kfree(session->initiatorname);
+
+- iscsi_free_session(cls_session);
++ iscsi_destroy_session(cls_session);
+ scsi_host_put(shost);
+ module_put(owner);
+ }
+@@ -1972,17 +1493,22 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
+ conn->c_stage = ISCSI_CONN_INITIAL_STAGE;
+ conn->id = conn_idx;
+ conn->exp_statsn = 0;
+- conn->tmf_state = TMF_INITIAL;
+-
+- init_timer(&conn->transport_timer);
+- conn->transport_timer.data = (unsigned long)conn;
+- conn->transport_timer.function = iscsi_check_transport_timeouts;
+-
++ conn->tmabort_state = TMABORT_INITIAL;
+ INIT_LIST_HEAD(&conn->run_list);
+ INIT_LIST_HEAD(&conn->mgmt_run_list);
+- INIT_LIST_HEAD(&conn->mgmtqueue);
+ INIT_LIST_HEAD(&conn->xmitqueue);
+- INIT_LIST_HEAD(&conn->requeue);
++
++ /* 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 */
+@@ -2000,7 +1526,8 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
+ goto login_mtask_data_alloc_fail;
+ conn->login_mtask->data = conn->data = data;
+
+- init_timer(&conn->tmf_timer);
++ init_timer(&conn->tmabort_timer);
++ mutex_init(&conn->xmitmutex);
+ init_waitqueue_head(&conn->ehwait);
+
+ return cls_conn;
+@@ -2009,6 +1536,10 @@ 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;
+ }
+@@ -2027,7 +1558,8 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn)
+ struct iscsi_session *session = conn->session;
+ unsigned long flags;
+
+- del_timer_sync(&conn->transport_timer);
++ set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
++ mutex_lock(&conn->xmitmutex);
+
+ spin_lock_bh(&session->lock);
+ conn->c_stage = ISCSI_CONN_CLEANUP_WAIT;
+@@ -2040,6 +1572,8 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn)
+ }
+ spin_unlock_bh(&session->lock);
+
++ mutex_unlock(&conn->xmitmutex);
++
+ /*
+ * Block until all in-progress commands for this connection
+ * time out or fail.
+@@ -2052,10 +1586,9 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn)
+ }
+ spin_unlock_irqrestore(session->host->host_lock, flags);
+ msleep_interruptible(500);
+- iscsi_conn_printk(KERN_INFO, conn, "iscsi conn_destroy(): "
+- "host_busy %d host_failed %d\n",
+- session->host->host_busy,
+- session->host->host_failed);
++ 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
+ */
+@@ -2063,17 +1596,23 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn)
+ }
+
+ /* flush queued up work because we free the connection below */
+- iscsi_suspend_tx(conn);
++ 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)
++ 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);
+@@ -2084,41 +1623,21 @@ int iscsi_conn_start(struct iscsi_cls_conn *cls_conn)
+ struct iscsi_session *session = conn->session;
+
+ if (!session) {
+- iscsi_conn_printk(KERN_ERR, conn,
+- "can't start unbound connection\n");
++ 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) {
+- iscsi_conn_printk(KERN_INFO, conn, "invalid burst lengths: "
+- "first_burst %d max_burst %d\n",
+- 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;
+ }
+
+- if (conn->ping_timeout && !conn->recv_timeout) {
+- iscsi_conn_printk(KERN_ERR, conn, "invalid recv timeout of "
+- "zero. Using 5 seconds\n.");
+- conn->recv_timeout = 5;
+- }
+-
+- if (conn->recv_timeout && !conn->ping_timeout) {
+- iscsi_conn_printk(KERN_ERR, conn, "invalid ping timeout of "
+- "zero. Using 5 seconds.\n");
+- conn->ping_timeout = 5;
+- }
+-
+ spin_lock_bh(&session->lock);
+ conn->c_stage = ISCSI_CONN_STARTED;
+ session->state = ISCSI_STATE_LOGGED_IN;
+- session->queued_cmdsn = session->cmdsn;
+-
+- conn->last_recv = jiffies;
+- conn->last_ping = jiffies;
+- if (conn->recv_timeout && conn->ping_timeout)
+- mod_timer(&conn->transport_timer,
+- jiffies + (conn->recv_timeout * HZ));
+
+ switch(conn->stop_stage) {
+ case STOP_CONN_RECOVER:
+@@ -2127,11 +1646,13 @@ int iscsi_conn_start(struct iscsi_cls_conn *cls_conn)
+ * commands after successful recovery
+ */
+ conn->stop_stage = 0;
+- conn->tmf_state = TMF_INITIAL;
++ conn->tmabort_state = TMABORT_INITIAL;
+ session->age++;
+- if (session->age == 16)
+- session->age = 0;
+- break;
++ 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;
+@@ -2140,8 +1661,6 @@ int iscsi_conn_start(struct iscsi_cls_conn *cls_conn)
+ }
+ spin_unlock_bh(&session->lock);
+
+- iscsi_unblock_session(session_to_cls(session));
+- wake_up(&conn->ehwait);
+ return 0;
+ }
+ EXPORT_SYMBOL_GPL(iscsi_conn_start);
+@@ -2152,43 +1671,59 @@ flush_control_queues(struct iscsi_session *session, struct iscsi_conn *conn)
+ struct iscsi_mgmt_task *mtask, *tmp;
+
+ /* handle pending */
+- list_for_each_entry_safe(mtask, tmp, &conn->mgmtqueue, running) {
++ 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);
+- iscsi_free_mgmt_task(conn, mtask);
++ __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);
+- iscsi_free_mgmt_task(conn, mtask);
++ 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;
+
+- del_timer_sync(&conn->transport_timer);
+-
+- mutex_lock(&session->eh_mutex);
+ spin_lock_bh(&session->lock);
+ if (conn->stop_stage == STOP_CONN_TERM) {
+ spin_unlock_bh(&session->lock);
+- mutex_unlock(&session->eh_mutex);
+- return;
+- }
+-
+- /*
+- * The LLD either freed/unset the lock on us, or userspace called
+- * stop but did not create a proper connection (connection was never
+- * bound or it was unbound then stop was called).
+- */
+- if (!conn->recv_lock) {
+- spin_unlock_bh(&session->lock);
+- mutex_unlock(&session->eh_mutex);
+ return;
+ }
+
+@@ -2205,14 +1740,14 @@ static void iscsi_start_session_recovery(struct iscsi_session *session,
+ 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);
+
+- iscsi_suspend_tx(conn);
+-
+ 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
+@@ -2233,11 +1768,11 @@ static void iscsi_start_session_recovery(struct iscsi_session *session,
+ * flush queues.
+ */
+ spin_lock_bh(&session->lock);
+- fail_all_commands(conn, -1,
+- STOP_CONN_RECOVER ? DID_BUS_BUSY : DID_ERROR);
++ fail_all_commands(conn);
+ flush_control_queues(session, conn);
+ spin_unlock_bh(&session->lock);
+- mutex_unlock(&session->eh_mutex);
++
++ mutex_unlock(&conn->xmitmutex);
+ }
+
+ void iscsi_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
+@@ -2251,8 +1786,7 @@ void iscsi_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
+ iscsi_start_session_recovery(session, conn, flag);
+ break;
+ default:
+- iscsi_conn_printk(KERN_ERR, conn,
+- "invalid stop flag %d\n", flag);
++ printk(KERN_ERR "iscsi: invalid stop flag %d\n", flag);
+ }
+ }
+ EXPORT_SYMBOL_GPL(iscsi_conn_stop);
+@@ -2286,21 +1820,6 @@ int iscsi_set_param(struct iscsi_cls_conn *cls_conn,
+ uint32_t value;
+
+ switch(param) {
+- case ISCSI_PARAM_FAST_ABORT:
+- sscanf(buf, "%d", &session->fast_abort);
+- break;
+- case ISCSI_PARAM_ABORT_TMO:
+- sscanf(buf, "%d", &session->abort_timeout);
+- break;
+- case ISCSI_PARAM_LU_RESET_TMO:
+- sscanf(buf, "%d", &session->lu_reset_timeout);
+- break;
+- case ISCSI_PARAM_PING_TMO:
+- sscanf(buf, "%d", &conn->ping_timeout);
+- break;
+- case ISCSI_PARAM_RECV_TMO:
+- sscanf(buf, "%d", &conn->recv_timeout);
+- break;
+ case ISCSI_PARAM_MAX_RECV_DLENGTH:
+ sscanf(buf, "%d", &conn->max_recv_dlength);
+ break;
+@@ -2348,30 +1867,6 @@ int iscsi_set_param(struct iscsi_cls_conn *cls_conn,
+ case ISCSI_PARAM_EXP_STATSN:
+ sscanf(buf, "%u", &conn->exp_statsn);
+ break;
+- case ISCSI_PARAM_USERNAME:
+- kfree(session->username);
+- session->username = kstrdup(buf, GFP_KERNEL);
+- if (!session->username)
+- return -ENOMEM;
+- break;
+- case ISCSI_PARAM_USERNAME_IN:
+- kfree(session->username_in);
+- session->username_in = kstrdup(buf, GFP_KERNEL);
+- if (!session->username_in)
+- return -ENOMEM;
+- break;
+- case ISCSI_PARAM_PASSWORD:
+- kfree(session->password);
+- session->password = kstrdup(buf, GFP_KERNEL);
+- if (!session->password)
+- return -ENOMEM;
+- break;
+- case ISCSI_PARAM_PASSWORD_IN:
+- kfree(session->password_in);
+- session->password_in = kstrdup(buf, GFP_KERNEL);
+- if (!session->password_in)
+- return -ENOMEM;
+- break;
+ case ISCSI_PARAM_TARGET_NAME:
+ /* this should not change between logins */
+ if (session->targetname)
+@@ -2415,15 +1910,6 @@ int iscsi_session_get_param(struct iscsi_cls_session *cls_session,
+ int len;
+
+ switch(param) {
+- case ISCSI_PARAM_FAST_ABORT:
+- len = sprintf(buf, "%d\n", session->fast_abort);
+- break;
+- case ISCSI_PARAM_ABORT_TMO:
+- len = sprintf(buf, "%d\n", session->abort_timeout);
+- break;
+- case ISCSI_PARAM_LU_RESET_TMO:
+- len = sprintf(buf, "%d\n", session->lu_reset_timeout);
+- break;
+ case ISCSI_PARAM_INITIAL_R2T_EN:
+ len = sprintf(buf, "%d\n", session->initial_r2t_en);
+ break;
+@@ -2454,18 +1940,6 @@ int iscsi_session_get_param(struct iscsi_cls_session *cls_session,
+ case ISCSI_PARAM_TPGT:
+ len = sprintf(buf, "%d\n", session->tpgt);
+ break;
+- case ISCSI_PARAM_USERNAME:
+- len = sprintf(buf, "%s\n", session->username);
+- break;
+- case ISCSI_PARAM_USERNAME_IN:
+- len = sprintf(buf, "%s\n", session->username_in);
+- break;
+- case ISCSI_PARAM_PASSWORD:
+- len = sprintf(buf, "%s\n", session->password);
+- break;
+- case ISCSI_PARAM_PASSWORD_IN:
+- len = sprintf(buf, "%s\n", session->password_in);
+- break;
+ default:
+ return -ENOSYS;
+ }
+@@ -2481,12 +1955,6 @@ int iscsi_conn_get_param(struct iscsi_cls_conn *cls_conn,
+ int len;
+
+ switch(param) {
+- case ISCSI_PARAM_PING_TMO:
+- len = sprintf(buf, "%u\n", conn->ping_timeout);
+- break;
+- case ISCSI_PARAM_RECV_TMO:
+- len = sprintf(buf, "%u\n", conn->recv_timeout);
+- break;
+ case ISCSI_PARAM_MAX_RECV_DLENGTH:
+ len = sprintf(buf, "%u\n", conn->max_recv_dlength);
+ break;
+@@ -2522,66 +1990,6 @@ int iscsi_conn_get_param(struct iscsi_cls_conn *cls_conn,
+ }
+ EXPORT_SYMBOL_GPL(iscsi_conn_get_param);
+
+-int iscsi_host_get_param(struct Scsi_Host *shost, enum iscsi_host_param param,
+- char *buf)
+-{
+- struct iscsi_session *session = iscsi_hostdata(shost->hostdata);
+- int len;
+-
+- switch (param) {
+- case ISCSI_HOST_PARAM_NETDEV_NAME:
+- if (!session->netdev)
+- len = sprintf(buf, "%s\n", "default");
+- else
+- len = sprintf(buf, "%s\n", session->netdev);
+- break;
+- case ISCSI_HOST_PARAM_HWADDRESS:
+- if (!session->hwaddress)
+- len = sprintf(buf, "%s\n", "default");
+- else
+- len = sprintf(buf, "%s\n", session->hwaddress);
+- break;
+- case ISCSI_HOST_PARAM_INITIATOR_NAME:
+- if (!session->initiatorname)
+- len = sprintf(buf, "%s\n", "unknown");
+- else
+- len = sprintf(buf, "%s\n", session->initiatorname);
+- break;
+-
+- default:
+- return -ENOSYS;
+- }
+-
+- return len;
+-}
+-EXPORT_SYMBOL_GPL(iscsi_host_get_param);
+-
+-int iscsi_host_set_param(struct Scsi_Host *shost, enum iscsi_host_param param,
+- char *buf, int buflen)
+-{
+- struct iscsi_session *session = iscsi_hostdata(shost->hostdata);
+-
+- switch (param) {
+- case ISCSI_HOST_PARAM_NETDEV_NAME:
+- if (!session->netdev)
+- session->netdev = kstrdup(buf, GFP_KERNEL);
+- break;
+- case ISCSI_HOST_PARAM_HWADDRESS:
+- if (!session->hwaddress)
+- session->hwaddress = kstrdup(buf, GFP_KERNEL);
+- break;
+- case ISCSI_HOST_PARAM_INITIATOR_NAME:
+- if (!session->initiatorname)
+- session->initiatorname = kstrdup(buf, GFP_KERNEL);
+- break;
+- default:
+- return -ENOSYS;
+- }
+-
+- return 0;
+-}
+-EXPORT_SYMBOL_GPL(iscsi_host_set_param);
+-
+ MODULE_AUTHOR("Mike Christie");
+ MODULE_DESCRIPTION("iSCSI library functions");
+ MODULE_LICENSE("GPL");
+diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c
+index 65d1737..caf1836 100644
+--- a/drivers/scsi/scsi_transport_iscsi.c
++++ b/drivers/scsi/scsi_transport_iscsi.c
+@@ -30,27 +30,26 @@
+ #include <scsi/scsi_transport_iscsi.h>
+ #include <scsi/iscsi_if.h>
+
+-#define ISCSI_SESSION_ATTRS 19
+-#define ISCSI_CONN_ATTRS 13
+-#define ISCSI_HOST_ATTRS 4
+-#define ISCSI_TRANSPORT_VERSION "2.0-869"
++#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 device dev;
++ struct class_device cdev;
+
+- struct device_attribute *host_attrs[ISCSI_HOST_ATTRS + 1];
++ struct class_device_attribute *host_attrs[ISCSI_HOST_ATTRS + 1];
+ struct transport_container conn_cont;
+- struct device_attribute *conn_attrs[ISCSI_CONN_ATTRS + 1];
++ struct class_device_attribute *conn_attrs[ISCSI_CONN_ATTRS + 1];
+ struct transport_container session_cont;
+- struct device_attribute *session_attrs[ISCSI_SESSION_ATTRS + 1];
++ struct class_device_attribute *session_attrs[ISCSI_SESSION_ATTRS + 1];
+ };
+
+ static atomic_t iscsi_session_nr; /* sysfs session id for next new session */
+-static struct workqueue_struct *iscsi_eh_timer_workq;
+
+ /*
+ * list of registered transports and lock that must
+@@ -63,12 +62,12 @@ static DEFINE_SPINLOCK(iscsi_transport_lock);
+ #define to_iscsi_internal(tmpl) \
+ container_of(tmpl, struct iscsi_internal, t)
+
+-#define dev_to_iscsi_internal(_dev) \
+- container_of(_dev, struct iscsi_internal, dev)
++#define cdev_to_iscsi_internal(_cdev) \
++ container_of(_cdev, struct iscsi_internal, cdev)
+
+-static void iscsi_transport_release(struct device *dev)
++static void iscsi_transport_release(struct class_device *cdev)
+ {
+- struct iscsi_internal *priv = dev_to_iscsi_internal(dev);
++ struct iscsi_internal *priv = cdev_to_iscsi_internal(cdev);
+ kfree(priv);
+ }
+
+@@ -78,27 +77,25 @@ static void iscsi_transport_release(struct device *dev)
+ */
+ static struct class iscsi_transport_class = {
+ .name = "iscsi_transport",
+- .dev_release = iscsi_transport_release,
++ .release = iscsi_transport_release,
+ };
+
+ static ssize_t
+-show_transport_handle(struct device *dev, struct device_attribute *attr,
+- char *buf)
++show_transport_handle(struct class_device *cdev, char *buf)
+ {
+- struct iscsi_internal *priv = dev_to_iscsi_internal(dev);
++ struct iscsi_internal *priv = cdev_to_iscsi_internal(cdev);
+ return sprintf(buf, "%llu\n", (unsigned long long)iscsi_handle(priv->iscsi_transport));
+ }
+-static DEVICE_ATTR(handle, S_IRUGO, show_transport_handle, NULL);
++static CLASS_DEVICE_ATTR(handle, S_IRUGO, show_transport_handle, NULL);
+
+ #define show_transport_attr(name, format) \
+ static ssize_t \
+-show_transport_##name(struct device *dev, \
+- struct device_attribute *attr,char *buf) \
++show_transport_##name(struct class_device *cdev, char *buf) \
+ { \
+- struct iscsi_internal *priv = dev_to_iscsi_internal(dev); \
++ struct iscsi_internal *priv = cdev_to_iscsi_internal(cdev); \
+ return sprintf(buf, format"\n", priv->iscsi_transport->name); \
+ } \
+-static DEVICE_ATTR(name, S_IRUGO, show_transport_##name, NULL);
++static CLASS_DEVICE_ATTR(name, S_IRUGO, show_transport_##name, NULL);
+
+ show_transport_attr(caps, "0x%x");
+ show_transport_attr(max_lun, "%d");
+@@ -106,11 +103,11 @@ show_transport_attr(max_conn, "%d");
+ show_transport_attr(max_cmd_len, "%d");
+
+ static struct attribute *iscsi_transport_attrs[] = {
+- &dev_attr_handle.attr,
+- &dev_attr_caps.attr,
+- &dev_attr_max_lun.attr,
+- &dev_attr_max_conn.attr,
+- &dev_attr_max_cmd_len.attr,
++ &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,
+ };
+
+@@ -118,10 +115,8 @@ static struct attribute_group iscsi_transport_group = {
+ .attrs = iscsi_transport_attrs,
+ };
+
+-
+-
+ static int iscsi_setup_host(struct transport_container *tc, struct device *dev,
+- struct device *cdev)
++ struct class_device *cdev)
+ {
+ struct Scsi_Host *shost = dev_to_shost(dev);
+ struct iscsi_host *ihost = shost->shost_data;
+@@ -129,31 +124,13 @@ static int iscsi_setup_host(struct transport_container *tc, struct device *dev,
+ memset(ihost, 0, sizeof(*ihost));
+ INIT_LIST_HEAD(&ihost->sessions);
+ mutex_init(&ihost->mutex);
+- atomic_set(&ihost->nr_scans, 0);
+-
+- snprintf(ihost->scan_workq_name, KOBJ_NAME_LEN, "iscsi_scan_%d",
+- shost->host_no);
+- ihost->scan_workq = create_singlethread_workqueue(
+- ihost->scan_workq_name);
+- if (!ihost->scan_workq)
+- return -ENOMEM;
+- return 0;
+-}
+-
+-static int iscsi_remove_host(struct transport_container *tc, struct device *dev,
+- struct device *cdev)
+-{
+- struct Scsi_Host *shost = dev_to_shost(dev);
+- struct iscsi_host *ihost = shost->shost_data;
+-
+- destroy_workqueue(ihost->scan_workq);
+ return 0;
+ }
+
+ static DECLARE_TRANSPORT_CLASS(iscsi_host_class,
+ "iscsi_host",
+ iscsi_setup_host,
+- iscsi_remove_host,
++ NULL,
+ NULL);
+
+ static DECLARE_TRANSPORT_CLASS(iscsi_session_class,
+@@ -224,54 +201,6 @@ static struct iscsi_cls_conn *iscsi_conn_lookup(uint32_t sid, uint32_t cid)
+ * The following functions can be used by LLDs that allocate
+ * their own scsi_hosts or by software iscsi LLDs
+ */
+-static struct {
+- int value;
+- char *name;
+-} iscsi_session_state_names[] = {
+- { ISCSI_SESSION_LOGGED_IN, "LOGGED_IN" },
+- { ISCSI_SESSION_FAILED, "FAILED" },
+- { ISCSI_SESSION_FREE, "FREE" },
+-};
+-
+-static const char *iscsi_session_state_name(int state)
+-{
+- int i;
+- char *name = NULL;
+-
+- for (i = 0; i < ARRAY_SIZE(iscsi_session_state_names); i++) {
+- if (iscsi_session_state_names[i].value == state) {
+- name = iscsi_session_state_names[i].name;
+- break;
+- }
+- }
+- return name;
+-}
+-
+-int iscsi_session_chkready(struct iscsi_cls_session *session)
+-{
+- unsigned long flags;
+- int err;
+-
+- spin_lock_irqsave(&session->lock, flags);
+- switch (session->state) {
+- case ISCSI_SESSION_LOGGED_IN:
+- err = 0;
+- break;
+- case ISCSI_SESSION_FAILED:
+- err = DID_IMM_RETRY << 16;
+- break;
+- case ISCSI_SESSION_FREE:
+- err = DID_NO_CONNECT << 16;
+- break;
+- default:
+- err = DID_NO_CONNECT << 16;
+- break;
+- }
+- spin_unlock_irqrestore(&session->lock, flags);
+- return err;
+-}
+-EXPORT_SYMBOL_GPL(iscsi_session_chkready);
+-
+ static void iscsi_session_release(struct device *dev)
+ {
+ struct iscsi_cls_session *session = iscsi_dev_to_session(dev);
+@@ -287,25 +216,6 @@ static int iscsi_is_session_dev(const struct device *dev)
+ return dev->release == iscsi_session_release;
+ }
+
+-/**
+- * iscsi_scan_finished - helper to report when running scans are done
+- * @shost: scsi host
+- * @time: scan run time
+- *
+- * This function can be used by drives like qla4xxx to report to the scsi
+- * layer when the scans it kicked off at module load time are done.
+- */
+-int iscsi_scan_finished(struct Scsi_Host *shost, unsigned long time)
+-{
+- struct iscsi_host *ihost = shost->shost_data;
+- /*
+- * qla4xxx will have kicked off some session unblocks before calling
+- * scsi_scan_host, so just wait for them to complete.
+- */
+- return !atomic_read(&ihost->nr_scans);
+-}
+-EXPORT_SYMBOL_GPL(iscsi_scan_finished);
+-
+ static int iscsi_user_scan(struct Scsi_Host *shost, uint channel,
+ uint id, uint lun)
+ {
+@@ -324,50 +234,14 @@ static int iscsi_user_scan(struct Scsi_Host *shost, uint channel,
+ return 0;
+ }
+
+-static void iscsi_scan_session(struct work_struct *work)
+-{
+- struct iscsi_cls_session *session =
+- container_of(work, struct iscsi_cls_session, scan_work);
+- struct Scsi_Host *shost = iscsi_session_to_shost(session);
+- struct iscsi_host *ihost = shost->shost_data;
+- unsigned long flags;
+-
+- spin_lock_irqsave(&session->lock, flags);
+- if (session->state != ISCSI_SESSION_LOGGED_IN) {
+- spin_unlock_irqrestore(&session->lock, flags);
+- goto done;
+- }
+- spin_unlock_irqrestore(&session->lock, flags);
+-
+- scsi_scan_target(&session->dev, 0, session->target_id,
+- SCAN_WILD_CARD, 1);
+-done:
+- atomic_dec(&ihost->nr_scans);
+-}
+-
+ static void session_recovery_timedout(struct work_struct *work)
+ {
+ struct iscsi_cls_session *session =
+ container_of(work, struct iscsi_cls_session,
+ recovery_work.work);
+- unsigned long flags;
+-
+- iscsi_cls_session_printk(KERN_INFO, session,
+- "session recovery timed out after %d secs\n",
+- session->recovery_tmo);
+
+- spin_lock_irqsave(&session->lock, flags);
+- switch (session->state) {
+- case ISCSI_SESSION_FAILED:
+- session->state = ISCSI_SESSION_FREE;
+- break;
+- case ISCSI_SESSION_LOGGED_IN:
+- case ISCSI_SESSION_FREE:
+- /* we raced with the unblock's flush */
+- spin_unlock_irqrestore(&session->lock, flags);
+- return;
+- }
+- spin_unlock_irqrestore(&session->lock, flags);
++ 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);
+@@ -375,103 +249,22 @@ static void session_recovery_timedout(struct work_struct *work)
+ scsi_target_unblock(&session->dev);
+ }
+
+-static void __iscsi_unblock_session(struct work_struct *work)
+-{
+- struct iscsi_cls_session *session =
+- container_of(work, struct iscsi_cls_session,
+- unblock_work);
+- struct Scsi_Host *shost = iscsi_session_to_shost(session);
+- struct iscsi_host *ihost = shost->shost_data;
+- unsigned long flags;
+-
+- /*
+- * The recovery and unblock work get run from the same workqueue,
+- * so try to cancel it if it was going to run after this unblock.
+- */
+- cancel_delayed_work(&session->recovery_work);
+- spin_lock_irqsave(&session->lock, flags);
+- session->state = ISCSI_SESSION_LOGGED_IN;
+- spin_unlock_irqrestore(&session->lock, flags);
+- /* start IO */
+- scsi_target_unblock(&session->dev);
+- /*
+- * Only do kernel scanning if the driver is properly hooked into
+- * the async scanning code (drivers like iscsi_tcp do login and
+- * scanning from userspace).
+- */
+- if (shost->hostt->scan_finished) {
+- if (queue_work(ihost->scan_workq, &session->scan_work))
+- atomic_inc(&ihost->nr_scans);
+- }
+-}
+-
+-/**
+- * iscsi_unblock_session - set a session as logged in and start IO.
+- * @session: iscsi session
+- *
+- * Mark a session as ready to accept IO.
+- */
+ void iscsi_unblock_session(struct iscsi_cls_session *session)
+ {
+- queue_work(iscsi_eh_timer_workq, &session->unblock_work);
+- /*
+- * make sure all the events have completed before tell the driver
+- * it is safe
+- */
+- flush_workqueue(iscsi_eh_timer_workq);
++ if (!cancel_delayed_work(&session->recovery_work))
++ flush_scheduled_work();
++ scsi_target_unblock(&session->dev);
+ }
+ EXPORT_SYMBOL_GPL(iscsi_unblock_session);
+
+-static void __iscsi_block_session(struct work_struct *work)
+-{
+- struct iscsi_cls_session *session =
+- container_of(work, struct iscsi_cls_session,
+- block_work);
+- unsigned long flags;
+-
+- spin_lock_irqsave(&session->lock, flags);
+- session->state = ISCSI_SESSION_FAILED;
+- spin_unlock_irqrestore(&session->lock, flags);
+- scsi_target_block(&session->dev);
+- queue_delayed_work(iscsi_eh_timer_workq, &session->recovery_work,
+- session->recovery_tmo * HZ);
+-}
+-
+ void iscsi_block_session(struct iscsi_cls_session *session)
+ {
+- queue_work(iscsi_eh_timer_workq, &session->block_work);
++ scsi_target_block(&session->dev);
++ schedule_delayed_work(&session->recovery_work,
++ session->recovery_tmo * HZ);
+ }
+ EXPORT_SYMBOL_GPL(iscsi_block_session);
+
+-static void __iscsi_unbind_session(struct work_struct *work)
+-{
+- struct iscsi_cls_session *session =
+- container_of(work, struct iscsi_cls_session,
+- unbind_work);
+- struct Scsi_Host *shost = iscsi_session_to_shost(session);
+- struct iscsi_host *ihost = shost->shost_data;
+-
+- /* Prevent new scans and make sure scanning is not in progress */
+- mutex_lock(&ihost->mutex);
+- if (list_empty(&session->host_list)) {
+- mutex_unlock(&ihost->mutex);
+- return;
+- }
+- list_del_init(&session->host_list);
+- mutex_unlock(&ihost->mutex);
+-
+- scsi_remove_target(&session->dev);
+- iscsi_session_event(session, ISCSI_KEVENT_UNBIND_SESSION);
+-}
+-
+-static int iscsi_unbind_session(struct iscsi_cls_session *session)
+-{
+- struct Scsi_Host *shost = iscsi_session_to_shost(session);
+- struct iscsi_host *ihost = shost->shost_data;
+-
+- return queue_work(ihost->scan_workq, &session->unbind_work);
+-}
+-
+ struct iscsi_cls_session *
+ iscsi_alloc_session(struct Scsi_Host *shost,
+ struct iscsi_transport *transport)
+@@ -485,15 +278,9 @@ iscsi_alloc_session(struct Scsi_Host *shost,
+
+ session->transport = transport;
+ session->recovery_tmo = 120;
+- session->state = ISCSI_SESSION_FREE;
+ INIT_DELAYED_WORK(&session->recovery_work, session_recovery_timedout);
+ INIT_LIST_HEAD(&session->host_list);
+ INIT_LIST_HEAD(&session->sess_list);
+- INIT_WORK(&session->unblock_work, __iscsi_unblock_session);
+- INIT_WORK(&session->block_work, __iscsi_block_session);
+- INIT_WORK(&session->unbind_work, __iscsi_unbind_session);
+- INIT_WORK(&session->scan_work, iscsi_scan_session);
+- spin_lock_init(&session->lock);
+
+ /* this is released in the dev's release function */
+ scsi_host_get(shost);
+@@ -510,7 +297,6 @@ 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;
+- unsigned long flags;
+ int err;
+
+ ihost = shost->shost_data;
+@@ -521,21 +307,15 @@ int iscsi_add_session(struct iscsi_cls_session *session, unsigned int target_id)
+ session->sid);
+ err = device_add(&session->dev);
+ if (err) {
+- iscsi_cls_session_printk(KERN_ERR, session,
+- "could not register session's dev\n");
++ dev_printk(KERN_ERR, &session->dev, "iscsi: could not "
++ "register session's dev\n");
+ goto release_host;
+ }
+ transport_register_device(&session->dev);
+
+- spin_lock_irqsave(&sesslock, flags);
+- list_add(&session->sess_list, &sesslist);
+- spin_unlock_irqrestore(&sesslock, flags);
+-
+ mutex_lock(&ihost->mutex);
+ list_add(&session->host_list, &ihost->sessions);
+ mutex_unlock(&ihost->mutex);
+-
+- iscsi_session_event(session, ISCSI_KEVENT_CREATE_SESSION);
+ return 0;
+
+ release_host:
+@@ -548,10 +328,9 @@ EXPORT_SYMBOL_GPL(iscsi_add_session);
+ * iscsi_create_session - create iscsi class session
+ * @shost: scsi host
+ * @transport: iscsi transport
+- * @target_id: which target
+ *
+ * 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,
+@@ -571,65 +350,19 @@ iscsi_create_session(struct Scsi_Host *shost,
+ }
+ EXPORT_SYMBOL_GPL(iscsi_create_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;
+-}
+-
+-static int iscsi_iter_destroy_conn_fn(struct device *dev, void *data)
+-{
+- if (!iscsi_is_conn_dev(dev))
+- return 0;
+- return iscsi_destroy_conn(iscsi_dev_to_conn(dev));
+-}
+-
+ 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;
+- unsigned long flags;
+- int err;
+-
+- spin_lock_irqsave(&sesslock, flags);
+- list_del(&session->sess_list);
+- spin_unlock_irqrestore(&sesslock, flags);
+
+- /* make sure there are no blocks/unblocks queued */
+- flush_workqueue(iscsi_eh_timer_workq);
+- /* make sure the timedout callout is not running */
+ if (!cancel_delayed_work(&session->recovery_work))
+- flush_workqueue(iscsi_eh_timer_workq);
+- /*
+- * If we are blocked let commands flow again. The lld or iscsi
+- * layer should set up the queuecommand to fail commands.
+- * We assume that LLD will not be calling block/unblock while
+- * removing the session.
+- */
+- spin_lock_irqsave(&session->lock, flags);
+- session->state = ISCSI_SESSION_FREE;
+- spin_unlock_irqrestore(&session->lock, flags);
++ flush_scheduled_work();
+
+- scsi_target_unblock(&session->dev);
+- /* flush running scans then delete devices */
+- flush_workqueue(ihost->scan_workq);
+- __iscsi_unbind_session(&session->unbind_work);
++ mutex_lock(&ihost->mutex);
++ list_del(&session->host_list);
++ mutex_unlock(&ihost->mutex);
+
+- /* hw iscsi may not have removed all connections from session */
+- err = device_for_each_child(&session->dev, NULL,
+- iscsi_iter_destroy_conn_fn);
+- if (err)
+- iscsi_cls_session_printk(KERN_ERR, session,
+- "Could not delete all connections "
+- "for session. Error %d.\n", err);
++ scsi_remove_target(&session->dev);
+
+ transport_unregister_device(&session->dev);
+ device_del(&session->dev);
+@@ -638,9 +371,9 @@ EXPORT_SYMBOL_GPL(iscsi_remove_session);
+
+ void iscsi_free_session(struct iscsi_cls_session *session)
+ {
+- iscsi_session_event(session, ISCSI_KEVENT_DESTROY_SESSION);
+ put_device(&session->dev);
+ }
++
+ EXPORT_SYMBOL_GPL(iscsi_free_session);
+
+ /**
+@@ -649,7 +382,7 @@ EXPORT_SYMBOL_GPL(iscsi_free_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);
+@@ -658,6 +391,20 @@ int iscsi_destroy_session(struct iscsi_cls_session *session)
+ }
+ 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
+@@ -671,13 +418,12 @@ EXPORT_SYMBOL_GPL(iscsi_destroy_session);
+ * 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;
+- unsigned long flags;
+ int err;
+
+ conn = kzalloc(sizeof(*conn) + transport->conndata_size, GFP_KERNEL);
+@@ -701,16 +447,11 @@ iscsi_create_conn(struct iscsi_cls_session *session, uint32_t cid)
+ conn->dev.release = iscsi_conn_release;
+ err = device_register(&conn->dev);
+ if (err) {
+- iscsi_cls_session_printk(KERN_ERR, session, "could not "
+- "register connection's dev\n");
++ dev_printk(KERN_ERR, &conn->dev, "iscsi: could not register "
++ "connection's dev\n");
+ goto release_parent_ref;
+ }
+ transport_register_device(&conn->dev);
+-
+- spin_lock_irqsave(&connlock, flags);
+- list_add(&conn->conn_list, &connlist);
+- conn->active = 1;
+- spin_unlock_irqrestore(&connlock, flags);
+ return conn;
+
+ release_parent_ref:
+@@ -724,23 +465,17 @@ EXPORT_SYMBOL_GPL(iscsi_create_conn);
+
+ /**
+ * iscsi_destroy_conn - destroy iscsi class connection
+- * @conn: iscsi cls session
++ * @session: iscsi cls session
+ *
+ * This can be called from a LLD or iscsi_transport.
+- */
++ **/
+ int iscsi_destroy_conn(struct iscsi_cls_conn *conn)
+ {
+- unsigned long flags;
+-
+- spin_lock_irqsave(&connlock, flags);
+- conn->active = 0;
+- list_del(&conn->conn_list);
+- spin_unlock_irqrestore(&connlock, flags);
+-
+ transport_unregister_device(&conn->dev);
+ device_unregister(&conn->dev);
+ return 0;
+ }
++
+ EXPORT_SYMBOL_GPL(iscsi_destroy_conn);
+
+ /*
+@@ -809,8 +544,8 @@ int iscsi_recv_pdu(struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr,
+ skb = alloc_skb(len, GFP_ATOMIC);
+ if (!skb) {
+ iscsi_conn_error(conn, ISCSI_ERR_CONN_FAILED);
+- iscsi_cls_conn_printk(KERN_ERR, conn, "can not deliver "
+- "control PDU: OOM\n");
++ dev_printk(KERN_ERR, &conn->dev, "iscsi: can not deliver "
++ "control PDU: OOM\n");
+ return -ENOMEM;
+ }
+
+@@ -843,8 +578,8 @@ void iscsi_conn_error(struct iscsi_cls_conn *conn, enum iscsi_err error)
+
+ skb = alloc_skb(len, GFP_ATOMIC);
+ if (!skb) {
+- iscsi_cls_conn_printk(KERN_ERR, conn, "gracefully ignored "
+- "conn error (%d)\n", error);
++ dev_printk(KERN_ERR, &conn->dev, "iscsi: gracefully ignored "
++ "conn error (%d)\n", error);
+ return;
+ }
+
+@@ -858,8 +593,8 @@ void iscsi_conn_error(struct iscsi_cls_conn *conn, enum iscsi_err error)
+
+ iscsi_broadcast_skb(skb, GFP_ATOMIC);
+
+- iscsi_cls_conn_printk(KERN_INFO, conn, "detected conn error (%d)\n",
+- error);
++ dev_printk(KERN_INFO, &conn->dev, "iscsi: detected conn error (%d)\n",
++ error);
+ }
+ EXPORT_SYMBOL_GPL(iscsi_conn_error);
+
+@@ -874,10 +609,12 @@ iscsi_if_send_reply(int pid, int seq, int type, int done, int multi,
+ 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;
+- }
++ /*
++ * FIXME:
++ * user is supposed to react on iferror == -ENOMEM;
++ * see iscsi_if_rx().
++ */
++ BUG_ON(!skb);
+
+ nlh = __nlmsg_put(skb, pid, seq, t, (len - sizeof(*nlh)), 0);
+ nlh->nlmsg_flags = flags;
+@@ -914,8 +651,8 @@ iscsi_if_get_stats(struct iscsi_transport *transport, struct nlmsghdr *nlh)
+
+ skbstat = alloc_skb(len, GFP_ATOMIC);
+ if (!skbstat) {
+- iscsi_cls_conn_printk(KERN_ERR, conn, "can not "
+- "deliver stats: OOM\n");
++ dev_printk(KERN_ERR, &conn->dev, "iscsi: can not "
++ "deliver stats: OOM\n");
+ return -ENOMEM;
+ }
+
+@@ -950,87 +687,144 @@ iscsi_if_get_stats(struct iscsi_transport *transport, struct nlmsghdr *nlh)
+ }
+
+ /**
+- * iscsi_session_event - send session destr. completion event
+- * @session: iscsi class session
+- * @event: type of event
+- */
+-int iscsi_session_event(struct iscsi_cls_session *session,
+- enum iscsi_uevent_e event)
++ * 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(session->transport);
++ 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) {
+- iscsi_cls_session_printk(KERN_ERR, session,
+- "Cannot notify userspace of session "
+- "event %u\n", event);
++ 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(session->transport);
++ 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;
+
+- ev->type = event;
+- switch (event) {
+- case ISCSI_KEVENT_DESTROY_SESSION:
+- ev->r.d_session.host_no = shost->host_no;
+- ev->r.d_session.sid = session->sid;
+- break;
+- case ISCSI_KEVENT_CREATE_SESSION:
+- ev->r.c_session_ret.host_no = shost->host_no;
+- ev->r.c_session_ret.sid = session->sid;
+- break;
+- case ISCSI_KEVENT_UNBIND_SESSION:
+- ev->r.unbind_session.host_no = shost->host_no;
+- ev->r.unbind_session.sid = session->sid;
+- break;
+- default:
+- iscsi_cls_session_printk(KERN_ERR, session, "Invalid event "
+- "%u.\n", event);
+- kfree_skb(skb);
++ /*
++ * 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)
+- iscsi_cls_session_printk(KERN_ERR, session,
+- "Cannot notify userspace of session "
+- "event %u. Check iscsi daemon\n",
+- event);
++ 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_session_event);
++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.cmds_max,
+- ev->u.c_session.queue_depth,
+ 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;
+@@ -1041,34 +835,47 @@ 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",
++ 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) {
+- iscsi_cls_session_printk(KERN_ERR, session,
+- "couldn't create a new connection.");
++ 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);
+@@ -1140,50 +947,15 @@ static int
+ iscsi_tgt_dscvr(struct iscsi_transport *transport,
+ struct iscsi_uevent *ev)
+ {
+- struct Scsi_Host *shost;
+ struct sockaddr *dst_addr;
+- int err;
+
+ if (!transport->tgt_dscvr)
+ return -EINVAL;
+
+- shost = scsi_host_lookup(ev->u.tgt_dscvr.host_no);
+- if (IS_ERR(shost)) {
+- printk(KERN_ERR "target discovery could not find host no %u\n",
+- ev->u.tgt_dscvr.host_no);
+- return -ENODEV;
+- }
+-
+-
+ dst_addr = (struct sockaddr *)((char*)ev + sizeof(*ev));
+- err = transport->tgt_dscvr(shost, ev->u.tgt_dscvr.type,
+- ev->u.tgt_dscvr.enable, dst_addr);
+- scsi_host_put(shost);
+- return err;
+-}
+-
+-static int
+-iscsi_set_host_param(struct iscsi_transport *transport,
+- struct iscsi_uevent *ev)
+-{
+- char *data = (char*)ev + sizeof(*ev);
+- struct Scsi_Host *shost;
+- int err;
+-
+- if (!transport->set_host_param)
+- return -ENOSYS;
+-
+- shost = scsi_host_lookup(ev->u.set_host_param.host_no);
+- if (IS_ERR(shost)) {
+- printk(KERN_ERR "set_host_param could not find host no %u\n",
+- ev->u.set_host_param.host_no);
+- return -ENODEV;
+- }
+-
+- err = transport->set_host_param(shost, ev->u.set_host_param.param,
+- data, ev->u.set_host_param.len);
+- scsi_host_put(shost);
+- return err;
++ return transport->tgt_dscvr(ev->u.tgt_dscvr.type,
++ ev->u.tgt_dscvr.host_no,
++ ev->u.tgt_dscvr.enable, dst_addr);
+ }
+
+ static int
+@@ -1195,6 +967,7 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
+ 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)
+@@ -1212,16 +985,13 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
+ break;
+ case ISCSI_UEVENT_DESTROY_SESSION:
+ session = iscsi_session_lookup(ev->u.d_session.sid);
+- if (session)
++ 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_UNBIND_SESSION:
+- session = iscsi_session_lookup(ev->u.d_session.sid);
+- if (session)
+- iscsi_unbind_session(session);
+- else
++ } else
+ err = -EINVAL;
+ break;
+ case ISCSI_UEVENT_CREATE_CONN:
+@@ -1279,11 +1049,8 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
+ case ISCSI_UEVENT_TGT_DSCVR:
+ err = iscsi_tgt_dscvr(transport, ev);
+ break;
+- case ISCSI_UEVENT_SET_HOST_PARAM:
+- err = iscsi_set_host_param(transport, ev);
+- break;
+ default:
+- err = -ENOSYS;
++ err = -EINVAL;
+ break;
+ }
+
+@@ -1292,55 +1059,70 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
+ }
+
+ /*
+- * Get message from skb. Each message is processed by iscsi_if_recv_msg.
+- * Malformed skbs with wrong lengths or invalid creds are not processed.
++ * 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 sk_buff *skb)
++iscsi_if_rx(struct sock *sk, int len)
+ {
++ struct sk_buff *skb;
++
+ mutex_lock(&rx_queue_mutex);
+- while (skb->len >= NLMSG_SPACE(0)) {
+- int err;
+- uint32_t rlen;
+- struct nlmsghdr *nlh;
+- struct iscsi_uevent *ev;
+-
+- nlh = nlmsg_hdr(skb);
+- if (nlh->nlmsg_len < sizeof(*nlh) ||
+- skb->len < nlh->nlmsg_len) {
+- break;
++ while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) {
++ if (NETLINK_CREDS(skb)->uid) {
++ skb_pull(skb, skb->len);
++ goto free_skb;
+ }
+
+- ev = NLMSG_DATA(nlh);
+- rlen = NLMSG_ALIGN(nlh->nlmsg_len);
+- if (rlen > skb->len)
+- rlen = skb->len;
++ while (skb->len >= NLMSG_SPACE(0)) {
++ int err;
++ uint32_t rlen;
++ struct nlmsghdr *nlh;
++ struct iscsi_uevent *ev;
+
+- 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)
++ nlh = nlmsg_hdr(skb);
++ if (nlh->nlmsg_len < sizeof(*nlh) ||
++ skb->len < nlh->nlmsg_len) {
+ 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);
++ }
++
++ 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 device_attribute dev_attr_##_prefix##_##_name = \
++struct class_device_attribute class_device_attr_##_prefix##_##_name = \
+ __ATTR(_name,_mode,_show,_store)
+
+ /*
+@@ -1348,10 +1130,9 @@ struct device_attribute dev_attr_##_prefix##_##_name = \
+ */
+ #define iscsi_conn_attr_show(param) \
+ static ssize_t \
+-show_conn_param_##param(struct device *dev, \
+- struct device_attribute *attr, char *buf) \
++show_conn_param_##param(struct class_device *cdev, char *buf) \
+ { \
+- struct iscsi_cls_conn *conn = iscsi_dev_to_conn(dev->parent); \
++ struct iscsi_cls_conn *conn = iscsi_cdev_to_conn(cdev); \
+ struct iscsi_transport *t = conn->transport; \
+ return t->get_conn_param(conn, param, buf); \
+ }
+@@ -1372,66 +1153,43 @@ 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);
+-iscsi_conn_attr(ping_tmo, ISCSI_PARAM_PING_TMO);
+-iscsi_conn_attr(recv_tmo, ISCSI_PARAM_RECV_TMO);
++
++#define iscsi_cdev_to_session(_cdev) \
++ iscsi_dev_to_session(_cdev->dev)
+
+ /*
+ * iSCSI session attrs
+ */
+-#define iscsi_session_attr_show(param, perm) \
++#define iscsi_session_attr_show(param) \
+ static ssize_t \
+-show_session_param_##param(struct device *dev, \
+- struct device_attribute *attr, char *buf) \
++show_session_param_##param(struct class_device *cdev, char *buf) \
+ { \
+- struct iscsi_cls_session *session = \
+- iscsi_dev_to_session(dev->parent); \
++ struct iscsi_cls_session *session = iscsi_cdev_to_session(cdev); \
+ struct iscsi_transport *t = session->transport; \
+- \
+- if (perm && !capable(CAP_SYS_ADMIN)) \
+- return -EACCES; \
+ return t->get_session_param(session, param, buf); \
+ }
+
+-#define iscsi_session_attr(field, param, perm) \
+- iscsi_session_attr_show(param, perm) \
++#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, 0);
+-iscsi_session_attr(initial_r2t, ISCSI_PARAM_INITIAL_R2T_EN, 0);
+-iscsi_session_attr(max_outstanding_r2t, ISCSI_PARAM_MAX_R2T, 0);
+-iscsi_session_attr(immediate_data, ISCSI_PARAM_IMM_DATA_EN, 0);
+-iscsi_session_attr(first_burst_len, ISCSI_PARAM_FIRST_BURST, 0);
+-iscsi_session_attr(max_burst_len, ISCSI_PARAM_MAX_BURST, 0);
+-iscsi_session_attr(data_pdu_in_order, ISCSI_PARAM_PDU_INORDER_EN, 0);
+-iscsi_session_attr(data_seq_in_order, ISCSI_PARAM_DATASEQ_INORDER_EN, 0);
+-iscsi_session_attr(erl, ISCSI_PARAM_ERL, 0);
+-iscsi_session_attr(tpgt, ISCSI_PARAM_TPGT, 0);
+-iscsi_session_attr(username, ISCSI_PARAM_USERNAME, 1);
+-iscsi_session_attr(username_in, ISCSI_PARAM_USERNAME_IN, 1);
+-iscsi_session_attr(password, ISCSI_PARAM_PASSWORD, 1);
+-iscsi_session_attr(password_in, ISCSI_PARAM_PASSWORD_IN, 1);
+-iscsi_session_attr(fast_abort, ISCSI_PARAM_FAST_ABORT, 0);
+-iscsi_session_attr(abort_tmo, ISCSI_PARAM_ABORT_TMO, 0);
+-iscsi_session_attr(lu_reset_tmo, ISCSI_PARAM_LU_RESET_TMO, 0);
+-
+-static ssize_t
+-show_priv_session_state(struct device *dev, struct device_attribute *attr,
+- char *buf)
+-{
+- struct iscsi_cls_session *session = iscsi_dev_to_session(dev->parent);
+- return sprintf(buf, "%s\n", iscsi_session_state_name(session->state));
+-}
+-static ISCSI_CLASS_ATTR(priv_sess, state, S_IRUGO, show_priv_session_state,
+- 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 device *dev, \
+- struct device_attribute *attr, char *buf) \
++show_priv_session_##field(struct class_device *cdev, char *buf) \
+ { \
+- struct iscsi_cls_session *session = \
+- iscsi_dev_to_session(dev->parent); \
++ struct iscsi_cls_session *session = iscsi_cdev_to_session(cdev);\
+ return sprintf(buf, format"\n", session->field); \
+ }
+
+@@ -1441,32 +1199,9 @@ static ISCSI_CLASS_ATTR(priv_sess, field, S_IRUGO, show_priv_session_##field, \
+ NULL)
+ iscsi_priv_session_attr(recovery_tmo, "%d");
+
+-/*
+- * iSCSI host attrs
+- */
+-#define iscsi_host_attr_show(param) \
+-static ssize_t \
+-show_host_param_##param(struct device *dev, \
+- struct device_attribute *attr, char *buf) \
+-{ \
+- struct Scsi_Host *shost = transport_class_to_shost(dev); \
+- struct iscsi_internal *priv = to_iscsi_internal(shost->transportt); \
+- return priv->iscsi_transport->get_host_param(shost, param, buf); \
+-}
+-
+-#define iscsi_host_attr(field, param) \
+- iscsi_host_attr_show(param) \
+-static ISCSI_CLASS_ATTR(host, field, S_IRUGO, show_host_param_##param, \
+- NULL);
+-
+-iscsi_host_attr(netdev, ISCSI_HOST_PARAM_NETDEV_NAME);
+-iscsi_host_attr(hwaddress, ISCSI_HOST_PARAM_HWADDRESS);
+-iscsi_host_attr(ipaddress, ISCSI_HOST_PARAM_IPADDRESS);
+-iscsi_host_attr(initiatorname, ISCSI_HOST_PARAM_INITIATOR_NAME);
+-
+ #define SETUP_PRIV_SESSION_RD_ATTR(field) \
+ do { \
+- priv->session_attrs[count] = &dev_attr_priv_sess_##field; \
++ priv->session_attrs[count] = &class_device_attr_priv_sess_##field; \
+ count++; \
+ } while (0)
+
+@@ -1474,7 +1209,7 @@ do { \
+ #define SETUP_SESSION_RD_ATTR(field, param_flag) \
+ do { \
+ if (tt->param_mask & param_flag) { \
+- priv->session_attrs[count] = &dev_attr_sess_##field; \
++ priv->session_attrs[count] = &class_device_attr_sess_##field; \
+ count++; \
+ } \
+ } while (0)
+@@ -1482,15 +1217,7 @@ do { \
+ #define SETUP_CONN_RD_ATTR(field, param_flag) \
+ do { \
+ if (tt->param_mask & param_flag) { \
+- priv->conn_attrs[count] = &dev_attr_conn_##field; \
+- count++; \
+- } \
+-} while (0)
+-
+-#define SETUP_HOST_RD_ATTR(field, param_flag) \
+-do { \
+- if (tt->host_param_mask & param_flag) { \
+- priv->host_attrs[count] = &dev_attr_host_##field; \
++ priv->conn_attrs[count] = &class_device_attr_conn_##field; \
+ count++; \
+ } \
+ } while (0)
+@@ -1581,31 +1308,24 @@ iscsi_register_transport(struct iscsi_transport *tt)
+ priv->iscsi_transport = tt;
+ priv->t.user_scan = iscsi_user_scan;
+
+- priv->dev.class = &iscsi_transport_class;
+- snprintf(priv->dev.bus_id, BUS_ID_SIZE, "%s", tt->name);
+- err = device_register(&priv->dev);
++ 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->dev.kobj, &iscsi_transport_group);
++ err = sysfs_create_group(&priv->cdev.kobj, &iscsi_transport_group);
+ if (err)
+- goto unregister_dev;
++ 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);
+
+- SETUP_HOST_RD_ATTR(netdev, ISCSI_HOST_NETDEV_NAME);
+- SETUP_HOST_RD_ATTR(ipaddress, ISCSI_HOST_IPADDRESS);
+- SETUP_HOST_RD_ATTR(hwaddress, ISCSI_HOST_HWADDRESS);
+- SETUP_HOST_RD_ATTR(initiatorname, ISCSI_HOST_INITIATOR_NAME);
+- BUG_ON(count > ISCSI_HOST_ATTRS);
+- priv->host_attrs[count] = NULL;
+- count = 0;
+-
+ /* connection parameters */
+ priv->conn_cont.ac.attrs = &priv->conn_attrs[0];
+ priv->conn_cont.ac.class = &iscsi_connection_class.class;
+@@ -1623,8 +1343,6 @@ iscsi_register_transport(struct iscsi_transport *tt)
+ 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);
+- SETUP_CONN_RD_ATTR(ping_tmo, ISCSI_PING_TMO);
+- SETUP_CONN_RD_ATTR(recv_tmo, ISCSI_RECV_TMO);
+
+ BUG_ON(count > ISCSI_CONN_ATTRS);
+ priv->conn_attrs[count] = NULL;
+@@ -1646,15 +1364,7 @@ iscsi_register_transport(struct iscsi_transport *tt)
+ SETUP_SESSION_RD_ATTR(erl, ISCSI_ERL);
+ SETUP_SESSION_RD_ATTR(targetname, ISCSI_TARGET_NAME);
+ SETUP_SESSION_RD_ATTR(tpgt, ISCSI_TPGT);
+- SETUP_SESSION_RD_ATTR(password, ISCSI_USERNAME);
+- SETUP_SESSION_RD_ATTR(password_in, ISCSI_USERNAME_IN);
+- SETUP_SESSION_RD_ATTR(username, ISCSI_PASSWORD);
+- SETUP_SESSION_RD_ATTR(username_in, ISCSI_PASSWORD_IN);
+- SETUP_SESSION_RD_ATTR(fast_abort, ISCSI_FAST_ABORT);
+- SETUP_SESSION_RD_ATTR(abort_tmo, ISCSI_ABORT_TMO);
+- SETUP_SESSION_RD_ATTR(lu_reset_tmo,ISCSI_LU_RESET_TMO);
+ SETUP_PRIV_SESSION_RD_ATTR(recovery_tmo);
+- SETUP_PRIV_SESSION_RD_ATTR(state);
+
+ BUG_ON(count > ISCSI_SESSION_ATTRS);
+ priv->session_attrs[count] = NULL;
+@@ -1666,8 +1376,8 @@ iscsi_register_transport(struct iscsi_transport *tt)
+ printk(KERN_NOTICE "iscsi: registered transport (%s)\n", tt->name);
+ return &priv->t;
+
+-unregister_dev:
+- device_unregister(&priv->dev);
++unregister_cdev:
++ class_device_unregister(&priv->cdev);
+ free_priv:
+ kfree(priv);
+ return NULL;
+@@ -1694,8 +1404,8 @@ int iscsi_unregister_transport(struct iscsi_transport *tt)
+ transport_container_unregister(&priv->session_cont);
+ transport_container_unregister(&priv->t.host_attrs);
+
+- sysfs_remove_group(&priv->dev.kobj, &iscsi_transport_group);
+- device_unregister(&priv->dev);
++ sysfs_remove_group(&priv->cdev.kobj, &iscsi_transport_group);
++ class_device_unregister(&priv->cdev);
+ mutex_unlock(&rx_queue_mutex);
+
+ return 0;
+@@ -1727,21 +1437,15 @@ static __init int iscsi_transport_init(void)
+ if (err)
+ goto unregister_conn_class;
+
+- nls = netlink_kernel_create(&init_net, NETLINK_ISCSI, 1, iscsi_if_rx, NULL,
++ nls = netlink_kernel_create(NETLINK_ISCSI, 1, iscsi_if_rx, NULL,
+ THIS_MODULE);
+ if (!nls) {
+ err = -ENOBUFS;
+ goto unregister_session_class;
+ }
+
+- iscsi_eh_timer_workq = create_singlethread_workqueue("iscsi_eh");
+- if (!iscsi_eh_timer_workq)
+- goto release_nls;
+-
+ return 0;
+
+-release_nls:
+- netlink_kernel_release(nls);
+ unregister_session_class:
+ transport_class_unregister(&iscsi_session_class);
+ unregister_conn_class:
+@@ -1755,8 +1459,7 @@ unregister_transport_class:
+
+ static void __exit iscsi_transport_exit(void)
+ {
+- destroy_workqueue(iscsi_eh_timer_workq);
+- netlink_kernel_release(nls);
++ sock_release(nls->sk_socket);
+ transport_class_unregister(&iscsi_connection_class);
+ transport_class_unregister(&iscsi_session_class);
+ transport_class_unregister(&iscsi_host_class);
+diff --git a/include/scsi/iscsi_if.h b/include/scsi/iscsi_if.h
+index e19e584..55ebf03 100644
+--- a/include/scsi/iscsi_if.h
++++ b/include/scsi/iscsi_if.h
+@@ -48,16 +48,12 @@ enum iscsi_uevent_e {
+ ISCSI_UEVENT_TRANSPORT_EP_DISCONNECT = UEVENT_BASE + 14,
+
+ ISCSI_UEVENT_TGT_DSCVR = UEVENT_BASE + 15,
+- ISCSI_UEVENT_SET_HOST_PARAM = UEVENT_BASE + 16,
+- ISCSI_UEVENT_UNBIND_SESSION = UEVENT_BASE + 17,
+
+ /* up events */
+ ISCSI_KEVENT_RECV_PDU = KEVENT_BASE + 1,
+ ISCSI_KEVENT_CONN_ERROR = KEVENT_BASE + 2,
+ ISCSI_KEVENT_IF_ERROR = KEVENT_BASE + 3,
+ ISCSI_KEVENT_DESTROY_SESSION = KEVENT_BASE + 4,
+- ISCSI_KEVENT_UNBIND_SESSION = KEVENT_BASE + 5,
+- ISCSI_KEVENT_CREATE_SESSION = KEVENT_BASE + 6,
+ };
+
+ enum iscsi_tgt_dscvr {
+@@ -75,8 +71,6 @@ struct iscsi_uevent {
+ /* messages u -> k */
+ struct msg_create_session {
+ uint32_t initial_cmdsn;
+- uint16_t cmds_max;
+- uint16_t queue_depth;
+ } c_session;
+ struct msg_destroy_session {
+ uint32_t sid;
+@@ -142,11 +136,6 @@ struct iscsi_uevent {
+ */
+ uint32_t enable;
+ } tgt_dscvr;
+- struct msg_set_host_param {
+- uint32_t host_no;
+- uint32_t param; /* enum iscsi_host_param */
+- uint32_t len;
+- } set_host_param;
+ } u;
+ union {
+ /* messages k -> u */
+@@ -159,10 +148,6 @@ struct iscsi_uevent {
+ uint32_t sid;
+ uint32_t cid;
+ } c_conn_ret;
+- struct msg_unbind_session {
+- uint32_t sid;
+- uint32_t host_no;
+- } unbind_session;
+ struct msg_recv_req {
+ uint32_t sid;
+ uint32_t cid;
+@@ -238,18 +223,6 @@ enum iscsi_param {
+ ISCSI_PARAM_CONN_PORT,
+ ISCSI_PARAM_CONN_ADDRESS,
+
+- ISCSI_PARAM_USERNAME,
+- ISCSI_PARAM_USERNAME_IN,
+- ISCSI_PARAM_PASSWORD,
+- ISCSI_PARAM_PASSWORD_IN,
+-
+- ISCSI_PARAM_FAST_ABORT,
+- ISCSI_PARAM_ABORT_TMO,
+- ISCSI_PARAM_LU_RESET_TMO,
+- ISCSI_PARAM_HOST_RESET_TMO,
+-
+- ISCSI_PARAM_PING_TMO,
+- ISCSI_PARAM_RECV_TMO,
+ /* must always be last */
+ ISCSI_PARAM_MAX,
+ };
+@@ -276,30 +249,6 @@ enum iscsi_param {
+ #define ISCSI_SESS_RECOVERY_TMO (1 << ISCSI_PARAM_SESS_RECOVERY_TMO)
+ #define ISCSI_CONN_PORT (1 << ISCSI_PARAM_CONN_PORT)
+ #define ISCSI_CONN_ADDRESS (1 << ISCSI_PARAM_CONN_ADDRESS)
+-#define ISCSI_USERNAME (1 << ISCSI_PARAM_USERNAME)
+-#define ISCSI_USERNAME_IN (1 << ISCSI_PARAM_USERNAME_IN)
+-#define ISCSI_PASSWORD (1 << ISCSI_PARAM_PASSWORD)
+-#define ISCSI_PASSWORD_IN (1 << ISCSI_PARAM_PASSWORD_IN)
+-#define ISCSI_FAST_ABORT (1 << ISCSI_PARAM_FAST_ABORT)
+-#define ISCSI_ABORT_TMO (1 << ISCSI_PARAM_ABORT_TMO)
+-#define ISCSI_LU_RESET_TMO (1 << ISCSI_PARAM_LU_RESET_TMO)
+-#define ISCSI_HOST_RESET_TMO (1 << ISCSI_PARAM_HOST_RESET_TMO)
+-#define ISCSI_PING_TMO (1 << ISCSI_PARAM_PING_TMO)
+-#define ISCSI_RECV_TMO (1 << ISCSI_PARAM_RECV_TMO)
+-
+-/* iSCSI HBA params */
+-enum iscsi_host_param {
+- ISCSI_HOST_PARAM_HWADDRESS,
+- ISCSI_HOST_PARAM_INITIATOR_NAME,
+- ISCSI_HOST_PARAM_NETDEV_NAME,
+- ISCSI_HOST_PARAM_IPADDRESS,
+- ISCSI_HOST_PARAM_MAX,
+-};
+-
+-#define ISCSI_HOST_HWADDRESS (1 << ISCSI_HOST_PARAM_HWADDRESS)
+-#define ISCSI_HOST_INITIATOR_NAME (1 << ISCSI_HOST_PARAM_INITIATOR_NAME)
+-#define ISCSI_HOST_NETDEV_NAME (1 << ISCSI_HOST_PARAM_NETDEV_NAME)
+-#define ISCSI_HOST_IPADDRESS (1 << ISCSI_HOST_PARAM_IPADDRESS)
+
+ #define iscsi_ptr(_handle) ((void*)(unsigned long)_handle)
+ #define iscsi_handle(_ptr) ((uint64_t)(unsigned long)_ptr)
+@@ -323,9 +272,6 @@ enum iscsi_host_param {
+ #define CAP_MULTI_CONN 0x40
+ #define CAP_TEXT_NEGO 0x80
+ #define CAP_MARKERS 0x100
+-#define CAP_FW_DB 0x200
+-#define CAP_SENDTARGETS_OFFLOAD 0x400
+-#define CAP_DATA_PATH_OFFLOAD 0x800
+
+ /*
+ * These flags describes reason of stop_conn() call
+diff --git a/include/scsi/iscsi_proto.h b/include/scsi/iscsi_proto.h
+index e0593bf..8d1e4e8 100644
+--- a/include/scsi/iscsi_proto.h
++++ b/include/scsi/iscsi_proto.h
+@@ -21,15 +21,13 @@
+ #ifndef ISCSI_PROTO_H
+ #define ISCSI_PROTO_H
+
+-#include <linux/types.h>
+-
+ #define ISCSI_DRAFT20_VERSION 0x00
+
+ /* default iSCSI listen port for incoming connections */
+ #define ISCSI_LISTEN_PORT 3260
+
+ /* Padding word length */
+-#define ISCSI_PAD_LEN 4
++#define PAD_WORD_LEN 4
+
+ /*
+ * useful common(control and data pathes) macro
+@@ -45,8 +43,8 @@
+ /* 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, age) ((__force itt_t)\
+- ((itt) | ((age) << ISCSI_AGE_SHIFT)))
++#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)
+
+@@ -112,7 +110,6 @@ struct iscsi_ahs_hdr {
+
+ #define ISCSI_AHSTYPE_CDB 1
+ #define ISCSI_AHSTYPE_RLENGTH 2
+-#define ISCSI_CDB_SIZE 16
+
+ /* iSCSI PDU Header */
+ struct iscsi_cmd {
+@@ -126,7 +123,7 @@ struct iscsi_cmd {
+ __be32 data_length;
+ __be32 cmdsn;
+ __be32 exp_statsn;
+- uint8_t cdb[ISCSI_CDB_SIZE]; /* SCSI Command Block */
++ uint8_t cdb[16]; /* SCSI Command Block */
+ /* Additional Data (Command Dependent) */
+ };
+
+@@ -150,15 +147,6 @@ struct iscsi_rlength_ahdr {
+ __be32 read_length;
+ };
+
+-/* Extended CDB AHS */
+-struct iscsi_ecdb_ahdr {
+- __be16 ahslength; /* CDB length - 15, including reserved byte */
+- uint8_t ahstype;
+- uint8_t reserved;
+- /* 4-byte aligned extended CDB spillover */
+- uint8_t ecdb[260 - ISCSI_CDB_SIZE];
+-};
+-
+ /* SCSI Response Header */
+ struct iscsi_cmd_rsp {
+ uint8_t opcode;
+@@ -612,8 +600,6 @@ struct iscsi_reject {
+ #define ISCSI_MIN_MAX_BURST_LEN 512
+ #define ISCSI_MAX_MAX_BURST_LEN 16777215
+
+-#define ISCSI_DEF_TIME2WAIT 2
+-
+ /************************* RFC 3720 End *****************************/
+
+ #endif /* ISCSI_PROTO_H */
+diff --git a/include/scsi/libiscsi.h b/include/scsi/libiscsi.h
+index cd3ca63..ea0816d 100644
+--- a/include/scsi/libiscsi.h
++++ b/include/scsi/libiscsi.h
+@@ -48,8 +48,9 @@ struct iscsi_nopin;
+ #define debug_scsi(fmt...)
+ #endif
+
+-#define ISCSI_DEF_XMIT_CMDS_MAX 128 /* must be power of 2 */
+-#define ISCSI_MGMT_CMDS_MAX 16 /* must be power of 2 */
++#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
+
+@@ -57,31 +58,21 @@ struct iscsi_nopin;
+ #define ISCSI_MAX_CMD_PER_LUN 128
+
+ /* Task Mgmt states */
+-enum {
+- TMF_INITIAL,
+- TMF_QUEUED,
+- TMF_SUCCESS,
+- TMF_FAILED,
+- TMF_TIMEDOUT,
+- TMF_NOT_FOUND,
+-};
++#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)
+
+-#define ISCSI_ADDRESS_BUF_LEN 64
+-
+-enum {
+- /* this is the maximum possible storage for AHSs */
+- ISCSI_MAX_AHS_SIZE = sizeof(struct iscsi_ecdb_ahdr) +
+- sizeof(struct iscsi_rlength_ahdr),
+- ISCSI_DIGEST_SIZE = sizeof(__u32),
+-};
+-
+ struct iscsi_mgmt_task {
+ /*
+ * Becuae LLDs allocate their hdr differently, this is a pointer to
+@@ -89,7 +80,7 @@ struct iscsi_mgmt_task {
+ */
+ struct iscsi_hdr *hdr;
+ char *data; /* mgmt payload */
+- unsigned data_count; /* counts data to be sent */
++ int data_count; /* counts data to be sent */
+ uint32_t itt; /* this ITT */
+ void *dd_data; /* driver/transport data */
+ struct list_head running;
+@@ -103,23 +94,23 @@ enum {
+
+ struct iscsi_cmd_task {
+ /*
+- * Because LLDs allocate their hdr differently, this is a pointer
+- * and length to that storage. It must be setup at session
+- * creation time.
++ * 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;
+- unsigned short hdr_max;
+- unsigned short hdr_len; /* accumulated size of hdr used */
+ int itt; /* this ITT */
++ int datasn; /* DataSN */
+
+ uint32_t unsol_datasn;
+- unsigned imm_count; /* imm-data (bytes) */
+- unsigned unsol_count; /* unsolicited (bytes)*/
++ int imm_count; /* imm-data (bytes) */
++ int unsol_count; /* unsolicited (bytes)*/
+ /* offset in unsolicited stream (bytes); */
+- unsigned unsol_offset;
+- unsigned data_count; /* remaining Data-Out */
++ 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;
+@@ -128,19 +119,6 @@ struct iscsi_cmd_task {
+ void *dd_data; /* driver/transport data */
+ };
+
+-static inline void* iscsi_next_hdr(struct iscsi_cmd_task *ctask)
+-{
+- return (void*)ctask->hdr + ctask->hdr_len;
+-}
+-
+-/* Connection's states */
+-enum {
+- ISCSI_CONN_INITIAL_STAGE,
+- ISCSI_CONN_STARTED,
+- ISCSI_CONN_STOPPED,
+- ISCSI_CONN_CLEANUP_WAIT,
+-};
+-
+ struct iscsi_conn {
+ struct iscsi_cls_conn *cls_conn; /* ptr to class connection */
+ void *dd_data; /* iscsi_transport data */
+@@ -154,12 +132,6 @@ struct iscsi_conn {
+ * conn_stop() flag: stop to recover, stop to terminate
+ */
+ int stop_stage;
+- struct timer_list transport_timer;
+- unsigned long last_recv;
+- unsigned long last_ping;
+- int ping_timeout;
+- int recv_timeout;
+- struct iscsi_mgmt_task *ping_mtask;
+
+ /* iSCSI connection-wide sequencing */
+ uint32_t exp_statsn;
+@@ -180,24 +152,30 @@ struct iscsi_conn {
+ struct iscsi_cmd_task *ctask; /* xmit ctask in progress */
+
+ /* xmit */
+- struct list_head mgmtqueue; /* mgmt (control) xmit queue */
++ 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 list_head requeue; /* tasks needing another run */
+ 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 tmf_timer;
+- int tmf_state; /* see TMF_INITIAL, etc.*/
++ struct timer_list tmabort_timer;
++ int tmabort_state; /* see TMABORT_INITIAL, etc.*/
+
+ /* negotiated params */
+- unsigned max_recv_dlength; /* initiator_max_recv_dsl*/
+- unsigned max_xmit_dlength; /* target_max_recv_dsl */
++ 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;
+@@ -205,12 +183,6 @@ struct iscsi_conn {
+ /* values userspace uses to id a conn */
+ int persistent_port;
+ char *persistent_address;
+- /* remote portal currently connected to */
+- int portal_port;
+- char portal_address[ISCSI_ADDRESS_BUF_LEN];
+- /* local address */
+- int local_port;
+- char local_address[ISCSI_ADDRESS_BUF_LEN];
+
+ /* MIB-statistics */
+ uint64_t txdata_octets;
+@@ -225,66 +197,34 @@ struct iscsi_conn {
+
+ /* custom statistics */
+ uint32_t eh_abort_cnt;
+- uint32_t fmr_unalign_cnt;
+ };
+
+-struct iscsi_pool {
++struct iscsi_queue {
+ struct kfifo *queue; /* FIFO Queue */
+ void **pool; /* Pool of elements */
+ int max; /* Max number of elements */
+ };
+
+-/* Session's states */
+-enum {
+- ISCSI_STATE_FREE = 1,
+- ISCSI_STATE_LOGGED_IN,
+- ISCSI_STATE_FAILED,
+- ISCSI_STATE_TERMINATE,
+- ISCSI_STATE_IN_RECOVERY,
+- ISCSI_STATE_RECOVERY_FAILED,
+- ISCSI_STATE_LOGGING_OUT,
+-};
+-
+ struct iscsi_session {
+- /*
+- * Syncs up the scsi eh thread with the iscsi eh thread when sending
+- * task management functions. This must be taken before the session
+- * and recv lock.
+- */
+- struct mutex eh_mutex;
+-
+ /* iSCSI session-wide sequencing */
+ uint32_t cmdsn;
+ uint32_t exp_cmdsn;
+ uint32_t max_cmdsn;
+
+- /* This tracks the reqs queued into the initiator */
+- uint32_t queued_cmdsn;
+-
+ /* configuration */
+- int abort_timeout;
+- int lu_reset_timeout;
+ int initial_r2t_en;
+- unsigned max_r2t;
++ int max_r2t;
+ int imm_data_en;
+- unsigned first_burst;
+- unsigned max_burst;
++ int first_burst;
++ int max_burst;
+ int time2wait;
+ int time2retain;
+ int pdu_inorder_en;
+ int dataseq_inorder_en;
+ int erl;
+- int fast_abort;
+ int tpgt;
+- char *username;
+- char *username_in;
+- char *password;
+- char *password_in;
+ char *targetname;
+- char *initiatorname;
+- /* hw address or netdev iscsi connection is bound to */
+- char *hwaddress;
+- char *netdev;
++
+ /* control data */
+ struct iscsi_transport *tt;
+ struct Scsi_Host *host;
+@@ -300,10 +240,10 @@ struct iscsi_session {
+
+ int cmds_max; /* size of cmds array */
+ struct iscsi_cmd_task **cmds; /* Original Cmds arr */
+- struct iscsi_pool cmdpool; /* PDU's pool */
++ 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_pool mgmtpool; /* Mgmt PDU's pool */
++ struct iscsi_queue mgmtpool; /* Mgmt PDU's pool */
+ };
+
+ /*
+@@ -312,26 +252,15 @@ struct iscsi_session {
+ 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_eh_device_reset(struct scsi_cmnd *sc);
+ extern int iscsi_queuecommand(struct scsi_cmnd *sc,
+ void (*done)(struct scsi_cmnd *));
+
+-
+-/*
+- * iSCSI host helpers.
+- */
+-extern int iscsi_host_set_param(struct Scsi_Host *shost,
+- enum iscsi_host_param param, char *buf,
+- int buflen);
+-extern int iscsi_host_get_param(struct Scsi_Host *shost,
+- enum iscsi_host_param param, char *buf);
+-
+ /*
+ * session management
+ */
+ extern struct iscsi_cls_session *
+ iscsi_session_setup(struct iscsi_transport *, struct scsi_transport_template *,
+- uint16_t, uint16_t, int, int, uint32_t, uint32_t *);
++ 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 *);
+@@ -343,10 +272,6 @@ extern int iscsi_session_get_param(struct iscsi_cls_session *cls_session,
+ #define session_to_cls(_sess) \
+ hostdata_session(_sess->host->hostdata)
+
+-#define iscsi_session_printk(prefix, _sess, fmt, a...) \
+- iscsi_cls_session_printk(prefix, \
+- (struct iscsi_cls_session *)session_to_cls(_sess), fmt, ##a)
+-
+ /*
+ * connection management
+ */
+@@ -361,47 +286,26 @@ 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);
+
+-#define iscsi_conn_printk(prefix, _c, fmt, a...) \
+- iscsi_cls_conn_printk(prefix, _c->cls_conn, fmt, ##a)
+-
+ /*
+ * pdu and task processing
+ */
+-extern void iscsi_update_cmdsn(struct iscsi_session *, struct iscsi_nopin *);
++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 *);
+-extern void iscsi_requeue_ctask(struct iscsi_cmd_task *ctask);
+-extern void iscsi_free_mgmt_task(struct iscsi_conn *conn,
+- struct iscsi_mgmt_task *mtask);
+
+ /*
+ * generic helpers
+ */
+-extern void iscsi_pool_free(struct iscsi_pool *);
+-extern int iscsi_pool_init(struct iscsi_pool *, int, void ***, int);
+-
+-/*
+- * inline functions to deal with padding.
+- */
+-static inline unsigned int
+-iscsi_padded(unsigned int len)
+-{
+- return (len + ISCSI_PAD_LEN - 1) & ~(ISCSI_PAD_LEN - 1);
+-}
+-
+-static inline unsigned int
+-iscsi_padding(unsigned int len)
+-{
+- len &= (ISCSI_PAD_LEN - 1);
+- if (len)
+- len = ISCSI_PAD_LEN - len;
+- return len;
+-}
++extern void iscsi_pool_free(struct iscsi_queue *, void **);
++extern int iscsi_pool_init(struct iscsi_queue *, int, void ***, int);
+
+ #endif
+diff --git a/include/scsi/scsi_transport_iscsi.h b/include/scsi/scsi_transport_iscsi.h
+index aab1eae..d5c218d 100644
+--- a/include/scsi/scsi_transport_iscsi.h
++++ b/include/scsi/scsi_transport_iscsi.h
+@@ -24,8 +24,6 @@
+ #define SCSI_TRANSPORT_ISCSI_H
+
+ #include <linux/device.h>
+-#include <linux/list.h>
+-#include <linux/mutex.h>
+ #include <scsi/iscsi_if.h>
+
+ struct scsi_transport_template;
+@@ -81,8 +79,7 @@ struct iscsi_transport {
+ char *name;
+ unsigned int caps;
+ /* LLD sets this to indicate what values it can export to sysfs */
+- uint64_t param_mask;
+- uint64_t host_param_mask;
++ unsigned int param_mask;
+ struct scsi_host_template *host_template;
+ /* LLD connection data size */
+ int conndata_size;
+@@ -92,8 +89,7 @@ struct iscsi_transport {
+ unsigned int max_conn;
+ unsigned int max_cmd_len;
+ struct iscsi_cls_session *(*create_session) (struct iscsi_transport *it,
+- struct scsi_transport_template *t, uint16_t, uint16_t,
+- uint32_t sn, uint32_t *hn);
++ 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);
+@@ -109,18 +105,14 @@ struct iscsi_transport {
+ enum iscsi_param param, char *buf);
+ int (*get_session_param) (struct iscsi_cls_session *session,
+ enum iscsi_param param, char *buf);
+- int (*get_host_param) (struct Scsi_Host *shost,
+- enum iscsi_host_param param, char *buf);
+- int (*set_host_param) (struct Scsi_Host *shost,
+- enum iscsi_host_param param, char *buf,
+- int buflen);
+ 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);
+- int (*init_cmd_task) (struct iscsi_cmd_task *ctask);
++ void (*init_cmd_task) (struct iscsi_cmd_task *ctask);
+ void (*init_mgmt_task) (struct iscsi_conn *conn,
+- struct iscsi_mgmt_task *mtask);
++ 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,
+@@ -132,7 +124,7 @@ struct iscsi_transport {
+ uint64_t *ep_handle);
+ int (*ep_poll) (uint64_t ep_handle, int timeout_ms);
+ void (*ep_disconnect) (uint64_t ep_handle);
+- int (*tgt_dscvr) (struct Scsi_Host *shost, enum iscsi_tgt_dscvr type,
++ int (*tgt_dscvr) (enum iscsi_tgt_dscvr type, uint32_t host_no,
+ uint32_t enable, struct sockaddr *dst_addr);
+ };
+
+@@ -149,6 +141,13 @@ 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 */
+@@ -162,25 +161,18 @@ struct iscsi_cls_conn {
+ #define iscsi_dev_to_conn(_dev) \
+ container_of(_dev, struct iscsi_cls_conn, dev)
+
+-#define iscsi_conn_to_session(_conn) \
+- iscsi_dev_to_session(_conn->dev.parent)
+-
+-/* iscsi class session state */
+-enum {
+- ISCSI_SESSION_LOGGED_IN,
+- ISCSI_SESSION_FAILED,
+- ISCSI_SESSION_FREE,
+-};
++/* 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;
+- spinlock_t lock;
+- struct work_struct block_work;
+- struct work_struct unblock_work;
+- struct work_struct scan_work;
+- struct work_struct unbind_work;
+
+ /* recovery fields */
+ int recovery_tmo;
+@@ -188,7 +180,6 @@ struct iscsi_cls_session {
+
+ int target_id;
+
+- int state;
+ int sid; /* session id */
+ void *dd_data; /* LLD private data */
+ struct device dev; /* sysfs transport/container device */
+@@ -205,28 +196,18 @@ struct iscsi_cls_session {
+
+ struct iscsi_host {
+ struct list_head sessions;
+- atomic_t nr_scans;
+ struct mutex mutex;
+- struct workqueue_struct *scan_workq;
+- char scan_workq_name[KOBJ_NAME_LEN];
+ };
+
+ /*
+ * session and connection functions that can be used by HW iSCSI LLDs
+ */
+-#define iscsi_cls_session_printk(prefix, _cls_session, fmt, a...) \
+- dev_printk(prefix, &(_cls_session)->dev, fmt, ##a)
+-
+-#define iscsi_cls_conn_printk(prefix, _cls_conn, fmt, a...) \
+- dev_printk(prefix, &(_cls_conn)->dev, fmt, ##a)
+-
+-extern int iscsi_session_chkready(struct iscsi_cls_session *session);
+ 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_session_event(struct iscsi_cls_session *session,
+- enum iscsi_uevent_e event);
++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);
+@@ -238,6 +219,6 @@ extern struct iscsi_cls_conn *iscsi_create_conn(struct iscsi_cls_session *sess,
+ 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);
+-extern int iscsi_scan_finished(struct Scsi_Host *shost, unsigned long time);
++
+
+ #endif
+--
+1.5.3.8
+
diff --git a/kernel_patches/backport/2.6.9_U4/iscsi_02_add_to_2_6_9.patch b/kernel_patches/backport/2.6.9_U4/iscsi_02_add_to_2_6_9.patch
new file mode 100644
index 0000000..1f05d95
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U4/iscsi_02_add_to_2_6_9.patch
@@ -0,0 +1,180 @@
+diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c
+index 4376840..11dfaf9 100644
+--- a/drivers/scsi/iscsi_tcp.c
++++ b/drivers/scsi/iscsi_tcp.c
+@@ -2145,7 +2145,6 @@ static void iscsi_tcp_session_destroy(struct iscsi_cls_session *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,
+diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
+index d37048c..60f5846 100644
+--- a/drivers/scsi/libiscsi.c
++++ b/drivers/scsi/libiscsi.c
+@@ -1366,7 +1366,6 @@ iscsi_session_setup(struct iscsi_transport *iscsit,
+ 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);
+diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c
+index 8133c22..f1c68f7 100644
+--- a/drivers/scsi/scsi_transport_iscsi.c
++++ b/drivers/scsi/scsi_transport_iscsi.c
+@@ -65,6 +65,8 @@ static DEFINE_SPINLOCK(iscsi_transport_lock);
+ #define cdev_to_iscsi_internal(_cdev) \
+ container_of(_cdev, struct iscsi_internal, cdev)
+
++extern int attribute_container_init(void);
++
+ static void iscsi_transport_release(struct class_device *cdev)
+ {
+ struct iscsi_internal *priv = cdev_to_iscsi_internal(cdev);
+@@ -80,6 +82,17 @@ static struct class iscsi_transport_class = {
+ .release = iscsi_transport_release,
+ };
+
++static void iscsi_host_class_release(struct class_device *class_dev)
++{
++ struct Scsi_Host *shost = transport_class_to_shost(class_dev);
++ put_device(&shost->shost_gendev);
++}
++
++struct class iscsi_host_class = {
++ .name = "iscsi_host",
++ .release = iscsi_host_class_release,
++};
++
+ static ssize_t
+ show_transport_handle(struct class_device *cdev, char *buf)
+ {
+@@ -115,10 +128,8 @@ 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)
++static int iscsi_setup_host(struct Scsi_Host *shost)
+ {
+- struct Scsi_Host *shost = dev_to_shost(dev);
+ struct iscsi_host *ihost = shost->shost_data;
+
+ memset(ihost, 0, sizeof(*ihost));
+@@ -127,12 +138,6 @@ static int iscsi_setup_host(struct transport_container *tc, struct device *dev,
+ 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,
+@@ -216,24 +221,6 @@ 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 =
+@@ -362,8 +349,6 @@ void iscsi_remove_session(struct iscsi_cls_session *session)
+ list_del(&session->host_list);
+ mutex_unlock(&ihost->mutex);
+
+- scsi_remove_target(&session->dev);
+-
+ transport_unregister_device(&session->dev);
+ device_del(&session->dev);
+ }
+@@ -1269,24 +1254,6 @@ static int iscsi_conn_match(struct attribute_container *cont,
+ 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)
+ {
+@@ -1306,7 +1273,6 @@ iscsi_register_transport(struct iscsi_transport *tt)
+ 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);
+@@ -1319,12 +1285,10 @@ iscsi_register_transport(struct iscsi_transport *tt)
+ 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_attrs = &priv->host_attrs[0];
++ priv->t.host_class = &iscsi_host_class;
++ priv->t.host_setup = iscsi_setup_host;
+ 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];
+@@ -1402,7 +1366,6 @@ int iscsi_unregister_transport(struct iscsi_transport *tt)
+
+ 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);
+@@ -1420,6 +1420,7 @@ static __init int iscsi_transport_init(void)
+ ISCSI_TRANSPORT_VERSION);
+
+ atomic_set(&iscsi_session_nr, 0);
++ attribute_container_init();
+
+ err = class_register(&iscsi_transport_class);
+ if (err)
+ return err;
diff --git a/kernel_patches/backport/2.6.9_U4/iscsi_03_add_session_wq.patch b/kernel_patches/backport/2.6.9_U4/iscsi_03_add_session_wq.patch
new file mode 100644
index 0000000..55564c5
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U4/iscsi_03_add_session_wq.patch
@@ -0,0 +1,76 @@
+diff --git a/drivers/infiniband/ulp/iser/iser_initiator.c b/drivers/infiniband/ulp/iser/iser_initiator.c
+index a6f2303..5d62cc0 100644
+--- a/drivers/infiniband/ulp/iser/iser_initiator.c
++++ b/drivers/infiniband/ulp/iser/iser_initiator.c
+@@ -610,7 +610,7 @@ void iser_snd_completion(struct iser_desc *tx_desc)
+
+ if (resume_tx) {
+ iser_dbg("%ld resuming tx\n",jiffies);
+- scsi_queue_work(conn->session->host, &conn->xmitwork);
++ queue_work(conn->session->wq, &conn->xmitwork);
+ }
+
+ if (tx_desc->type == ISCSI_TX_CONTROL) {
+diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
+index e8020a5..43e9128 100644
+--- a/drivers/scsi/libiscsi.c
++++ b/drivers/scsi/libiscsi.c
+@@ -828,7 +828,7 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
+ session->cmdsn, session->max_cmdsn - session->exp_cmdsn + 1);
+ spin_unlock(&session->lock);
+
+- scsi_queue_work(host, &conn->xmitwork);
++ queue_work(session->wq, &conn->xmitwork);
+ return 0;
+
+ reject:
+@@ -928,7 +928,7 @@ iscsi_conn_send_generic(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ else
+ __kfifo_put(conn->mgmtqueue, (void*)&mtask, sizeof(void*));
+
+- scsi_queue_work(session->host, &conn->xmitwork);
++ queue_work(session->wq, &conn->xmitwork);
+ return 0;
+ }
+
+@@ -1415,6 +1415,9 @@ iscsi_session_setup(struct iscsi_transport *iscsit,
+ INIT_LIST_HEAD(&mtask->running);
+ }
+
++ session->wq = create_singlethread_workqueue("");
++ BUG_ON(!session->wq);
++
+ if (scsi_add_host(shost, NULL))
+ goto add_host_fail;
+
+@@ -1462,6 +1465,8 @@ void iscsi_session_teardown(struct iscsi_cls_session *cls_session)
+
+ kfree(session->targetname);
+
++ destroy_workqueue(session->wq);
++
+ iscsi_destroy_session(cls_session);
+ scsi_host_put(shost);
+ module_put(owner);
+@@ -1595,7 +1600,7 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn)
+ }
+
+ /* flush queued up work because we free the connection below */
+- scsi_flush_work(session->host);
++ flush_workqueue(session->wq);
+
+ spin_lock_bh(&session->lock);
+ kfree(conn->data);
+diff --git a/include/scsi/libiscsi.h b/include/scsi/libiscsi.h
+index ea0816d..e8a95f5 100644
+--- a/include/scsi/libiscsi.h
++++ b/include/scsi/libiscsi.h
+@@ -244,6 +244,8 @@ struct iscsi_session {
+ int mgmtpool_max; /* size of mgmt array */
+ struct iscsi_mgmt_task **mgmt_cmds; /* Original mgmt arr */
+ struct iscsi_queue mgmtpool; /* Mgmt PDU's pool */
++
++ struct workqueue_struct *wq;
+ };
+
+ /*
diff --git a/kernel_patches/backport/2.6.9_U4/iscsi_04_inet_sock_to_opt.patch b/kernel_patches/backport/2.6.9_U4/iscsi_04_inet_sock_to_opt.patch
new file mode 100644
index 0000000..1fb2376
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U4/iscsi_04_inet_sock_to_opt.patch
@@ -0,0 +1,13 @@
+diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c
+index 905efc4..f73a743 100644
+--- a/drivers/scsi/iscsi_tcp.c
++++ b/drivers/scsi/iscsi_tcp.c
+@@ -2027,7 +2027,7 @@ iscsi_tcp_conn_get_param(struct iscsi_cls_conn *cls_conn,
+ {
+ struct iscsi_conn *conn = cls_conn->dd_data;
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+- struct inet_sock *inet;
++ struct inet_opt *inet;
+ struct ipv6_pinfo *np;
+ struct sock *sk;
+ int len;
diff --git a/kernel_patches/backport/2.6.9_U4/iscsi_05_release_host_lock_before_eh.patch b/kernel_patches/backport/2.6.9_U4/iscsi_05_release_host_lock_before_eh.patch
new file mode 100644
index 0000000..c994506
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U4/iscsi_05_release_host_lock_before_eh.patch
@@ -0,0 +1,60 @@
+diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
+index 7db081b..211944e 100644
+--- a/drivers/scsi/libiscsi.c
++++ b/drivers/scsi/libiscsi.c
+@@ -968,12 +968,14 @@ int iscsi_eh_host_reset(struct scsi_cmnd *sc)
+ struct iscsi_conn *conn = session->leadconn;
+ int fail_session = 0;
+
++ spin_unlock_irq(host->host_lock);
+ 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);
++ spin_lock_irq(host->host_lock);
+ return FAILED;
+ }
+
+@@ -1005,6 +1007,7 @@ failed:
+ else
+ goto failed;
+ spin_unlock_bh(&session->lock);
++ spin_lock_irq(host->host_lock);
+
+ return SUCCESS;
+ }
+@@ -1162,13 +1165,16 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
+ struct iscsi_conn *conn;
+ struct iscsi_session *session;
+ int rc;
++ struct Scsi_Host *shost = sc->device->host;
+
++ spin_unlock_irq(shost->host_lock);
+ /*
+ * 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");
++ spin_lock_irq(shost->host_lock);
+ return SUCCESS;
+ }
+
+@@ -1253,6 +1259,7 @@ success_cleanup:
+
+ success_rel_mutex:
+ mutex_unlock(&conn->xmitmutex);
++ spin_lock_irq(shost->host_lock);
+ return SUCCESS;
+
+ failed:
+@@ -1260,6 +1267,7 @@ failed:
+ mutex_unlock(&conn->xmitmutex);
+
+ debug_scsi("abort failed [sc %lx itt 0x%x]\n", (long)sc, ctask->itt);
++ spin_lock_irq(shost->host_lock);
+ return FAILED;
+ }
+ EXPORT_SYMBOL_GPL(iscsi_eh_abort);
diff --git a/kernel_patches/backport/2.6.9_U4/iscsi_06_scsi_addons.patch b/kernel_patches/backport/2.6.9_U4/iscsi_06_scsi_addons.patch
new file mode 100644
index 0000000..a114696
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U4/iscsi_06_scsi_addons.patch
@@ -0,0 +1,75 @@
+diff --git a/drivers/scsi/init.c b/drivers/scsi/init.c
+new file mode 100644
+index 0000000..58cf933
+--- /dev/null
++++ b/drivers/scsi/init.c
+@@ -0,0 +1 @@
++#include "src/init.c"
+diff --git a/drivers/scsi/attribute_container.c b/drivers/scsi/attribute_container.c
+new file mode 100644
+index 0000000..58cf933
+--- /dev/null
++++ b/drivers/scsi/attribute_container.c
+@@ -0,0 +1 @@
++#include "../drivers/base/attribute_container.c"
+diff --git a/drivers/scsi/transport_class.c b/drivers/scsi/transport_class.c
+new file mode 100644
+index 0000000..58cf933
+--- /dev/null
++++ b/drivers/scsi/transport_class.c
+@@ -0,0 +1 @@
++#include "../drivers/base/transport_class.c"
+diff --git a/drivers/scsi/klist.c b/drivers/scsi/klist.c
+new file mode 100644
+index 0000000..58cf933
+--- /dev/null
++++ b/drivers/scsi/klist.c
+@@ -0,0 +1 @@
++#include "../../lib/klist.c"
+diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
+new file mode 100644
+index 0000000..58cf933
+--- /dev/null
++++ b/drivers/scsi/scsi.c
+@@ -0,0 +1 @@
++#include "src/scsi.c"
+diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
+new file mode 100644
+index 0000000..58cf933
+--- /dev/null
++++ b/drivers/scsi/scsi_lib.c
+@@ -0,0 +1 @@
++#include "src/scsi_lib.c"
+diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
+new file mode 100644
+index 0000000..58cf933
+--- /dev/null
++++ b/drivers/scsi/scsi_scan.c
+@@ -0,0 +1 @@
++#include "src/scsi_scan.c"
+diff --git a/drivers/scsi/libiscsi_f.c b/drivers/scsi/libiscsi_f.c
+new file mode 100644
+index 0000000..58cf933
+--- /dev/null
++++ b/drivers/scsi/libiscsi_f.c
+@@ -0,0 +1 @@
++#include "libiscsi.c"
+diff --git a/drivers/scsi/scsi_transport_iscsi_f.c b/drivers/scsi/scsi_transport_iscsi_f.c
+new file mode 100644
+index 0000000..58cf933
+--- /dev/null
++++ b/drivers/scsi/scsi_transport_iscsi_f.c
+@@ -0,0 +1 @@
++#include "scsi_transport_iscsi.c"
+diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
+index e212608..3bf2015 100644
+--- a/drivers/scsi/Makefile
++++ b/drivers/scsi/Makefile
+@@ -3,2 +3,7 @@
+ obj-$(CONFIG_SCSI_ISCSI_ATTRS) += scsi_transport_iscsi.o
+ obj-$(CONFIG_ISCSI_TCP) += libiscsi.o iscsi_tcp.o
++
++CFLAGS_attribute_container.o = $(BACKPORT_INCLUDES)/src/
++
++scsi_transport_iscsi-y := scsi_transport_iscsi_f.o scsi.o scsi_lib.o init.o klist.o attribute_container.o transport_class.o
++libiscsi-y := libiscsi_f.o scsi_scan.o
diff --git a/kernel_patches/backport/2.6.9_U4/iser_01_revert_da9c0c770e775e655e3f77c96d91ee557b117adb.patch b/kernel_patches/backport/2.6.9_U4/iser_01_revert_da9c0c770e775e655e3f77c96d91ee557b117adb.patch
new file mode 100644
index 0000000..101fdc6
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U4/iser_01_revert_da9c0c770e775e655e3f77c96d91ee557b117adb.patch
@@ -0,0 +1,44 @@
+diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
+index bad8dac..75ecabe 100644
+--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
++++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
+@@ -211,10 +211,10 @@ iscsi_iser_ctask_xmit(struct iscsi_conn *conn,
+ int error = 0;
+
+ if (ctask->sc->sc_data_direction == DMA_TO_DEVICE) {
+- BUG_ON(scsi_bufflen(ctask->sc) == 0);
++ BUG_ON(ctask->sc->request_bufflen == 0);
+
+ debug_scsi("cmd [itt %x total %d imm %d unsol_data %d\n",
+- ctask->itt, scsi_bufflen(ctask->sc),
++ ctask->itt, ctask->sc->request_bufflen,
+ ctask->imm_count, ctask->unsol_count);
+ }
+
+diff --git a/drivers/infiniband/ulp/iser/iser_initiator.c b/drivers/infiniband/ulp/iser/iser_initiator.c
+index 5d62cc0..1ae80d8 100644
+--- a/drivers/infiniband/ulp/iser/iser_initiator.c
++++ b/drivers/infiniband/ulp/iser/iser_initiator.c
+@@ -349,12 +349,18 @@ int iser_send_command(struct iscsi_conn *conn,
+ else
+ data_buf = &iser_ctask->data[ISER_DIR_OUT];
+
+- if (scsi_sg_count(sc)) { /* using a scatter list */
+- data_buf->buf = scsi_sglist(sc);
+- data_buf->size = scsi_sg_count(sc);
++ if (sc->use_sg) { /* using a scatter list */
++ data_buf->buf = sc->request_buffer;
++ data_buf->size = sc->use_sg;
++ } else if (sc->request_bufflen) {
++ /* using a single buffer - convert it into one entry SG */
++ sg_init_one(&data_buf->sg_single,
++ sc->request_buffer, sc->request_bufflen);
++ data_buf->buf = &data_buf->sg_single;
++ data_buf->size = 1;
+ }
+
+- data_buf->data_len = scsi_bufflen(sc);
++ data_buf->data_len = sc->request_bufflen;
+
+ if (hdr->flags & ISCSI_FLAG_CMD_READ) {
+ err = iser_prepare_read_cmd(ctask, edtl);
diff --git a/kernel_patches/backport/2.6.9_U4/iser_02_revert_d8196ed2181b4595eaf464a5bcbddb6c28649a39.patch b/kernel_patches/backport/2.6.9_U4/iser_02_revert_d8196ed2181b4595eaf464a5bcbddb6c28649a39.patch
new file mode 100644
index 0000000..7b21cba
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U4/iser_02_revert_d8196ed2181b4595eaf464a5bcbddb6c28649a39.patch
@@ -0,0 +1,12 @@
+diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
+index bad8dac..933429b 100644
+--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
++++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
+@@ -586,7 +586,6 @@ static struct iscsi_transport iscsi_iser_transport = {
+ ISCSI_FAST_ABORT | ISCSI_ABORT_TMO |
+ ISCSI_PING_TMO | ISCSI_RECV_TMO,
+ .host_param_mask = ISCSI_HOST_HWADDRESS |
+- ISCSI_HOST_NETDEV_NAME |
+ ISCSI_HOST_INITIATOR_NAME,
+ .host_template = &iscsi_iser_sht,
+ .conndata_size = sizeof(struct iscsi_conn),
diff --git a/kernel_patches/backport/2.6.9_U4/iser_03_revert_1548271ece9e9312fd5feb41fd58773b56a71d39.patch b/kernel_patches/backport/2.6.9_U4/iser_03_revert_1548271ece9e9312fd5feb41fd58773b56a71d39.patch
new file mode 100644
index 0000000..d72eb5a
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U4/iser_03_revert_1548271ece9e9312fd5feb41fd58773b56a71d39.patch
@@ -0,0 +1,74 @@
+diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
+index bad8dac..7baac99 100644
+--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
++++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
+@@ -368,8 +368,7 @@ static struct iscsi_transport iscsi_iser_transport;
+ static struct iscsi_cls_session *
+ iscsi_iser_session_create(struct iscsi_transport *iscsit,
+ struct scsi_transport_template *scsit,
+- uint16_t cmds_max, uint16_t qdepth,
+- uint32_t initial_cmdsn, uint32_t *hostno)
++ uint32_t initial_cmdsn, uint32_t *hostno)
+ {
+ struct iscsi_cls_session *cls_session;
+ struct iscsi_session *session;
+@@ -380,13 +380,7 @@ iscsi_iser_session_create(struct iscsi_transport *iscsit,
+ struct iscsi_iser_cmd_task *iser_ctask;
+ struct iser_desc *desc;
+
+- /*
+- * we do not support setting can_queue cmd_per_lun from userspace yet
+- * because we preallocate so many resources
+- */
+ cls_session = iscsi_session_setup(iscsit, scsit,
+- ISCSI_DEF_XMIT_CMDS_MAX,
+- ISCSI_MAX_CMD_PER_LUN,
+ sizeof(struct iscsi_iser_cmd_task),
+ sizeof(struct iser_desc),
+ initial_cmdsn, &hn);
+@@ -550,7 +550,7 @@ static struct scsi_host_template iscsi_iser_sht = {
+ .name = "iSCSI Initiator over iSER, v." DRV_VER,
+ .queuecommand = iscsi_queuecommand,
+ .change_queue_depth = iscsi_change_queue_depth,
+- .can_queue = ISCSI_DEF_XMIT_CMDS_MAX - 1,
++ .can_queue = ISCSI_XMIT_CMDS_MAX - 1,
+ .sg_tablesize = ISCSI_ISER_SG_TABLESIZE,
+ .max_sectors = 1024,
+ .cmd_per_lun = ISCSI_MAX_CMD_PER_LUN,
+diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.h b/drivers/infiniband/ulp/iser/iscsi_iser.h
+index 1ee867b..671faff 100644
+--- a/drivers/infiniband/ulp/iser/iscsi_iser.h
++++ b/drivers/infiniband/ulp/iser/iscsi_iser.h
+@@ -105,7 +105,7 @@
+ #define ISER_MAX_TX_MISC_PDUS 6 /* NOOP_OUT(2), TEXT(1), *
+ * SCSI_TMFUNC(2), LOGOUT(1) */
+
+-#define ISER_QP_MAX_RECV_DTOS (ISCSI_DEF_XMIT_CMDS_MAX + \
++#define ISER_QP_MAX_RECV_DTOS (ISCSI_XMIT_CMDS_MAX + \
+ ISER_MAX_RX_MISC_PDUS + \
+ ISER_MAX_TX_MISC_PDUS)
+
+@@ -117,7 +117,7 @@
+
+ #define ISER_INFLIGHT_DATAOUTS 8
+
+-#define ISER_QP_MAX_REQ_DTOS (ISCSI_DEF_XMIT_CMDS_MAX * \
++#define ISER_QP_MAX_REQ_DTOS (ISCSI_XMIT_CMDS_MAX * \
+ (1 + ISER_INFLIGHT_DATAOUTS) + \
+ ISER_MAX_TX_MISC_PDUS + \
+ ISER_MAX_RX_MISC_PDUS)
+diff --git a/drivers/infiniband/ulp/iser/iser_verbs.c b/drivers/infiniband/ulp/iser/iser_verbs.c
+index 654a4dc..f3d8ba5 100644
+--- a/drivers/infiniband/ulp/iser/iser_verbs.c
++++ b/drivers/infiniband/ulp/iser/iser_verbs.c
+@@ -154,8 +154,8 @@ static int iser_create_ib_conn_res(struct iser_conn *ib_conn)
+ params.max_pages_per_fmr = ISCSI_ISER_SG_TABLESIZE + 1;
+ /* make the pool size twice the max number of SCSI commands *
+ * the ML is expected to queue, watermark for unmap at 50% */
+- params.pool_size = ISCSI_DEF_XMIT_CMDS_MAX * 2;
+- params.dirty_watermark = ISCSI_DEF_XMIT_CMDS_MAX;
++ params.pool_size = ISCSI_XMIT_CMDS_MAX * 2;
++ params.dirty_watermark = ISCSI_XMIT_CMDS_MAX;
+ params.cache = 0;
+ params.flush_function = NULL;
+ params.access = (IB_ACCESS_LOCAL_WRITE |
diff --git a/kernel_patches/backport/2.6.9_U4/iser_04_revert_77a23c21aaa723f6b0ffc4a701be8c8e5a32346d.patch b/kernel_patches/backport/2.6.9_U4/iser_04_revert_77a23c21aaa723f6b0ffc4a701be8c8e5a32346d.patch
new file mode 100644
index 0000000..26fa09c
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U4/iser_04_revert_77a23c21aaa723f6b0ffc4a701be8c8e5a32346d.patch
@@ -0,0 +1,38 @@
+diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
+index 8f7b859..5f82d6c 100644
+--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
++++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
+@@ -134,9 +134,18 @@ iscsi_iser_cmd_init(struct iscsi_cmd_task *ctask)
+ {
+ struct iscsi_iser_conn *iser_conn = ctask->conn->dd_data;
+ struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
++ struct scsi_cmnd *sc = ctask->sc;
+
+ iser_ctask->command_sent = 0;
+ iser_ctask->iser_conn = iser_conn;
++ if (sc->sc_data_direction == DMA_TO_DEVICE) {
++ BUG_ON(sc->request_bufflen == 0);
++
++ debug_scsi("cmd [itt %x total %d imm %d unsol_data %d\n",
++ ctask->itt, sc->request_bufflen, ctask->imm_count,
++ ctask->unsol_count);
++ }
++
+ iser_ctask_rdma_init(iser_ctask);
+ return 0;
+ }
+@@ -210,14 +219,6 @@ iscsi_iser_ctask_xmit(struct iscsi_conn *conn,
+ struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
+ int error = 0;
+
+- if (ctask->sc->sc_data_direction == DMA_TO_DEVICE) {
+- BUG_ON(ctask->sc->request_bufflen == 0);
+-
+- debug_scsi("cmd [itt %x total %d imm %d unsol_data %d\n",
+- ctask->itt, ctask->sc->request_bufflen,
+- ctask->imm_count, ctask->unsol_count);
+- }
+-
+ debug_scsi("ctask deq [cid %d itt 0x%x]\n",
+ conn->id, ctask->itt);
+
diff --git a/kernel_patches/backport/2.6.9_U4/iser_05_revert_b2c6416736b847b91950bd43cc5153e11a1f83ee.patch b/kernel_patches/backport/2.6.9_U4/iser_05_revert_b2c6416736b847b91950bd43cc5153e11a1f83ee.patch
new file mode 100644
index 0000000..417415f
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U4/iser_05_revert_b2c6416736b847b91950bd43cc5153e11a1f83ee.patch
@@ -0,0 +1,18 @@
+diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
+index 5f82d6c..3a67d76 100644
+--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
++++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
+@@ -574,11 +574,8 @@ static struct iscsi_transport iscsi_iser_transport = {
+ ISCSI_EXP_STATSN |
+ ISCSI_PERSISTENT_PORT |
+ ISCSI_PERSISTENT_ADDRESS |
+- ISCSI_TARGET_NAME | ISCSI_TPGT |
+- ISCSI_USERNAME | ISCSI_PASSWORD |
+- ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN |
+- ISCSI_FAST_ABORT | ISCSI_ABORT_TMO |
+- ISCSI_PING_TMO | ISCSI_RECV_TMO,
++ ISCSI_TARGET_NAME |
++ ISCSI_TPGT,
+ .host_param_mask = ISCSI_HOST_HWADDRESS |
+ ISCSI_HOST_INITIATOR_NAME,
+ .host_template = &iscsi_iser_sht,
diff --git a/kernel_patches/backport/2.6.9_U4/iser_06_revert_857ae0bdb72999936a28ce621e38e2e288c485da.patch b/kernel_patches/backport/2.6.9_U4/iser_06_revert_857ae0bdb72999936a28ce621e38e2e288c485da.patch
new file mode 100644
index 0000000..0b1a4c4
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U4/iser_06_revert_857ae0bdb72999936a28ce621e38e2e288c485da.patch
@@ -0,0 +1,16 @@
+diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
+index c5941fa..2f4f125 100644
+--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
++++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
+@@ -140,8 +140,8 @@ iscsi_iser_cmd_init(struct iscsi_cmd_task *ctask)
+ iser_ctask->iser_conn = iser_conn;
+ if (sc->sc_data_direction == DMA_TO_DEVICE) {
+- BUG_ON(sc->request_bufflen == 0);
++ BUG_ON(ctask->total_length == 0);
+
+ debug_scsi("cmd [itt %x total %d imm %d unsol_data %d\n",
+- ctask->itt, sc->request_bufflen, ctask->imm_count,
++ ctask->itt, ctask->total_length, ctask->imm_count,
+ ctask->unsol_count);
+ }
+
diff --git a/kernel_patches/backport/2.6.9_U4/iser_07_revert_8ad5781ae9702a8f95cfdf30967752e4297613ee.patch b/kernel_patches/backport/2.6.9_U4/iser_07_revert_8ad5781ae9702a8f95cfdf30967752e4297613ee.patch
new file mode 100644
index 0000000..f207af3
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U4/iser_07_revert_8ad5781ae9702a8f95cfdf30967752e4297613ee.patch
@@ -0,0 +1,14 @@
+diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
+index 2f4f125..940bf98 100644
+--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
++++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
+@@ -576,8 +576,7 @@ static struct iscsi_transport iscsi_iser_transport = {
+ ISCSI_PERSISTENT_ADDRESS |
+ ISCSI_TARGET_NAME |
+ ISCSI_TPGT,
+- .host_param_mask = ISCSI_HOST_HWADDRESS |
+- ISCSI_HOST_INITIATOR_NAME,
++ .host_param_mask = ISCSI_HOST_HWADDRESS,
+ .host_template = &iscsi_iser_sht,
+ .conndata_size = sizeof(struct iscsi_conn),
+ .max_lun = ISCSI_ISER_MAX_LUN,
diff --git a/kernel_patches/backport/2.6.9_U4/iser_08_revert_0801c242a33426fddc005c2f559a3d2fa6fca7eb.patch b/kernel_patches/backport/2.6.9_U4/iser_08_revert_0801c242a33426fddc005c2f559a3d2fa6fca7eb.patch
new file mode 100644
index 0000000..f9dceb1
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U4/iser_08_revert_0801c242a33426fddc005c2f559a3d2fa6fca7eb.patch
@@ -0,0 +1,22 @@
+diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
+index 940bf98..6a35eff 100644
+--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
++++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
+@@ -576,7 +576,6 @@ static struct iscsi_transport iscsi_iser_transport = {
+ ISCSI_PERSISTENT_ADDRESS |
+ ISCSI_TARGET_NAME |
+ ISCSI_TPGT,
+- .host_param_mask = ISCSI_HOST_HWADDRESS,
+ .host_template = &iscsi_iser_sht,
+ .conndata_size = sizeof(struct iscsi_conn),
+ .max_lun = ISCSI_ISER_MAX_LUN,
+@@ -593,9 +593,6 @@ static struct iscsi_transport iscsi_iser_transport = {
+ .get_session_param = iscsi_session_get_param,
+ .start_conn = iscsi_iser_conn_start,
+ .stop_conn = iscsi_conn_stop,
+- /* iscsi host params */
+- .get_host_param = iscsi_host_get_param,
+- .set_host_param = iscsi_host_set_param,
+ /* IO */
+ .send_pdu = iscsi_conn_send_pdu,
+ .get_stats = iscsi_iser_conn_get_stats,
diff --git a/kernel_patches/backport/2.6.9_U4/iser_09_fix_inclusion_order.patch b/kernel_patches/backport/2.6.9_U4/iser_09_fix_inclusion_order.patch
new file mode 100644
index 0000000..3c2a969
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U4/iser_09_fix_inclusion_order.patch
@@ -0,0 +1,13 @@
+--- linux-2.6.20-rc7-orig/drivers/infiniband/ulp/iser/iscsi_iser.c 2007-02-08 09:13:43.000000000 +0200
++++ linux-2.6.20-rc7/drivers/infiniband/ulp/iser/iscsi_iser.c 2007-02-08 09:14:31.000000000 +0200
+@@ -70,9 +70,8 @@
+ #include <scsi/scsi_tcq.h>
+ #include <scsi/scsi_host.h>
+ #include <scsi/scsi.h>
+-#include <scsi/scsi_transport_iscsi.h>
+-
+ #include "iscsi_iser.h"
++#include <scsi/scsi_transport_iscsi.h>
+
+ static unsigned int iscsi_max_lun = 512;
+ module_param_named(max_lun, iscsi_max_lun, uint, S_IRUGO);
diff --git a/kernel_patches/backport/2.6.9_U4/iser_10_fix_struct_scsi_host_template.patch b/kernel_patches/backport/2.6.9_U4/iser_10_fix_struct_scsi_host_template.patch
new file mode 100644
index 0000000..5b28ac4
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U4/iser_10_fix_struct_scsi_host_template.patch
@@ -0,0 +1,31 @@
+From 828e0ad429b92cf75781770ceb9ef7086f34fde2 Mon Sep 17 00:00:00 2001
+From: Doron Shoham <dorons at voltaire.com>
+Date: Thu, 3 Jul 2008 14:31:42 +0300
+Subject: [PATCH] fix_struct_scsi_host_template
+
+Signed-off-by: Doron Shoham <dorons at voltaire.com>
+---
+ drivers/infiniband/ulp/iser/iscsi_iser.c | 2 --
+ 1 files changed, 0 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
+index 9bf24c6..de1e783 100644
+--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
++++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
+@@ -542,13 +542,11 @@ static struct scsi_host_template iscsi_iser_sht = {
+ .module = THIS_MODULE,
+ .name = "iSCSI Initiator over iSER, v." DRV_VER,
+ .queuecommand = iscsi_queuecommand,
+- .change_queue_depth = iscsi_change_queue_depth,
+ .can_queue = ISCSI_XMIT_CMDS_MAX - 1,
+ .sg_tablesize = ISCSI_ISER_SG_TABLESIZE,
+ .max_sectors = 1024,
+ .cmd_per_lun = ISCSI_MAX_CMD_PER_LUN,
+ .eh_abort_handler = iscsi_eh_abort,
+- .eh_device_reset_handler= iscsi_eh_device_reset,
+ .eh_host_reset_handler = iscsi_eh_host_reset,
+ .use_clustering = DISABLE_CLUSTERING,
+ .proc_name = "iscsi_iser",
+--
+1.5.3.8
+
diff --git a/kernel_patches/backport/2.6.9_U4/iser_11_add_fmr_unalign_cnt.patch b/kernel_patches/backport/2.6.9_U4/iser_11_add_fmr_unalign_cnt.patch
new file mode 100644
index 0000000..ef2a2d6
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U4/iser_11_add_fmr_unalign_cnt.patch
@@ -0,0 +1,25 @@
+From 1255c8e5209ce19644e83e353c260f2eddc62cca Mon Sep 17 00:00:00 2001
+From: Doron Shoham <dorons at voltaire.com>
+Date: Thu, 3 Jul 2008 14:54:57 +0300
+Subject: [PATCH] add fmr_unalign_cnt to struct iscsi_conn
+
+Signed-off-by: Doron Shoham <dorons at voltaire.com>
+---
+ include/scsi/libiscsi.h | 1 +
+ 1 files changed, 1 insertions(+), 0 deletions(-)
+
+diff --git a/include/scsi/libiscsi.h b/include/scsi/libiscsi.h
+index ea0816d..182421f 100644
+--- a/include/scsi/libiscsi.h
++++ b/include/scsi/libiscsi.h
+@@ -197,6 +197,7 @@ struct iscsi_conn {
+
+ /* custom statistics */
+ uint32_t eh_abort_cnt;
++ uint32_t fmr_unalign_cnt;
+ };
+
+ struct iscsi_queue {
+--
+1.5.3.8
+
diff --git a/kernel_patches/backport/2.6.9_U4/iser_12_remove_hdr_max.patch b/kernel_patches/backport/2.6.9_U4/iser_12_remove_hdr_max.patch
new file mode 100644
index 0000000..c475001
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U4/iser_12_remove_hdr_max.patch
@@ -0,0 +1,25 @@
+From 97672ef8a29da5e16774d1de9527b2cc29415e36 Mon Sep 17 00:00:00 2001
+From: Doron Shoham <dorons at voltaire.com>
+Date: Thu, 3 Jul 2008 14:59:16 +0300
+Subject: [PATCH] remove hdr_max
+
+Signed-off-by: Doron Shoham <dorons at voltaire.com>
+---
+ drivers/infiniband/ulp/iser/iscsi_iser.c | 1 -
+ 1 files changed, 0 insertions(+), 1 deletions(-)
+
+diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
+index de1e783..6451e9d 100644
+--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
++++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
+@@ -394,7 +394,6 @@ iscsi_iser_session_create(struct iscsi_transport *iscsit,
+ ctask = session->cmds[i];
+ iser_ctask = ctask->dd_data;
+ ctask->hdr = (struct iscsi_cmd *)&iser_ctask->desc.iscsi_header;
+- ctask->hdr_max = sizeof(iser_ctask->desc.iscsi_header);
+ }
+
+ for (i = 0; i < session->mgmtpool_max; i++) {
+--
+1.5.3.8
+
diff --git a/kernel_patches/backport/2.6.9_U4/iser_13_fix_netlink_kernel_create.patch b/kernel_patches/backport/2.6.9_U4/iser_13_fix_netlink_kernel_create.patch
new file mode 100644
index 0000000..d47df44
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U4/iser_13_fix_netlink_kernel_create.patch
@@ -0,0 +1,26 @@
+From db61fe2c3062d8918e793ddc7e1a8cc3694bf620 Mon Sep 17 00:00:00 2001
+From: Doron Shoham <dorons at voltaire.com>
+Date: Sun, 6 Jul 2008 15:20:42 +0300
+Subject: [PATCH] fix netlink_kernel_create
+
+Signed-off-by: Doron Shoham <dorons at voltaire.com>
+---
+ drivers/scsi/scsi_transport_iscsi.c | 2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c
+index e969ef7..a2f4fb7 100644
+--- a/drivers/scsi/scsi_transport_iscsi.c
++++ b/drivers/scsi/scsi_transport_iscsi.c
+@@ -1401,7 +1401,7 @@ static __init int iscsi_transport_init(void)
+ if (err)
+ goto unregister_conn_class;
+
+- nls = netlink_kernel_create(NETLINK_ISCSI, 1, iscsi_if_rx, NULL,
++ nls = netlink_kernel_create(NULL, NETLINK_ISCSI, 1, iscsi_if_rx, NULL,
+ THIS_MODULE);
+ if (!nls) {
+ err = -ENOBUFS;
+--
+1.5.3.8
+
diff --git a/kernel_patches/backport/2.6.9_U4/iser_14_sync_attribute_container.c_from_ofed1.3.patch b/kernel_patches/backport/2.6.9_U4/iser_14_sync_attribute_container.c_from_ofed1.3.patch
new file mode 100644
index 0000000..e926007
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U4/iser_14_sync_attribute_container.c_from_ofed1.3.patch
@@ -0,0 +1,394 @@
+From bed65721f623039a119b5ff03c6c1fe44a1ccfb3 Mon Sep 17 00:00:00 2001
+From: Doron Shoham <dorons at voltaire.com>
+Date: Sun, 6 Jul 2008 15:26:20 +0300
+Subject: [PATCH] sync attribute_container.c from ofed1.3
+
+Signed-off-by: Doron Shoham <dorons at voltaire.com>
+---
+ drivers/base/attribute_container.c | 100 +++++++++++++++++------------------
+ drivers/base/transport_class.c | 21 ++++----
+ 2 files changed, 60 insertions(+), 61 deletions(-)
+
+diff --git a/drivers/base/attribute_container.c b/drivers/base/attribute_container.c
+index f57652d..7370d7c 100644
+--- a/drivers/base/attribute_container.c
++++ b/drivers/base/attribute_container.c
+@@ -27,21 +27,21 @@
+ struct internal_container {
+ struct klist_node node;
+ struct attribute_container *cont;
+- struct device classdev;
++ struct class_device classdev;
+ };
+
+ static void internal_container_klist_get(struct klist_node *n)
+ {
+ struct internal_container *ic =
+ container_of(n, struct internal_container, node);
+- get_device(&ic->classdev);
++ class_device_get(&ic->classdev);
+ }
+
+ static void internal_container_klist_put(struct klist_node *n)
+ {
+ struct internal_container *ic =
+ container_of(n, struct internal_container, node);
+- put_device(&ic->classdev);
++ class_device_put(&ic->classdev);
+ }
+
+
+@@ -53,7 +53,7 @@ static void internal_container_klist_put(struct klist_node *n)
+ * Returns the container associated with this classdev.
+ */
+ struct attribute_container *
+-attribute_container_classdev_to_container(struct device *classdev)
++attribute_container_classdev_to_container(struct class_device *classdev)
+ {
+ struct internal_container *ic =
+ container_of(classdev, struct internal_container, classdev);
+@@ -61,7 +61,7 @@ attribute_container_classdev_to_container(struct device *classdev)
+ }
+ EXPORT_SYMBOL_GPL(attribute_container_classdev_to_container);
+
+-static LIST_HEAD(attribute_container_list);
++static struct list_head attribute_container_list;
+
+ static DEFINE_MUTEX(attribute_container_mutex);
+
+@@ -110,11 +110,11 @@ attribute_container_unregister(struct attribute_container *cont)
+ EXPORT_SYMBOL_GPL(attribute_container_unregister);
+
+ /* private function used as class release */
+-static void attribute_container_release(struct device *classdev)
++static void attribute_container_release(struct class_device *classdev)
+ {
+ struct internal_container *ic
+ = container_of(classdev, struct internal_container, classdev);
+- struct device *dev = classdev->parent;
++ struct device *dev = classdev->dev;
+
+ kfree(ic);
+ put_device(dev);
+@@ -129,12 +129,12 @@ static void attribute_container_release(struct device *classdev)
+ * This function allocates storage for the class device(s) to be
+ * attached to dev (one for each matching attribute_container). If no
+ * fn is provided, the code will simply register the class device via
+- * device_add. If a function is provided, it is expected to add
++ * class_device_add. If a function is provided, it is expected to add
+ * the class device at the appropriate time. One of the things that
+ * might be necessary is to allocate and initialise the classdev and
+ * then add it a later time. To do this, call this routine for
+ * allocation and initialisation and then use
+- * attribute_container_device_trigger() to call device_add() on
++ * attribute_container_device_trigger() to call class_device_add() on
+ * it. Note: after this, the class device contains a reference to dev
+ * which is not relinquished until the release of the classdev.
+ */
+@@ -142,7 +142,7 @@ void
+ attribute_container_add_device(struct device *dev,
+ int (*fn)(struct attribute_container *,
+ struct device *,
+- struct device *))
++ struct class_device *))
+ {
+ struct attribute_container *cont;
+
+@@ -163,11 +163,11 @@ attribute_container_add_device(struct device *dev,
+ }
+
+ ic->cont = cont;
+- device_initialize(&ic->classdev);
+- ic->classdev.parent = get_device(dev);
++ class_device_initialize(&ic->classdev);
++ ic->classdev.dev = get_device(dev);
+ ic->classdev.class = cont->class;
+- cont->class->dev_release = attribute_container_release;
+- strcpy(ic->classdev.bus_id, dev->bus_id);
++ cont->class->release = attribute_container_release;
++ strcpy(ic->classdev.class_id, dev->bus_id);
+ if (fn)
+ fn(cont, dev, &ic->classdev);
+ else
+@@ -195,19 +195,20 @@ attribute_container_add_device(struct device *dev,
+ * @fn: A function to call to remove the device
+ *
+ * This routine triggers device removal. If fn is NULL, then it is
+- * simply done via device_unregister (note that if something
++ * simply done via class_device_unregister (note that if something
+ * still has a reference to the classdev, then the memory occupied
+ * will not be freed until the classdev is released). If you want a
+ * two phase release: remove from visibility and then delete the
+ * device, then you should use this routine with a fn that calls
+- * device_del() and then use attribute_container_device_trigger()
+- * to do the final put on the classdev.
++ * class_device_del() and then use
++ * attribute_container_device_trigger() to do the final put on the
++ * classdev.
+ */
+ void
+ attribute_container_remove_device(struct device *dev,
+ void (*fn)(struct attribute_container *,
+ struct device *,
+- struct device *))
++ struct class_device *))
+ {
+ struct attribute_container *cont;
+
+@@ -223,14 +224,14 @@ attribute_container_remove_device(struct device *dev,
+ continue;
+
+ klist_for_each_entry(ic, &cont->containers, node, &iter) {
+- if (dev != ic->classdev.parent)
++ if (dev != ic->classdev.dev)
+ continue;
+ klist_del(&ic->node);
+ if (fn)
+ fn(cont, dev, &ic->classdev);
+ else {
+ attribute_container_remove_attrs(&ic->classdev);
+- device_unregister(&ic->classdev);
++ class_device_unregister(&ic->classdev);
+ }
+ }
+ }
+@@ -251,7 +252,7 @@ void
+ attribute_container_device_trigger(struct device *dev,
+ int (*fn)(struct attribute_container *,
+ struct device *,
+- struct device *))
++ struct class_device *))
+ {
+ struct attribute_container *cont;
+
+@@ -269,7 +270,7 @@ attribute_container_device_trigger(struct device *dev,
+ }
+
+ klist_for_each_entry(ic, &cont->containers, node, &iter) {
+- if (dev == ic->classdev.parent)
++ if (dev == ic->classdev.dev)
+ fn(cont, dev, &ic->classdev);
+ }
+ }
+@@ -312,23 +313,18 @@ attribute_container_trigger(struct device *dev,
+ * attributes listed in the container
+ */
+ int
+-attribute_container_add_attrs(struct device *classdev)
++attribute_container_add_attrs(struct class_device *classdev)
+ {
+ struct attribute_container *cont =
+ attribute_container_classdev_to_container(classdev);
+- struct device_attribute **attrs = cont->attrs;
++ struct class_device_attribute **attrs = cont->attrs;
+ int i, error;
+
+- BUG_ON(attrs && cont->grp);
+-
+- if (!attrs && !cont->grp)
++ if (!attrs)
+ return 0;
+
+- if (cont->grp)
+- return sysfs_create_group(&classdev->kobj, cont->grp);
+-
+ for (i = 0; attrs[i]; i++) {
+- error = device_create_file(classdev, attrs[i]);
++ error = class_device_create_file(classdev, attrs[i]);
+ if (error)
+ return error;
+ }
+@@ -337,18 +333,18 @@ attribute_container_add_attrs(struct device *classdev)
+ }
+
+ /**
+- * attribute_container_add_class_device - same function as device_add
++ * attribute_container_add_class_device - same function as class_device_add
+ *
+ * @classdev: the class device to add
+ *
+- * This performs essentially the same function as device_add except for
++ * This performs essentially the same function as class_device_add except for
+ * attribute containers, namely add the classdev to the system and then
+ * create the attribute files
+ */
+ int
+-attribute_container_add_class_device(struct device *classdev)
++attribute_container_add_class_device(struct class_device *classdev)
+ {
+- int error = device_add(classdev);
++ int error = class_device_add(classdev);
+ if (error)
+ return error;
+ return attribute_container_add_attrs(classdev);
+@@ -363,7 +359,7 @@ attribute_container_add_class_device(struct device *classdev)
+ int
+ attribute_container_add_class_device_adapter(struct attribute_container *cont,
+ struct device *dev,
+- struct device *classdev)
++ struct class_device *classdev)
+ {
+ return attribute_container_add_class_device(classdev);
+ }
+@@ -375,23 +371,18 @@ attribute_container_add_class_device_adapter(struct attribute_container *cont,
+ *
+ */
+ void
+-attribute_container_remove_attrs(struct device *classdev)
++attribute_container_remove_attrs(struct class_device *classdev)
+ {
+ struct attribute_container *cont =
+ attribute_container_classdev_to_container(classdev);
+- struct device_attribute **attrs = cont->attrs;
++ struct class_device_attribute **attrs = cont->attrs;
+ int i;
+
+- if (!attrs && !cont->grp)
++ if (!attrs)
+ return;
+
+- if (cont->grp) {
+- sysfs_remove_group(&classdev->kobj, cont->grp);
+- return ;
+- }
+-
+ for (i = 0; attrs[i]; i++)
+- device_remove_file(classdev, attrs[i]);
++ class_device_remove_file(classdev, attrs[i]);
+ }
+
+ /**
+@@ -400,13 +391,13 @@ attribute_container_remove_attrs(struct device *classdev)
+ * @classdev: the class device
+ *
+ * This function simply removes all the attribute files and then calls
+- * device_del.
++ * class_device_del.
+ */
+ void
+-attribute_container_class_device_del(struct device *classdev)
++attribute_container_class_device_del(struct class_device *classdev)
+ {
+ attribute_container_remove_attrs(classdev);
+- device_del(classdev);
++ class_device_del(classdev);
+ }
+
+ /**
+@@ -418,16 +409,16 @@ attribute_container_class_device_del(struct device *classdev)
+ * Looks up the device in the container's list of class devices and returns
+ * the corresponding class_device.
+ */
+-struct device *
++struct class_device *
+ attribute_container_find_class_device(struct attribute_container *cont,
+ struct device *dev)
+ {
+- struct device *cdev = NULL;
++ struct class_device *cdev = NULL;
+ struct internal_container *ic;
+ struct klist_iter iter;
+
+ klist_for_each_entry(ic, &cont->containers, node, &iter) {
+- if (ic->classdev.parent == dev) {
++ if (ic->classdev.dev == dev) {
+ cdev = &ic->classdev;
+ /* FIXME: must exit iterator then break */
+ klist_iter_exit(&iter);
+@@ -438,3 +429,10 @@ attribute_container_find_class_device(struct attribute_container *cont,
+ return cdev;
+ }
+ EXPORT_SYMBOL_GPL(attribute_container_find_class_device);
++
++int __init
++attribute_container_init(void)
++{
++ INIT_LIST_HEAD(&attribute_container_list);
++ return 0;
++}
+diff --git a/drivers/base/transport_class.c b/drivers/base/transport_class.c
+index 84997ef..f25e7c6 100644
+--- a/drivers/base/transport_class.c
++++ b/drivers/base/transport_class.c
+@@ -66,7 +66,7 @@ EXPORT_SYMBOL_GPL(transport_class_unregister);
+
+ static int anon_transport_dummy_function(struct transport_container *tc,
+ struct device *dev,
+- struct device *cdev)
++ struct class_device *cdev)
+ {
+ /* do nothing */
+ return 0;
+@@ -108,14 +108,13 @@ EXPORT_SYMBOL_GPL(anon_transport_class_register);
+ */
+ void anon_transport_class_unregister(struct anon_transport_class *atc)
+ {
+- if (unlikely(attribute_container_unregister(&atc->container)))
+- BUG();
++ attribute_container_unregister(&atc->container);
+ }
+ EXPORT_SYMBOL_GPL(anon_transport_class_unregister);
+
+ static int transport_setup_classdev(struct attribute_container *cont,
+ struct device *dev,
+- struct device *classdev)
++ struct class_device *classdev)
+ {
+ struct transport_class *tclass = class_to_transport_class(cont->class);
+ struct transport_container *tcont = attribute_container_to_transport_container(cont);
+@@ -127,7 +126,9 @@ static int transport_setup_classdev(struct attribute_container *cont,
+ }
+
+ /**
+- * transport_setup_device - declare a new dev for transport class association but don't make it visible yet.
++ * transport_setup_device - declare a new dev for transport class association
++ * but don't make it visible yet.
++ *
+ * @dev: the generic device representing the entity being added
+ *
+ * Usually, dev represents some component in the HBA system (either
+@@ -149,7 +150,7 @@ EXPORT_SYMBOL_GPL(transport_setup_device);
+
+ static int transport_add_class_device(struct attribute_container *cont,
+ struct device *dev,
+- struct device *classdev)
++ struct class_device *classdev)
+ {
+ int error = attribute_container_add_class_device(classdev);
+ struct transport_container *tcont =
+@@ -181,7 +182,7 @@ EXPORT_SYMBOL_GPL(transport_add_device);
+
+ static int transport_configure(struct attribute_container *cont,
+ struct device *dev,
+- struct device *cdev)
++ struct class_device *cdev)
+ {
+ struct transport_class *tclass = class_to_transport_class(cont->class);
+ struct transport_container *tcont = attribute_container_to_transport_container(cont);
+@@ -212,7 +213,7 @@ EXPORT_SYMBOL_GPL(transport_configure_device);
+
+ static int transport_remove_classdev(struct attribute_container *cont,
+ struct device *dev,
+- struct device *classdev)
++ struct class_device *classdev)
+ {
+ struct transport_container *tcont =
+ attribute_container_to_transport_container(cont);
+@@ -251,12 +252,12 @@ EXPORT_SYMBOL_GPL(transport_remove_device);
+
+ static void transport_destroy_classdev(struct attribute_container *cont,
+ struct device *dev,
+- struct device *classdev)
++ struct class_device *classdev)
+ {
+ struct transport_class *tclass = class_to_transport_class(cont->class);
+
+ if (tclass->remove != anon_transport_dummy_function)
+- put_device(classdev);
++ class_device_put(classdev);
+ }
+
+
+--
+1.5.3.8
+
diff --git a/kernel_patches/backport/2.6.9_U4/iser_15_fix_iscsi_free_mgmt_task.patch b/kernel_patches/backport/2.6.9_U4/iser_15_fix_iscsi_free_mgmt_task.patch
new file mode 100644
index 0000000..7a3a3ea
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U4/iser_15_fix_iscsi_free_mgmt_task.patch
@@ -0,0 +1,28 @@
+From 5a9fd2300982aca58f1306bdb98cab878998a607 Mon Sep 17 00:00:00 2001
+From: Doron Shoham <dorons at voltaire.com>
+Date: Sun, 6 Jul 2008 15:53:59 +0300
+Subject: [PATCH] fix iscsi_free_mgmt_task
+
+Signed-off-by: Doron Shoham <dorons at voltaire.com>
+---
+ drivers/infiniband/ulp/iser/iser_initiator.c | 4 +++-
+ 1 files changed, 3 insertions(+), 1 deletions(-)
+
+diff --git a/drivers/infiniband/ulp/iser/iser_initiator.c b/drivers/infiniband/ulp/iser/iser_initiator.c
+index 4e20c8b..e7f2399 100644
+--- a/drivers/infiniband/ulp/iser/iser_initiator.c
++++ b/drivers/infiniband/ulp/iser/iser_initiator.c
+@@ -627,7 +627,9 @@ void iser_snd_completion(struct iser_desc *tx_desc)
+ struct iscsi_session *session = conn->session;
+
+ spin_lock(&conn->session->lock);
+- iscsi_free_mgmt_task(conn, mtask);
++ list_del(&mtask->running);
++ __kfifo_put(session->mgmtpool.queue, (void*)&mtask,
++ sizeof(void*));
+ spin_unlock(&session->lock);
+ }
+ }
+--
+1.5.3.8
+
diff --git a/kernel_patches/backport/2.6.9_U5/iscsi_01_sync_kernel_code_with_ofed_1_2_5.patch b/kernel_patches/backport/2.6.9_U5/iscsi_01_sync_kernel_code_with_ofed_1_2_5.patch
new file mode 100644
index 0000000..aeacec6
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U5/iscsi_01_sync_kernel_code_with_ofed_1_2_5.patch
@@ -0,0 +1,7858 @@
+From a2ee7540d79a7a287205fd9b75311a0cfeb64e38 Mon Sep 17 00:00:00 2001
+From: Doron Shoham <dorons at voltaire.com>
+Date: Thu, 3 Jul 2008 13:12:22 +0300
+Subject: [PATCH] sync_kernel_code_with_ofed_1_2_5
+
+Signed-off-by: Doron Shoham <dorons at voltaire.com>
+---
+ drivers/scsi/iscsi_tcp.c | 2313 +++++++++++++++++++----------------
+ drivers/scsi/iscsi_tcp.h | 131 ++-
+ drivers/scsi/libiscsi.c | 1800 +++++++++------------------
+ drivers/scsi/scsi_transport_iscsi.c | 869 +++++---------
+ include/scsi/iscsi_if.h | 54 -
+ include/scsi/iscsi_proto.h | 22 +-
+ include/scsi/libiscsi.h | 188 +---
+ include/scsi/scsi_transport_iscsi.h | 65 +-
+ 8 files changed, 2301 insertions(+), 3141 deletions(-)
+
+diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c
+index 72b9b2a..c9a3abf 100644
+--- a/drivers/scsi/iscsi_tcp.c
++++ b/drivers/scsi/iscsi_tcp.c
+@@ -29,15 +29,14 @@
+ #include <linux/types.h>
+ #include <linux/list.h>
+ #include <linux/inet.h>
+-#include <linux/file.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_device.h>
+ #include <scsi/scsi_host.h>
+ #include <scsi/scsi.h>
+ #include <scsi/scsi_transport_iscsi.h>
+@@ -48,7 +47,7 @@ MODULE_AUTHOR("Dmitry Yusupov <dmitry_yus at yahoo.com>, "
+ "Alex Aizman <itn780 at yahoo.com>");
+ MODULE_DESCRIPTION("iSCSI/TCP data-path");
+ MODULE_LICENSE("GPL");
+-#undef DEBUG_TCP
++/* #define DEBUG_TCP */
+ #define DEBUG_ASSERT
+
+ #ifdef DEBUG_TCP
+@@ -67,429 +66,118 @@ MODULE_LICENSE("GPL");
+ static unsigned int iscsi_max_lun = 512;
+ module_param_named(max_lun, iscsi_max_lun, uint, S_IRUGO);
+
+-static int iscsi_tcp_hdr_recv_done(struct iscsi_tcp_conn *tcp_conn,
+- struct iscsi_segment *segment);
+-
+-/*
+- * Scatterlist handling: inside the iscsi_segment, we
+- * remember an index into the scatterlist, and set data/size
+- * to the current scatterlist entry. For highmem pages, we
+- * kmap as needed.
+- *
+- * Note that the page is unmapped when we return from
+- * TCP's data_ready handler, so we may end up mapping and
+- * unmapping the same page repeatedly. The whole reason
+- * for this is that we shouldn't keep the page mapped
+- * outside the softirq.
+- */
+-
+-/**
+- * iscsi_tcp_segment_init_sg - init indicated scatterlist entry
+- * @segment: the buffer object
+- * @sg: scatterlist
+- * @offset: byte offset into that sg entry
+- *
+- * This function sets up the segment so that subsequent
+- * data is copied to the indicated sg entry, at the given
+- * offset.
+- */
+ static inline void
+-iscsi_tcp_segment_init_sg(struct iscsi_segment *segment,
+- struct scatterlist *sg, unsigned int offset)
++iscsi_buf_init_iov(struct iscsi_buf *ibuf, char *vbuf, int size)
+ {
+- segment->sg = sg;
+- segment->sg_offset = offset;
+- segment->size = min(sg->length - offset,
+- segment->total_size - segment->total_copied);
+- segment->data = NULL;
++ 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;
+ }
+
+-/**
+- * iscsi_tcp_segment_map - map the current S/G page
+- * @segment: iscsi_segment
+- * @recv: 1 if called from recv path
+- *
+- * We only need to possibly kmap data if scatter lists are being used,
+- * because the iscsi passthrough and internal IO paths will never use high
+- * mem pages.
+- */
+ static inline void
+-iscsi_tcp_segment_map(struct iscsi_segment *segment, int recv)
++iscsi_buf_init_sg(struct iscsi_buf *ibuf, struct scatterlist *sg)
+ {
+- struct scatterlist *sg;
+-
+- if (segment->data != NULL || !segment->sg)
+- return;
+-
+- sg = segment->sg;
+- BUG_ON(segment->sg_mapped);
+- BUG_ON(sg->length == 0);
+-
++ ibuf->sg.page = sg->page;
++ ibuf->sg.offset = sg->offset;
++ ibuf->sg.length = sg->length;
+ /*
+- * If the page count is greater than one it is ok to send
+- * to the network layer's zero copy send path. If not we
+- * have to go the slow sendmsg path. We always map for the
+- * recv path.
++ * Fastpath: sg element fits into single page
+ */
+- if (page_count(sg_page(sg)) >= 1 && !recv)
+- return;
+-
+- debug_tcp("iscsi_tcp_segment_map %s %p\n", recv ? "recv" : "xmit",
+- segment);
+- segment->sg_mapped = kmap_atomic(sg_page(sg), KM_SOFTIRQ0);
+- segment->data = segment->sg_mapped + sg->offset + segment->sg_offset;
++ if (sg->length + sg->offset <= PAGE_SIZE && !PageSlab(sg->page))
++ ibuf->use_sendmsg = 0;
++ else
++ ibuf->use_sendmsg = 1;
++ ibuf->sent = 0;
+ }
+
+-static inline void
+-iscsi_tcp_segment_unmap(struct iscsi_segment *segment)
++static inline int
++iscsi_buf_left(struct iscsi_buf *ibuf)
+ {
+- debug_tcp("iscsi_tcp_segment_unmap %p\n", segment);
++ int rc;
+
+- if (segment->sg_mapped) {
+- debug_tcp("iscsi_tcp_segment_unmap valid\n");
+- kunmap_atomic(segment->sg_mapped, KM_SOFTIRQ0);
+- segment->sg_mapped = NULL;
+- segment->data = NULL;
+- }
++ rc = ibuf->sg.length - ibuf->sent;
++ BUG_ON(rc < 0);
++ return rc;
+ }
+
+-/*
+- * Splice the digest buffer into the buffer
+- */
+ static inline void
+-iscsi_tcp_segment_splice_digest(struct iscsi_segment *segment, void *digest)
++iscsi_hdr_digest(struct iscsi_conn *conn, struct iscsi_buf *buf,
++ u8* crc)
+ {
+- segment->data = digest;
+- segment->digest_len = ISCSI_DIGEST_SIZE;
+- segment->total_size += ISCSI_DIGEST_SIZE;
+- segment->size = ISCSI_DIGEST_SIZE;
+- segment->copied = 0;
+- segment->sg = NULL;
+- segment->hash = NULL;
++ 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;
+ }
+
+-/**
+- * iscsi_tcp_segment_done - check whether the segment is complete
+- * @segment: iscsi segment to check
+- * @recv: set to one of this is called from the recv path
+- * @copied: number of bytes copied
+- *
+- * Check if we're done receiving this segment. If the receive
+- * buffer is full but we expect more data, move on to the
+- * next entry in the scatterlist.
+- *
+- * If the amount of data we received isn't a multiple of 4,
+- * we will transparently receive the pad bytes, too.
+- *
+- * This function must be re-entrant.
+- */
+ static inline int
+-iscsi_tcp_segment_done(struct iscsi_segment *segment, int recv, unsigned copied)
++iscsi_hdr_extract(struct iscsi_tcp_conn *tcp_conn)
+ {
+- static unsigned char padbuf[ISCSI_PAD_LEN];
+- struct scatterlist sg;
+- unsigned int pad;
++ struct sk_buff *skb = tcp_conn->in.skb;
++
++ tcp_conn->in.zero_copy_hdr = 0;
+
+- debug_tcp("copied %u %u size %u %s\n", segment->copied, copied,
+- segment->size, recv ? "recv" : "xmit");
+- if (segment->hash && copied) {
++ if (tcp_conn->in.copy >= tcp_conn->hdr_size &&
++ tcp_conn->in_progress == IN_PROGRESS_WAIT_HEADER) {
+ /*
+- * If a segment is kmapd we must unmap it before sending
+- * to the crypto layer since that will try to kmap it again.
++ * Zero-copy PDU Header: using connection context
++ * to store header pointer.
+ */
+- iscsi_tcp_segment_unmap(segment);
+-
+- if (!segment->data) {
+- sg_init_table(&sg, 1);
+- sg_set_page(&sg, sg_page(segment->sg), copied,
+- segment->copied + segment->sg_offset +
+- segment->sg->offset);
+- } else
+- sg_init_one(&sg, segment->data + segment->copied,
+- copied);
+- crypto_hash_update(segment->hash, &sg, copied);
+- }
+-
+- segment->copied += copied;
+- if (segment->copied < segment->size) {
+- iscsi_tcp_segment_map(segment, recv);
+- return 0;
+- }
+-
+- segment->total_copied += segment->copied;
+- segment->copied = 0;
+- segment->size = 0;
+-
+- /* Unmap the current scatterlist page, if there is one. */
+- iscsi_tcp_segment_unmap(segment);
+-
+- /* Do we have more scatterlist entries? */
+- debug_tcp("total copied %u total size %u\n", segment->total_copied,
+- segment->total_size);
+- if (segment->total_copied < segment->total_size) {
+- /* Proceed to the next entry in the scatterlist. */
+- iscsi_tcp_segment_init_sg(segment, sg_next(segment->sg),
+- 0);
+- iscsi_tcp_segment_map(segment, recv);
+- BUG_ON(segment->size == 0);
+- return 0;
+- }
+-
+- /* Do we need to handle padding? */
+- pad = iscsi_padding(segment->total_copied);
+- if (pad != 0) {
+- debug_tcp("consume %d pad bytes\n", pad);
+- segment->total_size += pad;
+- segment->size = pad;
+- segment->data = padbuf;
+- return 0;
+- }
+-
+- /*
+- * Set us up for transferring the data digest. hdr digest
+- * is completely handled in hdr done function.
+- */
+- if (segment->hash) {
+- crypto_hash_final(segment->hash, segment->digest);
+- iscsi_tcp_segment_splice_digest(segment,
+- recv ? segment->recv_digest : segment->digest);
+- return 0;
+- }
+-
+- return 1;
+-}
+-
+-/**
+- * iscsi_tcp_xmit_segment - transmit segment
+- * @tcp_conn: the iSCSI TCP connection
+- * @segment: the buffer to transmnit
+- *
+- * This function transmits as much of the buffer as
+- * the network layer will accept, and returns the number of
+- * bytes transmitted.
+- *
+- * If CRC hashing is enabled, the function will compute the
+- * hash as it goes. When the entire segment has been transmitted,
+- * it will retrieve the hash value and send it as well.
+- */
+-static int
+-iscsi_tcp_xmit_segment(struct iscsi_tcp_conn *tcp_conn,
+- struct iscsi_segment *segment)
+-{
+- struct socket *sk = tcp_conn->sock;
+- unsigned int copied = 0;
+- int r = 0;
+-
+- while (!iscsi_tcp_segment_done(segment, 0, r)) {
+- struct scatterlist *sg;
+- unsigned int offset, copy;
+- int flags = 0;
+-
+- r = 0;
+- offset = segment->copied;
+- copy = segment->size - offset;
+-
+- if (segment->total_copied + segment->size < segment->total_size)
+- flags |= MSG_MORE;
+-
+- /* Use sendpage if we can; else fall back to sendmsg */
+- if (!segment->data) {
+- sg = segment->sg;
+- offset += segment->sg_offset + sg->offset;
+- r = tcp_conn->sendpage(sk, sg_page(sg), offset, copy,
+- flags);
++ 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 {
+- struct msghdr msg = { .msg_flags = flags };
+- struct kvec iov = {
+- .iov_base = segment->data + offset,
+- .iov_len = copy
+- };
+-
+- r = kernel_sendmsg(sk, &msg, &iov, 1, copy);
+- }
+-
+- if (r < 0) {
+- iscsi_tcp_segment_unmap(segment);
+- if (copied || r == -EAGAIN)
+- break;
+- return r;
++ /* 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;
+ }
+- copied += r;
+- }
+- return copied;
+-}
+-
+-/**
+- * iscsi_tcp_segment_recv - copy data to segment
+- * @tcp_conn: the iSCSI TCP connection
+- * @segment: the buffer to copy to
+- * @ptr: data pointer
+- * @len: amount of data available
+- *
+- * This function copies up to @len bytes to the
+- * given buffer, and returns the number of bytes
+- * consumed, which can actually be less than @len.
+- *
+- * If hash digest is enabled, the function will update the
+- * hash while copying.
+- * Combining these two operations doesn't buy us a lot (yet),
+- * but in the future we could implement combined copy+crc,
+- * just way we do for network layer checksums.
+- */
+-static int
+-iscsi_tcp_segment_recv(struct iscsi_tcp_conn *tcp_conn,
+- struct iscsi_segment *segment, const void *ptr,
+- unsigned int len)
+-{
+- unsigned int copy = 0, copied = 0;
+-
+- while (!iscsi_tcp_segment_done(segment, 1, copy)) {
+- if (copied == len) {
+- debug_tcp("iscsi_tcp_segment_recv copied %d bytes\n",
+- len);
+- break;
+- }
+-
+- copy = min(len - copied, segment->size - segment->copied);
+- debug_tcp("iscsi_tcp_segment_recv copying %d\n", copy);
+- memcpy(segment->data + segment->copied, ptr + copied, copy);
+- copied += copy;
+- }
+- return copied;
+-}
+-
+-static inline void
+-iscsi_tcp_dgst_header(struct hash_desc *hash, const void *hdr, size_t hdrlen,
+- unsigned char digest[ISCSI_DIGEST_SIZE])
+-{
+- struct scatterlist sg;
++ tcp_conn->in.offset += tcp_conn->hdr_size;
++ tcp_conn->in.copy -= tcp_conn->hdr_size;
++ } else {
++ int hdr_remains;
++ int copylen;
+
+- sg_init_one(&sg, hdr, hdrlen);
+- crypto_hash_digest(hash, &sg, hdrlen, digest);
+-}
++ /*
++ * PDU header scattered across SKB's,
++ * copying it... This'll happen quite rarely.
++ */
+
+-static inline int
+-iscsi_tcp_dgst_verify(struct iscsi_tcp_conn *tcp_conn,
+- struct iscsi_segment *segment)
+-{
+- if (!segment->digest_len)
+- return 1;
++ if (tcp_conn->in_progress == IN_PROGRESS_WAIT_HEADER)
++ tcp_conn->in.hdr_offset = 0;
+
+- if (memcmp(segment->recv_digest, segment->digest,
+- segment->digest_len)) {
+- debug_scsi("digest mismatch\n");
+- return 0;
+- }
++ hdr_remains = tcp_conn->hdr_size - tcp_conn->in.hdr_offset;
++ BUG_ON(hdr_remains <= 0);
+
+- return 1;
+-}
++ 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);
+
+-/*
+- * Helper function to set up segment buffer
+- */
+-static inline void
+-__iscsi_segment_init(struct iscsi_segment *segment, size_t size,
+- iscsi_segment_done_fn_t *done, struct hash_desc *hash)
+-{
+- memset(segment, 0, sizeof(*segment));
+- segment->total_size = size;
+- segment->done = done;
+-
+- if (hash) {
+- segment->hash = hash;
+- crypto_hash_init(hash);
+- }
+-}
++ 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);
+
+-static inline void
+-iscsi_segment_init_linear(struct iscsi_segment *segment, void *data,
+- size_t size, iscsi_segment_done_fn_t *done,
+- struct hash_desc *hash)
+-{
+- __iscsi_segment_init(segment, size, done, hash);
+- segment->data = data;
+- segment->size = size;
+-}
+-
+-static inline int
+-iscsi_segment_seek_sg(struct iscsi_segment *segment,
+- struct scatterlist *sg_list, unsigned int sg_count,
+- unsigned int offset, size_t size,
+- iscsi_segment_done_fn_t *done, struct hash_desc *hash)
+-{
+- struct scatterlist *sg;
+- unsigned int i;
+-
+- debug_scsi("iscsi_segment_seek_sg offset %u size %llu\n",
+- offset, size);
+- __iscsi_segment_init(segment, size, done, hash);
+- for_each_sg(sg_list, sg, sg_count, i) {
+- debug_scsi("sg %d, len %u offset %u\n", i, sg->length,
+- sg->offset);
+- if (offset < sg->length) {
+- iscsi_tcp_segment_init_sg(segment, sg, offset);
+- return 0;
++ 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;
+ }
+- offset -= sg->length;
++ tcp_conn->in.hdr = &tcp_conn->hdr;
++ tcp_conn->discontiguous_hdr_cnt++;
++ tcp_conn->in_progress = IN_PROGRESS_WAIT_HEADER;
+ }
+
+- return ISCSI_ERR_DATA_OFFSET;
+-}
+-
+-/**
+- * iscsi_tcp_hdr_recv_prep - prep segment for hdr reception
+- * @tcp_conn: iscsi connection to prep for
+- *
+- * This function always passes NULL for the hash argument, because when this
+- * function is called we do not yet know the final size of the header and want
+- * to delay the digest processing until we know that.
+- */
+-static void
+-iscsi_tcp_hdr_recv_prep(struct iscsi_tcp_conn *tcp_conn)
+-{
+- debug_tcp("iscsi_tcp_hdr_recv_prep(%p%s)\n", tcp_conn,
+- tcp_conn->iscsi_conn->hdrdgst_en ? ", digest enabled" : "");
+- iscsi_segment_init_linear(&tcp_conn->in.segment,
+- tcp_conn->in.hdr_buf, sizeof(struct iscsi_hdr),
+- iscsi_tcp_hdr_recv_done, NULL);
+-}
+-
+-/*
+- * Handle incoming reply to any other type of command
+- */
+-static int
+-iscsi_tcp_data_recv_done(struct iscsi_tcp_conn *tcp_conn,
+- struct iscsi_segment *segment)
+-{
+- struct iscsi_conn *conn = tcp_conn->iscsi_conn;
+- int rc = 0;
+-
+- if (!iscsi_tcp_dgst_verify(tcp_conn, segment))
+- return ISCSI_ERR_DATA_DGST;
+-
+- rc = iscsi_complete_pdu(conn, tcp_conn->in.hdr,
+- conn->data, tcp_conn->in.datalen);
+- if (rc)
+- return rc;
+-
+- iscsi_tcp_hdr_recv_prep(tcp_conn);
+ return 0;
+ }
+
+-static void
+-iscsi_tcp_data_recv_prep(struct iscsi_tcp_conn *tcp_conn)
+-{
+- struct iscsi_conn *conn = tcp_conn->iscsi_conn;
+- struct hash_desc *rx_hash = NULL;
+-
+- if (conn->datadgst_en)
+- rx_hash = &tcp_conn->rx_hash;
+-
+- iscsi_segment_init_linear(&tcp_conn->in.segment,
+- conn->data, tcp_conn->in.datalen,
+- iscsi_tcp_data_recv_done, rx_hash);
+-}
+-
+ /*
+ * must be called with session lock
+ */
+@@ -498,6 +186,7 @@ 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*))) {
+@@ -506,12 +195,12 @@ iscsi_tcp_cleanup_ctask(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
+ debug_scsi("iscsi_tcp_cleanup_ctask pending r2t dropped\n");
+ }
+
+- r2t = tcp_ctask->r2t;
+- if (r2t != NULL) {
+- __kfifo_put(tcp_ctask->r2tpool.queue, (void*)&r2t,
+- sizeof(void*));
+- tcp_ctask->r2t = NULL;
+- }
++ sc = ctask->sc;
++ if (unlikely(!sc))
++ return;
++
++ tcp_ctask->xmstate = XMSTATE_IDLE;
++ tcp_ctask->r2t = NULL;
+ }
+
+ /**
+@@ -522,49 +211,52 @@ iscsi_tcp_cleanup_ctask(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
+ 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;
+- struct scsi_cmnd *sc = ctask->sc;
+ int datasn = be32_to_cpu(rhdr->datasn);
+- unsigned total_in_length = scsi_in(sc)->length;
+
+- iscsi_update_cmdsn(session, (struct iscsi_nopin*)rhdr);
++ 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 (tcp_ctask->exp_datasn != datasn) {
+- debug_tcp("%s: ctask->exp_datasn(%d) != rhdr->datasn(%d)\n",
+- __FUNCTION__, tcp_ctask->exp_datasn, datasn);
++ if (ctask->datasn != datasn)
+ return ISCSI_ERR_DATASN;
+- }
+
+- tcp_ctask->exp_datasn++;
++ ctask->datasn++;
+
+ tcp_ctask->data_offset = be32_to_cpu(rhdr->offset);
+- if (tcp_ctask->data_offset + tcp_conn->in.datalen > total_in_length) {
+- debug_tcp("%s: data_offset(%d) + data_len(%d) > total_length_in(%d)\n",
+- __FUNCTION__, tcp_ctask->data_offset,
+- tcp_conn->in.datalen, total_in_length);
++ if (tcp_ctask->data_offset + tcp_conn->in.datalen > ctask->total_length)
+ return ISCSI_ERR_DATA_OFFSET;
+- }
+
+ if (rhdr->flags & ISCSI_FLAG_DATA_STATUS) {
+- sc->result = (DID_OK << 16) | rhdr->cmd_status;
++ struct scsi_cmnd *sc = ctask->sc;
++
+ conn->exp_statsn = be32_to_cpu(rhdr->statsn) + 1;
+- if (rhdr->flags & (ISCSI_FLAG_DATA_UNDERFLOW |
+- ISCSI_FLAG_DATA_OVERFLOW)) {
++ if (rhdr->flags & ISCSI_FLAG_DATA_UNDERFLOW) {
+ int res_count = be32_to_cpu(rhdr->residual_count);
+
+ if (res_count > 0 &&
+- (rhdr->flags & ISCSI_FLAG_CMD_OVERFLOW ||
+- res_count <= total_in_length))
+- scsi_in(sc)->resid = res_count;
+- else
++ 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++;
+@@ -588,6 +280,7 @@ 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));
+@@ -611,6 +304,43 @@ iscsi_solicit_data_init(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
+ 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;
++ }
+ }
+
+ /**
+@@ -630,25 +360,27 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
+ int rc;
+
+ if (tcp_conn->in.datalen) {
+- iscsi_conn_printk(KERN_ERR, conn,
+- "invalid R2t with datalen %d\n",
+- 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_datasn != r2tsn){
+- debug_tcp("%s: ctask->exp_datasn(%d) != rhdr->r2tsn(%d)\n",
+- __FUNCTION__, tcp_ctask->exp_datasn, r2tsn);
++ if (tcp_ctask->exp_r2tsn && tcp_ctask->exp_r2tsn != r2tsn)
+ return ISCSI_ERR_R2TSN;
+- }
+
+- /* fill-in new R2T associated with the task */
+- iscsi_update_cmdsn(session, (struct iscsi_nopin*)rhdr);
++ rc = iscsi_check_assign_cmdsn(session, (struct iscsi_nopin*)rhdr);
++ if (rc)
++ return rc;
+
+- if (!ctask->sc || session->state != ISCSI_STATE_LOGGED_IN) {
+- iscsi_conn_printk(KERN_INFO, conn,
+- "dropping R2T itt %d in recovery.\n",
+- ctask->itt);
++ /* 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;
+ }
+
+@@ -658,10 +390,8 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
+ r2t->exp_statsn = rhdr->statsn;
+ r2t->data_length = be32_to_cpu(rhdr->data_length);
+ if (r2t->data_length == 0) {
+- iscsi_conn_printk(KERN_ERR, conn,
+- "invalid R2T with zero data len\n");
+- __kfifo_put(tcp_ctask->r2tpool.queue, (void*)&r2t,
+- sizeof(void*));
++ printk(KERN_ERR "iscsi_tcp: invalid R2T with zero data len\n");
++ spin_unlock(&session->lock);
+ return ISCSI_ERR_DATALEN;
+ }
+
+@@ -671,13 +401,11 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
+ r2t->data_length, session->max_burst);
+
+ r2t->data_offset = be32_to_cpu(rhdr->data_offset);
+- if (r2t->data_offset + r2t->data_length > scsi_out(ctask->sc)->length) {
+- iscsi_conn_printk(KERN_ERR, conn,
+- "invalid R2T with data len %u at offset %u "
+- "and total length %d\n", r2t->data_length,
+- r2t->data_offset, scsi_out(ctask->sc)->length);
+- __kfifo_put(tcp_ctask->r2tpool.queue, (void*)&r2t,
+- sizeof(void*));
++ 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;
+ }
+
+@@ -686,134 +414,108 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
+
+ iscsi_solicit_data_init(conn, ctask, r2t);
+
+- tcp_ctask->exp_datasn = r2tsn + 1;
++ tcp_ctask->exp_r2tsn = r2tsn + 1;
+ __kfifo_put(tcp_ctask->r2tqueue, (void*)&r2t, sizeof(void*));
+- conn->r2t_pdus_cnt++;
++ tcp_ctask->xmstate |= XMSTATE_SOL_HDR;
++ list_move_tail(&ctask->running, &conn->xmitqueue);
+
+- iscsi_requeue_ctask(ctask);
+- return 0;
+-}
+-
+-/*
+- * Handle incoming reply to DataIn command
+- */
+-static int
+-iscsi_tcp_process_data_in(struct iscsi_tcp_conn *tcp_conn,
+- struct iscsi_segment *segment)
+-{
+- struct iscsi_conn *conn = tcp_conn->iscsi_conn;
+- struct iscsi_hdr *hdr = tcp_conn->in.hdr;
+- int rc;
+-
+- if (!iscsi_tcp_dgst_verify(tcp_conn, segment))
+- return ISCSI_ERR_DATA_DGST;
+-
+- /* check for non-exceptional status */
+- if (hdr->flags & ISCSI_FLAG_DATA_STATUS) {
+- rc = iscsi_complete_pdu(conn, tcp_conn->in.hdr, NULL, 0);
+- if (rc)
+- return rc;
+- }
++ scsi_queue_work(session->host, &conn->xmitwork);
++ conn->r2t_pdus_cnt++;
++ spin_unlock(&session->lock);
+
+- iscsi_tcp_hdr_recv_prep(tcp_conn);
+ return 0;
+ }
+
+-/**
+- * iscsi_tcp_hdr_dissect - process PDU header
+- * @conn: iSCSI connection
+- * @hdr: PDU header
+- *
+- * This function analyzes the header of the PDU received,
+- * and performs several sanity checks. If the PDU is accompanied
+- * by data, the receive buffer is set up to copy the incoming data
+- * to the correct location.
+- */
+ static int
+-iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
++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;
+- struct iscsi_cmd_task *ctask;
+- uint32_t itt;
++ 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) {
+- iscsi_conn_printk(KERN_ERR, conn,
+- "iscsi_tcp: datalen %d > %d\n",
+- 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;
+
+- /* Additional header segments. So far, we don't
+- * process additional headers.
+- */
++ /* 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)
++ 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 ahslen %d datalen %d\n",
+- opcode, ahslen, tcp_conn->in.datalen);
++ 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:
+- ctask = session->cmds[itt];
+- spin_lock(&conn->session->lock);
+- rc = iscsi_data_rsp(conn, ctask);
+- spin_unlock(&conn->session->lock);
++ tcp_conn->in.ctask = session->cmds[itt];
++ rc = iscsi_data_rsp(conn, tcp_conn->in.ctask);
+ if (rc)
+ return rc;
+- if (tcp_conn->in.datalen) {
+- struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+- struct hash_desc *rx_hash = NULL;
+- struct scsi_data_buffer *sdb = scsi_in(ctask->sc);
+-
+- /*
+- * Setup copy of Data-In into the Scsi_Cmnd
+- * Scatterlist case:
+- * We set up the iscsi_segment to point to the next
+- * scatterlist entry to copy to. As we go along,
+- * we move on to the next scatterlist entry and
+- * update the digest per-entry.
+- */
+- if (conn->datadgst_en)
+- rx_hash = &tcp_conn->rx_hash;
+-
+- debug_tcp("iscsi_tcp_begin_data_in(%p, offset=%d, "
+- "datalen=%d)\n", tcp_conn,
+- tcp_ctask->data_offset,
+- tcp_conn->in.datalen);
+- return iscsi_segment_seek_sg(&tcp_conn->in.segment,
+- sdb->table.sgl,
+- sdb->table.nents,
+- tcp_ctask->data_offset,
+- tcp_conn->in.datalen,
+- iscsi_tcp_process_data_in,
+- rx_hash);
+- }
+ /* fall through */
+ case ISCSI_OP_SCSI_CMD_RSP:
+- if (tcp_conn->in.datalen) {
+- iscsi_tcp_data_recv_prep(tcp_conn);
+- return 0;
+- }
+- rc = iscsi_complete_pdu(conn, hdr, NULL, 0);
++ 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:
+- ctask = session->cmds[itt];
++ tcp_conn->in.ctask = session->cmds[itt];
+ if (ahslen)
+ rc = ISCSI_ERR_AHSLEN;
+- else if (ctask->sc->sc_data_direction == DMA_TO_DEVICE) {
+- spin_lock(&session->lock);
+- rc = iscsi_r2t_rsp(conn, ctask);
+- spin_unlock(&session->lock);
+- } else
++ 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:
+@@ -825,24 +527,18 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
+ * 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) {
+- iscsi_conn_printk(KERN_ERR, conn,
+- "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);
++ 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 there's data coming in with the response,
+- * receive it to the connection's buffer.
+- */
+- if (tcp_conn->in.datalen) {
+- iscsi_tcp_data_recv_prep(tcp_conn);
+- return 0;
+- }
++ if (tcp_conn->in.datalen)
++ goto copy_hdr;
+ /* fall through */
+ case ISCSI_OP_LOGOUT_RSP:
+ case ISCSI_OP_NOOP_IN:
+@@ -854,161 +550,457 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
+ break;
+ }
+
+- if (rc == 0) {
+- /* Anything that comes with data should have
+- * been handled above. */
+- if (tcp_conn->in.datalen)
+- return ISCSI_ERR_PROTO;
+- iscsi_tcp_hdr_recv_prep(tcp_conn);
++ 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;
++}
+
+- return rc;
++/**
++ * 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_hdr_recv_done - process PDU header
++ * iscsi_tcp_copy - copy skb bits to the destanation buffer
++ * @conn: iscsi tcp connection
+ *
+- * This is the callback invoked when the PDU header has
+- * been received. If the header is followed by additional
+- * header segments, we go back for more data.
+- */
+-static int
+-iscsi_tcp_hdr_recv_done(struct iscsi_tcp_conn *tcp_conn,
+- struct iscsi_segment *segment)
++ * 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_conn *conn = tcp_conn->iscsi_conn;
+- struct iscsi_hdr *hdr;
++ 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);
++}
+
+- /* Check if there are additional header segments
+- * *prior* to computing the digest, because we
+- * may need to go back to the caller for more.
++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
+ */
+- hdr = (struct iscsi_hdr *) tcp_conn->in.hdr_buf;
+- if (segment->copied == sizeof(struct iscsi_hdr) && hdr->hlength) {
+- /* Bump the header length - the caller will
+- * just loop around and get the AHS for us, and
+- * call again. */
+- unsigned int ahslen = hdr->hlength << 2;
+-
+- /* Make sure we don't overflow */
+- if (sizeof(*hdr) + ahslen > sizeof(tcp_conn->in.hdr_buf))
+- return ISCSI_ERR_AHSLEN;
+-
+- segment->total_size += ahslen;
+- segment->size += ahslen;
+- return 0;
++ 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;
+ }
+
+- /* We're done processing the header. See if we're doing
+- * header digests; if so, set up the recv_digest buffer
+- * and go back for more. */
+- if (conn->hdrdgst_en) {
+- if (segment->digest_len == 0) {
+- iscsi_tcp_segment_splice_digest(segment,
+- segment->recv_digest);
+- return 0;
++ 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++;
+ }
+- iscsi_tcp_dgst_header(&tcp_conn->rx_hash, hdr,
+- segment->total_copied - ISCSI_DIGEST_SIZE,
+- segment->digest);
+
+- if (!iscsi_tcp_dgst_verify(tcp_conn, segment))
+- return ISCSI_ERR_HDR_DGST;
++ 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);
+ }
+
+- tcp_conn->in.hdr = hdr;
+- return iscsi_tcp_hdr_dissect(conn, hdr);
++ 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_recv - TCP receive in sendfile fashion
++ * 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_recv(read_descriptor_t *rd_desc, struct sk_buff *skb,
+- unsigned int offset, size_t len)
++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;
+- struct iscsi_segment *segment = &tcp_conn->in.segment;
+- struct skb_seq_state seq;
+- unsigned int consumed = 0;
+- int rc = 0;
++ int processed;
++ char pad[ISCSI_PAD_LEN];
++ struct scatterlist sg;
+
+- debug_tcp("in %d bytes\n", skb->len - offset);
++ /*
++ * 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;
+ }
+
+- skb_prepare_seq_read(skb, offset, skb->len, &seq);
+- while (1) {
+- unsigned int avail;
+- const u8 *ptr;
++ 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;
++ }
++ }
+
+- avail = skb_seq_read(consumed, &ptr, &seq);
+- if (avail == 0) {
+- debug_tcp("no more data avail. Consumed %d\n",
+- consumed);
+- break;
++ /*
++ * 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;
+ }
+- BUG_ON(segment->copied >= segment->size);
+-
+- debug_tcp("skb %p ptr=%p avail=%u\n", skb, ptr, avail);
+- rc = iscsi_tcp_segment_recv(tcp_conn, segment, ptr, avail);
+- BUG_ON(rc == 0);
+- consumed += rc;
+-
+- if (segment->total_copied >= segment->total_size) {
+- debug_tcp("segment done\n");
+- rc = segment->done(tcp_conn, segment);
+- if (rc != 0) {
+- skb_abort_seq_read(&seq);
+- goto error;
+- }
++ }
+
+- /* The done() functions sets up the
+- * next segment. */
++ 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;
+ }
+ }
+- skb_abort_seq_read(&seq);
+- conn->rxdata_octets += consumed;
+- return consumed;
+
+-error:
+- debug_tcp("Error receiving PDU, errno=%d\n", rc);
+- iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
+- return 0;
++ 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;
+- struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ read_descriptor_t rd_desc;
+
+ read_lock(&sk->sk_callback_lock);
+
+ /*
+- * Use rd_desc to pass 'conn' to iscsi_tcp_recv.
++ * 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_recv
++ * 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_recv);
++ tcp_read_sock(sk, &rd_desc, iscsi_tcp_data_recv);
+
+ read_unlock(&sk->sk_callback_lock);
+-
+- /* If we had to (atomically) map a highmem page,
+- * unmap it now. */
+- iscsi_tcp_segment_unmap(&tcp_conn->in.segment);
+ }
+
+ static void
+@@ -1088,173 +1080,121 @@ iscsi_conn_restore_callbacks(struct iscsi_tcp_conn *tcp_conn)
+ }
+
+ /**
+- * iscsi_xmit - TCP transmit
+- **/
+-static int
+-iscsi_xmit(struct iscsi_conn *conn)
++ * 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 iscsi_segment *segment = &tcp_conn->out.segment;
+- unsigned int consumed = 0;
+- int rc = 0;
+-
+- while (1) {
+- rc = iscsi_tcp_xmit_segment(tcp_conn, segment);
+- if (rc < 0)
+- goto error;
+- if (rc == 0)
+- break;
+-
+- consumed += rc;
++ struct socket *sk = tcp_conn->sock;
++ int offset = buf->sg.offset + buf->sent, res;
+
+- if (segment->total_copied >= segment->total_size) {
+- if (segment->done != NULL) {
+- rc = segment->done(tcp_conn, segment);
+- if (rc < 0)
+- goto error;
+- }
+- }
++ /*
++ * 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;
+ }
+
+- debug_tcp("xmit %d bytes\n", consumed);
+-
+- conn->txdata_octets += consumed;
+- return consumed;
+-
+-error:
+- /* Transmit error. We could initiate error recovery
+- * here. */
+- debug_tcp("Error sending PDU, errno=%d\n", rc);
+- iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
+- return rc;
++ tcp_conn->sendpage_failures_cnt++;
++ if (res == -EAGAIN)
++ res = -ENOBUFS;
++ else
++ iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
++ return res;
+ }
+
+ /**
+- * iscsi_tcp_xmit_qlen - return the number of bytes queued for xmit
+- */
+-static inline int
+-iscsi_tcp_xmit_qlen(struct iscsi_conn *conn)
+-{
+- struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+- struct iscsi_segment *segment = &tcp_conn->out.segment;
+-
+- return segment->total_copied - segment->total_size;
+-}
+-
++ * 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_tcp_flush(struct iscsi_conn *conn)
++iscsi_sendhdr(struct iscsi_conn *conn, struct iscsi_buf *buf, int datalen)
+ {
+- int rc;
+-
+- while (iscsi_tcp_xmit_qlen(conn)) {
+- rc = iscsi_xmit(conn);
+- if (rc == 0)
++ 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;
+- if (rc < 0)
+- return rc;
++ return 0;
+ }
+
+- return 0;
+-}
+-
+-/*
+- * This is called when we're done sending the header.
+- * Simply copy the data_segment to the send segment, and return.
+- */
+-static int
+-iscsi_tcp_send_hdr_done(struct iscsi_tcp_conn *tcp_conn,
+- struct iscsi_segment *segment)
+-{
+- tcp_conn->out.segment = tcp_conn->out.data_segment;
+- debug_tcp("Header done. Next segment size %u total_size %u\n",
+- tcp_conn->out.segment.size, tcp_conn->out.segment.total_size);
+- return 0;
++ return res;
+ }
+
+-static void
+-iscsi_tcp_send_hdr_prep(struct iscsi_conn *conn, void *hdr, size_t hdrlen)
++/**
++ * 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)
+ {
+- struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+-
+- debug_tcp("%s(%p%s)\n", __FUNCTION__, tcp_conn,
+- conn->hdrdgst_en? ", digest enabled" : "");
+-
+- /* Clear the data segment - needs to be filled in by the
+- * caller using iscsi_tcp_send_data_prep() */
+- memset(&tcp_conn->out.data_segment, 0, sizeof(struct iscsi_segment));
+-
+- /* If header digest is enabled, compute the CRC and
+- * place the digest into the same buffer. We make
+- * sure that both iscsi_tcp_ctask and mtask have
+- * sufficient room.
+- */
+- if (conn->hdrdgst_en) {
+- iscsi_tcp_dgst_header(&tcp_conn->tx_hash, hdr, hdrlen,
+- hdr + hdrlen);
+- hdrlen += ISCSI_DIGEST_SIZE;
++ 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;
+ }
+
+- /* Remember header pointer for later, when we need
+- * to decide whether there's a payload to go along
+- * with the header. */
+- tcp_conn->out.hdr = hdr;
+-
+- iscsi_segment_init_linear(&tcp_conn->out.segment, hdr, hdrlen,
+- iscsi_tcp_send_hdr_done, NULL);
+-}
+-
+-/*
+- * Prepare the send buffer for the payload data.
+- * Padding and checksumming will all be taken care
+- * of by the iscsi_segment routines.
+- */
+-static int
+-iscsi_tcp_send_data_prep(struct iscsi_conn *conn, struct scatterlist *sg,
+- unsigned int count, unsigned int offset,
+- unsigned int len)
+-{
+- struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+- struct hash_desc *tx_hash = NULL;
+- unsigned int hdr_spec_len;
+-
+- debug_tcp("%s(%p, offset=%d, datalen=%d%s)\n", __FUNCTION__,
+- tcp_conn, offset, len,
+- conn->datadgst_en? ", digest enabled" : "");
+-
+- /* Make sure the datalen matches what the caller
+- said he would send. */
+- hdr_spec_len = ntoh24(tcp_conn->out.hdr->dlength);
+- WARN_ON(iscsi_padded(len) != iscsi_padded(hdr_spec_len));
+-
+- if (conn->datadgst_en)
+- tx_hash = &tcp_conn->tx_hash;
+-
+- return iscsi_segment_seek_sg(&tcp_conn->out.data_segment,
+- sg, count, offset, len,
+- NULL, tx_hash);
++ return res;
+ }
+
+-static void
+-iscsi_tcp_send_linear_data_prepare(struct iscsi_conn *conn, void *data,
+- size_t len)
++static inline void
++iscsi_data_digest_init(struct iscsi_tcp_conn *tcp_conn,
++ struct iscsi_tcp_cmd_task *tcp_ctask)
+ {
+- struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+- struct hash_desc *tx_hash = NULL;
+- unsigned int hdr_spec_len;
+-
+- debug_tcp("%s(%p, datalen=%d%s)\n", __FUNCTION__, tcp_conn, len,
+- conn->datadgst_en? ", digest enabled" : "");
+-
+- /* Make sure the datalen matches what the caller
+- said he would send. */
+- hdr_spec_len = ntoh24(tcp_conn->out.hdr->dlength);
+- WARN_ON(iscsi_padded(len) != iscsi_padded(hdr_spec_len));
+-
+- if (conn->datadgst_en)
+- tx_hash = &tcp_conn->tx_hash;
+-
+- iscsi_segment_init_linear(&tcp_conn->out.data_segment,
+- data, len, NULL, tx_hash);
++ crypto_hash_init(&tcp_conn->tx_hash);
++ tcp_ctask->digest_count = 4;
+ }
+
+ /**
+@@ -1270,17 +1210,13 @@ iscsi_tcp_send_linear_data_prepare(struct iscsi_conn *conn, void *data,
+ *
+ * Called under connection lock.
+ **/
+-static int
++static void
+ iscsi_solicit_data_cont(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
+- struct iscsi_r2t_info *r2t)
++ struct iscsi_r2t_info *r2t, int left)
+ {
+ struct iscsi_data *hdr;
+- int new_offset, left;
+-
+- BUG_ON(r2t->data_length - r2t->sent < 0);
+- left = r2t->data_length - r2t->sent;
+- if (left == 0)
+- return 0;
++ struct scsi_cmnd *sc = ctask->sc;
++ int new_offset;
+
+ hdr = &r2t->dtask.hdr;
+ memset(hdr, 0, sizeof(struct iscsi_data));
+@@ -1301,47 +1237,81 @@ iscsi_solicit_data_cont(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
+ r2t->data_count = left;
+ hdr->flags = ISCSI_FLAG_CMD_FINAL;
+ }
+-
+ conn->dataout_pdus_cnt++;
+- return 1;
++
++ 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_ctask - Initialize iSCSI SCSI_READ or SCSI_WRITE commands
++ * iscsi_tcp_cmd_init - Initialize iSCSI SCSI_READ or SCSI_WRITE commands
+ * @conn: iscsi connection
+ * @ctask: scsi command task
+ * @sc: scsi command
+ **/
+-static int
+-iscsi_tcp_ctask_init(struct iscsi_cmd_task *ctask)
++static void
++iscsi_tcp_cmd_init(struct iscsi_cmd_task *ctask)
+ {
+- struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+- struct iscsi_conn *conn = ctask->conn;
+ struct scsi_cmnd *sc = ctask->sc;
+- int err;
++ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+
+ BUG_ON(__kfifo_len(tcp_ctask->r2tqueue));
++
+ tcp_ctask->sent = 0;
+- tcp_ctask->exp_datasn = 0;
++ tcp_ctask->sg_count = 0;
+
+- /* Prepare PDU, optionally w/ immediate data */
+- debug_scsi("ctask deq [cid %d itt 0x%x imm %d unsol %d]\n",
+- conn->id, ctask->itt, ctask->imm_count,
+- ctask->unsol_count);
+- iscsi_tcp_send_hdr_prep(conn, ctask->hdr, ctask->hdr_len);
++ 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 (!ctask->imm_count)
+- return 0;
++ if (sc->use_sg) {
++ struct scatterlist *sg = sc->request_buffer;
+
+- /* If we have immediate data, attach a payload */
+- err = iscsi_tcp_send_data_prep(conn, scsi_out(sc)->table.sgl,
+- scsi_out(sc)->table.nents,
+- 0, ctask->imm_count);
+- if (err)
+- return err;
+- tcp_ctask->sent += ctask->imm_count;
+- ctask->imm_count = 0;
+- return 0;
++ 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));
+ }
+
+ /**
+@@ -1353,130 +1323,428 @@ iscsi_tcp_ctask_init(struct iscsi_cmd_task *ctask)
+ * 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;
+
+- /* Flush any pending data first. */
+- rc = iscsi_tcp_flush(conn);
+- if (rc < 0)
+- return 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);
+- iscsi_free_mgmt_task(conn, mtask);
++ 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;
+ }
+
+-/*
+- * iscsi_tcp_ctask_xmit - xmit normal PDU task
+- * @conn: iscsi connection
+- * @ctask: iscsi command task
+- *
+- * We're expected to return 0 when everything was transmitted succesfully,
+- * -EAGAIN if there's still data in the queue, or != 0 for any other kind
+- * of error.
+- */
+ static int
+-iscsi_tcp_ctask_xmit(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
++iscsi_send_padding(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
+ {
+ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+- struct scsi_cmnd *sc = ctask->sc;
+- struct scsi_data_buffer *sdb = scsi_out(sc);
+- int rc = 0;
++ 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;
+
+-flush:
+- /* Flush any pending data first. */
+- rc = iscsi_tcp_flush(conn);
+- if (rc < 0)
+- return rc;
++ 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;
++}
+
+- /* Are we done already? */
+- if (sc->sc_data_direction != DMA_TO_DEVICE)
++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;
+
+- if (ctask->unsol_count != 0) {
+- struct iscsi_data *hdr = &tcp_ctask->unsol_dtask.hdr;
++ tcp_ctask = ctask->dd_data;
++ tcp_conn = conn->dd_data;
+
+- /* Prepare a header for the unsolicited PDU.
+- * The amount of data we want to send will be
+- * in ctask->data_count.
+- * FIXME: return the data count instead.
+- */
+- iscsi_prep_unsolicit_data_pdu(ctask, hdr);
++ 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;
++}
+
+- debug_tcp("unsol dout [itt 0x%x doff %d dlen %d]\n",
+- ctask->itt, tcp_ctask->sent, ctask->data_count);
++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;
++ }
+
+- iscsi_tcp_send_hdr_prep(conn, hdr, sizeof(*hdr));
+- rc = iscsi_tcp_send_data_prep(conn, sdb->table.sgl,
+- sdb->table.nents, tcp_ctask->sent,
+- ctask->data_count);
+ if (rc)
+- goto fail;
+- tcp_ctask->sent += ctask->data_count;
+- ctask->unsol_count -= ctask->data_count;
+- goto flush;
+- } else {
+- struct iscsi_session *session = conn->session;
+- struct iscsi_r2t_info *r2t;
++ 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;
++}
+
+- /* All unsolicited PDUs sent. Check for solicited PDUs.
++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.
+ */
+- spin_lock_bh(&session->lock);
+- r2t = tcp_ctask->r2t;
+- if (r2t != NULL) {
+- /* Continue with this R2T? */
+- if (!iscsi_solicit_data_cont(conn, ctask, r2t)) {
+- debug_scsi(" done with r2t %p\n", r2t);
+-
+- __kfifo_put(tcp_ctask->r2tpool.queue,
+- (void*)&r2t, sizeof(void*));
+- tcp_ctask->r2t = r2t = NULL;
+- }
++ if (ctask->unsol_count) {
++ debug_scsi("sending more uns\n");
++ tcp_ctask->xmstate |= XMSTATE_UNS_INIT;
++ goto send_hdr;
+ }
++ }
++ return 0;
++}
+
+- if (r2t == NULL) {
++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*));
+- r2t = tcp_ctask->r2t;
++ 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;
+ }
+- spin_unlock_bh(&session->lock);
+
+- /* Waiting for more R2Ts to arrive. */
+- if (r2t == NULL) {
+- debug_tcp("no R2Ts yet\n");
+- return 0;
++ if (conn->datadgst_en) {
++ iscsi_data_digest_init(conn->dd_data, tcp_ctask);
++ dtask->digest = 0;
+ }
+
+- debug_scsi("sol dout %p [dsn %d itt 0x%x doff %d dlen %d]\n",
+- r2t, r2t->solicit_datasn - 1, ctask->itt,
+- r2t->data_offset + r2t->sent, r2t->data_count);
++ 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);
++ }
+
+- iscsi_tcp_send_hdr_prep(conn, &r2t->dtask.hdr,
+- sizeof(struct iscsi_hdr));
++ if (tcp_ctask->xmstate & XMSTATE_SOL_DATA) {
++ r2t = tcp_ctask->r2t;
++ dtask = &r2t->dtask;
+
+- rc = iscsi_tcp_send_data_prep(conn, sdb->table.sgl,
+- sdb->table.nents,
+- r2t->data_offset + r2t->sent,
+- r2t->data_count);
++ rc = iscsi_send_data(ctask, &r2t->sendbuf, &r2t->sg,
++ &r2t->sent, &r2t->data_count,
++ &dtask->digestbuf, &dtask->digest);
+ if (rc)
+- goto fail;
+- tcp_ctask->sent += r2t->data_count;
+- r2t->sent += r2t->data_count;
+- goto flush;
++ 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;
+-fail:
+- iscsi_conn_failure(conn, rc);
+- return -EIO;
++}
++
++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 *
+@@ -1502,29 +1770,37 @@ iscsi_tcp_conn_create(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
+
+ 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))
++ 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))
++ 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:
+- iscsi_conn_printk(KERN_ERR, conn,
+- "Could not create connection due to crc32c "
+- "loading error. Make sure the crc32c "
+- "module is built as a module or into the "
+- "kernel\n");
+ kfree(tcp_conn);
+ tcp_conn_alloc_fail:
+ iscsi_conn_teardown(cls_conn);
+@@ -1534,22 +1810,18 @@ tcp_conn_alloc_fail:
+ static void
+ iscsi_tcp_release_conn(struct iscsi_conn *conn)
+ {
+- struct iscsi_session *session = conn->session;
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+- struct socket *sock = tcp_conn->sock;
+
+- if (!sock)
++ if (!tcp_conn->sock)
+ return;
+
+- sock_hold(sock->sk);
++ sock_hold(tcp_conn->sock->sk);
+ iscsi_conn_restore_callbacks(tcp_conn);
+- sock_put(sock->sk);
++ sock_put(tcp_conn->sock->sk);
+
+- spin_lock_bh(&session->lock);
++ sock_release(tcp_conn->sock);
+ tcp_conn->sock = NULL;
+ conn->recv_lock = NULL;
+- spin_unlock_bh(&session->lock);
+- sockfd_put(sock);
+ }
+
+ static void
+@@ -1573,49 +1845,11 @@ 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);
+-}
+-
+-static int iscsi_tcp_get_addr(struct iscsi_conn *conn, struct socket *sock,
+- char *buf, int *port,
+- int (*getname)(struct socket *, struct sockaddr *,
+- int *addrlen))
+-{
+- struct sockaddr_storage *addr;
+- struct sockaddr_in6 *sin6;
+- struct sockaddr_in *sin;
+- int rc = 0, len;
+-
+- addr = kmalloc(sizeof(*addr), GFP_KERNEL);
+- if (!addr)
+- return -ENOMEM;
+-
+- if (getname(sock, (struct sockaddr *) addr, &len)) {
+- rc = -ENODEV;
+- goto free_addr;
+- }
+-
+- switch (addr->ss_family) {
+- case AF_INET:
+- sin = (struct sockaddr_in *)addr;
+- spin_lock_bh(&conn->session->lock);
+- sprintf(buf, NIPQUAD_FMT, NIPQUAD(sin->sin_addr.s_addr));
+- *port = be16_to_cpu(sin->sin_port);
+- spin_unlock_bh(&conn->session->lock);
+- break;
+- case AF_INET6:
+- sin6 = (struct sockaddr_in6 *)addr;
+- spin_lock_bh(&conn->session->lock);
+- sprintf(buf, NIP6_FMT, NIP6(sin6->sin6_addr));
+- *port = be16_to_cpu(sin6->sin6_port);
+- spin_unlock_bh(&conn->session->lock);
+- break;
+- }
+-free_addr:
+- kfree(addr);
+- return rc;
++ tcp_conn->hdr_size = sizeof(struct iscsi_hdr);
+ }
+
+ static int
+@@ -1632,28 +1866,13 @@ iscsi_tcp_conn_bind(struct iscsi_cls_session *cls_session,
+ /* lookup for existing socket */
+ sock = sockfd_lookup((int)transport_eph, &err);
+ if (!sock) {
+- iscsi_conn_printk(KERN_ERR, conn,
+- "sockfd_lookup failed %d\n", err);
++ printk(KERN_ERR "iscsi_tcp: sockfd_lookup failed %d\n", err);
+ return -EEXIST;
+ }
+- /*
+- * copy these values now because if we drop the session
+- * userspace may still want to query the values since we will
+- * be using them for the reconnect
+- */
+- err = iscsi_tcp_get_addr(conn, sock, conn->portal_address,
+- &conn->portal_port, kernel_getpeername);
+- if (err)
+- goto free_socket;
+-
+- err = iscsi_tcp_get_addr(conn, sock, conn->local_address,
+- &conn->local_port, kernel_getsockname);
+- if (err)
+- goto free_socket;
+
+ err = iscsi_conn_bind(cls_session, cls_conn, is_leading);
+ if (err)
+- goto free_socket;
++ return err;
+
+ /* bind iSCSI connection and socket */
+ tcp_conn->sock = sock;
+@@ -1676,27 +1895,26 @@ iscsi_tcp_conn_bind(struct iscsi_cls_session *cls_session,
+ /*
+ * set receive state machine into initial state
+ */
+- iscsi_tcp_hdr_recv_prep(tcp_conn);
+- return 0;
++ tcp_conn->in_progress = IN_PROGRESS_WAIT_HEADER;
+
+-free_socket:
+- sockfd_put(sock);
+- return err;
++ return 0;
+ }
+
+ /* called with host lock */
+ static void
+-iscsi_tcp_mtask_init(struct iscsi_conn *conn, struct iscsi_mgmt_task *mtask)
++iscsi_tcp_mgmt_init(struct iscsi_conn *conn, struct iscsi_mgmt_task *mtask,
++ char *data, uint32_t data_size)
+ {
+- debug_scsi("mtask deq [cid %d itt 0x%x]\n", conn->id, mtask->itt);
++ struct iscsi_tcp_mgmt_task *tcp_mtask = mtask->dd_data;
+
+- /* Prepare PDU, optionally w/ immediate data */
+- iscsi_tcp_send_hdr_prep(conn, mtask->hdr, sizeof(*mtask->hdr));
++ 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 we have immediate data, attach a payload */
+ if (mtask->data_count)
+- iscsi_tcp_send_linear_data_prepare(conn, mtask->data,
+- mtask->data_count);
++ iscsi_buf_init_iov(&tcp_mtask->sendbuf, (char*)mtask->data,
++ mtask->data_count);
+ }
+
+ static int
+@@ -1719,7 +1937,8 @@ iscsi_r2tpool_alloc(struct iscsi_session *session)
+ */
+
+ /* R2T pool */
+- if (iscsi_pool_init(&tcp_ctask->r2tpool, session->max_r2t * 4, NULL,
++ if (iscsi_pool_init(&tcp_ctask->r2tpool, session->max_r2t * 4,
++ (void***)&tcp_ctask->r2ts,
+ sizeof(struct iscsi_r2t_info))) {
+ goto r2t_alloc_fail;
+ }
+@@ -1728,7 +1947,8 @@ iscsi_r2tpool_alloc(struct iscsi_session *session)
+ 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);
++ iscsi_pool_free(&tcp_ctask->r2tpool,
++ (void**)tcp_ctask->r2ts);
+ goto r2t_alloc_fail;
+ }
+ }
+@@ -1741,7 +1961,8 @@ r2t_alloc_fail:
+ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+
+ kfifo_free(tcp_ctask->r2tqueue);
+- iscsi_pool_free(&tcp_ctask->r2tpool);
++ iscsi_pool_free(&tcp_ctask->r2tpool,
++ (void**)tcp_ctask->r2ts);
+ }
+ return -ENOMEM;
+ }
+@@ -1756,7 +1977,8 @@ iscsi_r2tpool_free(struct iscsi_session *session)
+ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+
+ kfifo_free(tcp_ctask->r2tqueue);
+- iscsi_pool_free(&tcp_ctask->r2tpool);
++ iscsi_pool_free(&tcp_ctask->r2tpool,
++ (void**)tcp_ctask->r2ts);
+ }
+ }
+
+@@ -1772,6 +1994,9 @@ iscsi_conn_set_param(struct iscsi_cls_conn *cls_conn, enum iscsi_param param,
+ 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);
+@@ -1780,12 +2005,12 @@ iscsi_conn_set_param(struct iscsi_cls_conn *cls_conn, enum iscsi_param param,
+ break;
+ case ISCSI_PARAM_MAX_R2T:
+ sscanf(buf, "%d", &value);
+- if (value <= 0 || !is_power_of_2(value))
+- return -EINVAL;
+- if (session->max_r2t == 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;
+@@ -1801,18 +2026,41 @@ 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:
+- spin_lock_bh(&conn->session->lock);
+- len = sprintf(buf, "%hu\n", conn->portal_port);
+- spin_unlock_bh(&conn->session->lock);
++ 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:
+- spin_lock_bh(&conn->session->lock);
+- len = sprintf(buf, "%s\n", conn->portal_address);
+- spin_unlock_bh(&conn->session->lock);
++ 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);
+@@ -1821,29 +2069,6 @@ iscsi_tcp_conn_get_param(struct iscsi_cls_conn *cls_conn,
+ return len;
+ }
+
+-static int
+-iscsi_tcp_host_get_param(struct Scsi_Host *shost, enum iscsi_host_param param,
+- char *buf)
+-{
+- struct iscsi_session *session = iscsi_hostdata(shost->hostdata);
+- int len;
+-
+- switch (param) {
+- case ISCSI_HOST_PARAM_IPADDRESS:
+- spin_lock_bh(&session->lock);
+- if (!session->leadconn)
+- len = -ENODEV;
+- else
+- len = sprintf(buf, "%s\n",
+- session->leadconn->local_address);
+- spin_unlock_bh(&session->lock);
+- break;
+- default:
+- return iscsi_host_get_param(shost, param, buf);
+- }
+- return len;
+-}
+-
+ static void
+ iscsi_conn_get_stats(struct iscsi_cls_conn *cls_conn, struct iscsi_stats *stats)
+ {
+@@ -1871,7 +2096,6 @@ iscsi_conn_get_stats(struct iscsi_cls_conn *cls_conn, struct iscsi_stats *stats)
+ static struct iscsi_cls_session *
+ iscsi_tcp_session_create(struct iscsi_transport *iscsit,
+ struct scsi_transport_template *scsit,
+- uint16_t cmds_max, uint16_t qdepth,
+ uint32_t initial_cmdsn, uint32_t *hostno)
+ {
+ struct iscsi_cls_session *cls_session;
+@@ -1879,7 +2103,7 @@ iscsi_tcp_session_create(struct iscsi_transport *iscsit,
+ uint32_t hn;
+ int cmd_i;
+
+- cls_session = iscsi_session_setup(iscsit, scsit, cmds_max, qdepth,
++ cls_session = iscsi_session_setup(iscsit, scsit,
+ sizeof(struct iscsi_tcp_cmd_task),
+ sizeof(struct iscsi_tcp_mgmt_task),
+ initial_cmdsn, &hn);
+@@ -1892,15 +2116,14 @@ iscsi_tcp_session_create(struct iscsi_transport *iscsit,
+ struct iscsi_cmd_task *ctask = session->cmds[cmd_i];
+ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+
+- ctask->hdr = &tcp_ctask->hdr.cmd_hdr;
+- ctask->hdr_max = sizeof(tcp_ctask->hdr) - ISCSI_DIGEST_SIZE;
++ 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 = (struct iscsi_hdr *) &tcp_mtask->hdr;
++ mtask->hdr = &tcp_mtask->hdr;
+ }
+
+ if (iscsi_r2tpool_alloc(class_to_transport_session(cls_session)))
+@@ -1919,27 +2142,17 @@ static void iscsi_tcp_session_destroy(struct iscsi_cls_session *cls_session)
+ iscsi_session_teardown(cls_session);
+ }
+
+-static int iscsi_tcp_slave_configure(struct scsi_device *sdev)
+-{
+- blk_queue_bounce_limit(sdev->request_queue, BLK_BOUNCE_ANY);
+- blk_queue_dma_alignment(sdev->request_queue, 0);
+- return 0;
+-}
+-
+ static struct scsi_host_template iscsi_sht = {
+- .module = THIS_MODULE,
+ .name = "iSCSI Initiator over TCP/IP",
+ .queuecommand = iscsi_queuecommand,
+ .change_queue_depth = iscsi_change_queue_depth,
+- .can_queue = ISCSI_DEF_XMIT_CMDS_MAX - 1,
+- .sg_tablesize = 4096,
++ .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_device_reset_handler= iscsi_eh_device_reset,
+ .eh_host_reset_handler = iscsi_eh_host_reset,
+ .use_clustering = DISABLE_CLUSTERING,
+- .slave_configure = iscsi_tcp_slave_configure,
+ .proc_name = "iscsi_tcp",
+ .this_id = -1,
+ };
+@@ -1966,19 +2179,12 @@ static struct iscsi_transport iscsi_tcp_transport = {
+ ISCSI_EXP_STATSN |
+ ISCSI_PERSISTENT_PORT |
+ ISCSI_PERSISTENT_ADDRESS |
+- ISCSI_TARGET_NAME | ISCSI_TPGT |
+- ISCSI_USERNAME | ISCSI_PASSWORD |
+- ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN |
+- ISCSI_FAST_ABORT | ISCSI_ABORT_TMO |
+- ISCSI_LU_RESET_TMO |
+- ISCSI_PING_TMO | ISCSI_RECV_TMO,
+- .host_param_mask = ISCSI_HOST_HWADDRESS | ISCSI_HOST_IPADDRESS |
+- ISCSI_HOST_INITIATOR_NAME |
+- ISCSI_HOST_NETDEV_NAME,
++ ISCSI_TARGET_NAME |
++ ISCSI_TPGT,
+ .host_template = &iscsi_sht,
+ .conndata_size = sizeof(struct iscsi_conn),
+ .max_conn = 1,
+- .max_cmd_len = 16,
++ .max_cmd_len = ISCSI_TCP_MAX_CMD_LEN,
+ /* session management */
+ .create_session = iscsi_tcp_session_create,
+ .destroy_session = iscsi_tcp_session_destroy,
+@@ -1991,14 +2197,11 @@ static struct iscsi_transport iscsi_tcp_transport = {
+ .get_session_param = iscsi_session_get_param,
+ .start_conn = iscsi_conn_start,
+ .stop_conn = iscsi_tcp_conn_stop,
+- /* iscsi host params */
+- .get_host_param = iscsi_tcp_host_get_param,
+- .set_host_param = iscsi_host_set_param,
+ /* IO */
+ .send_pdu = iscsi_conn_send_pdu,
+ .get_stats = iscsi_conn_get_stats,
+- .init_cmd_task = iscsi_tcp_ctask_init,
+- .init_mgmt_task = iscsi_tcp_mtask_init,
++ .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,
+diff --git a/drivers/scsi/iscsi_tcp.h b/drivers/scsi/iscsi_tcp.h
+index ed0b991..3273683 100644
+--- a/drivers/scsi/iscsi_tcp.h
++++ b/drivers/scsi/iscsi_tcp.h
+@@ -24,61 +24,68 @@
+
+ #include <scsi/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;
+-struct iscsi_tcp_conn;
+-struct iscsi_segment;
+-
+-typedef int iscsi_segment_done_fn_t(struct iscsi_tcp_conn *,
+- struct iscsi_segment *);
+-
+-struct iscsi_segment {
+- unsigned char *data;
+- unsigned int size;
+- unsigned int copied;
+- unsigned int total_size;
+- unsigned int total_copied;
+-
+- struct hash_desc *hash;
+- unsigned char recv_digest[ISCSI_DIGEST_SIZE];
+- unsigned char digest[ISCSI_DIGEST_SIZE];
+- unsigned int digest_len;
+-
+- struct scatterlist *sg;
+- void *sg_mapped;
+- unsigned int sg_offset;
+-
+- iscsi_segment_done_fn_t *done;
+-};
+
+ /* Socket connection recieve helper */
+ struct iscsi_tcp_recv {
+ struct iscsi_hdr *hdr;
+- struct iscsi_segment segment;
+-
+- /* Allocate buffer for BHS + AHS */
+- uint32_t hdr_buf[64];
++ 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;
+-};
+-
+-/* Socket connection send helper */
+-struct iscsi_tcp_send {
+- struct iscsi_hdr *hdr;
+- struct iscsi_segment segment;
+- struct iscsi_segment data_segment;
++ 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 */
+- struct iscsi_tcp_send out; /* TCP send context */
++ int in_progress; /* connection state machine */
+
+ /* old values for socket callbacks */
+ void (*old_data_ready)(struct sock *, int);
+@@ -93,19 +100,29 @@ struct iscsi_tcp_conn {
+ uint32_t sendpage_failures_cnt;
+ uint32_t discontiguous_hdr_cnt;
+
+- int error;
+-
+ 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[ISCSI_DIGEST_SIZE];/* Header-Digest */
++ 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[ISCSI_DIGEST_SIZE]; /* Header-Digest */
++ 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 {
+@@ -113,26 +130,38 @@ struct iscsi_r2t_info {
+ __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; /* Data-Out header buf */
++ struct iscsi_data_task dtask; /* which data task */
+ };
+
+ struct iscsi_tcp_cmd_task {
+- struct iscsi_hdr_buff {
+- struct iscsi_cmd cmd_hdr;
+- char hdrextbuf[ISCSI_MAX_AHS_SIZE +
+- ISCSI_DIGEST_SIZE];
+- } hdr;
+-
++ 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;
+- uint32_t exp_datasn; /* expected target's R2TSN/DataSN */
++ 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_pool r2tpool;
++ struct iscsi_r2t_info *r2t; /* in progress R2T */
++ struct iscsi_queue r2tpool;
+ struct kfifo *r2tqueue;
+- struct iscsi_data_task unsol_dtask; /* Data-Out header buf */
++ 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 */
+diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
+index b43bf1d..3f5b9b4 100644
+--- a/drivers/scsi/libiscsi.c
++++ b/drivers/scsi/libiscsi.c
+@@ -22,9 +22,9 @@
+ * 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 <linux/log2.h>
+ #include <asm/unaligned.h>
+ #include <net/tcp.h>
+ #include <scsi/scsi_cmnd.h>
+@@ -46,53 +46,27 @@ class_to_transport_session(struct iscsi_cls_session *cls_session)
+ }
+ EXPORT_SYMBOL_GPL(class_to_transport_session);
+
+-/* Serial Number Arithmetic, 32 bits, less than, RFC1982 */
+-#define SNA32_CHECK 2147483648UL
++#define INVALID_SN_DELTA 0xffff
+
+-static int iscsi_sna_lt(u32 n1, u32 n2)
+-{
+- return n1 != n2 && ((n1 < n2 && (n2 - n1 < SNA32_CHECK)) ||
+- (n1 > n2 && (n2 - n1 < SNA32_CHECK)));
+-}
+-
+-/* Serial Number Arithmetic, 32 bits, less than, RFC1982 */
+-static int iscsi_sna_lte(u32 n1, u32 n2)
+-{
+- return n1 == n2 || ((n1 < n2 && (n2 - n1 < SNA32_CHECK)) ||
+- (n1 > n2 && (n2 - n1 < SNA32_CHECK)));
+-}
+-
+-void
+-iscsi_update_cmdsn(struct iscsi_session *session, struct iscsi_nopin *hdr)
++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);
+
+- /*
+- * standard specifies this check for when to update expected and
+- * max sequence numbers
+- */
+- if (iscsi_sna_lt(max_cmdsn, exp_cmdsn - 1))
+- return;
+-
+- if (exp_cmdsn != session->exp_cmdsn &&
+- !iscsi_sna_lt(exp_cmdsn, session->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;
+
+- if (max_cmdsn != session->max_cmdsn &&
+- !iscsi_sna_lt(max_cmdsn, session->max_cmdsn)) {
+- session->max_cmdsn = max_cmdsn;
+- /*
+- * if the window closed with IO queued, then kick the
+- * xmit thread
+- */
+- if (!list_empty(&session->leadconn->xmitqueue) ||
+- !list_empty(&session->leadconn->mgmtqueue))
+- scsi_queue_work(session->host,
+- &session->leadconn->xmitwork);
+- }
++ return 0;
+ }
+-EXPORT_SYMBOL_GPL(iscsi_update_cmdsn);
++EXPORT_SYMBOL_GPL(iscsi_check_assign_cmdsn);
+
+ void iscsi_prep_unsolicit_data_pdu(struct iscsi_cmd_task *ctask,
+ struct iscsi_data *hdr)
+@@ -123,84 +97,6 @@ void iscsi_prep_unsolicit_data_pdu(struct iscsi_cmd_task *ctask,
+ }
+ EXPORT_SYMBOL_GPL(iscsi_prep_unsolicit_data_pdu);
+
+-static int iscsi_add_hdr(struct iscsi_cmd_task *ctask, unsigned len)
+-{
+- unsigned exp_len = ctask->hdr_len + len;
+-
+- if (exp_len > ctask->hdr_max) {
+- WARN_ON(1);
+- return -EINVAL;
+- }
+-
+- WARN_ON(len & (ISCSI_PAD_LEN - 1)); /* caller must pad the AHS */
+- ctask->hdr_len = exp_len;
+- return 0;
+-}
+-
+-/*
+- * make an extended cdb AHS
+- */
+-static int iscsi_prep_ecdb_ahs(struct iscsi_cmd_task *ctask)
+-{
+- struct scsi_cmnd *cmd = ctask->sc;
+- unsigned rlen, pad_len;
+- unsigned short ahslength;
+- struct iscsi_ecdb_ahdr *ecdb_ahdr;
+- int rc;
+-
+- ecdb_ahdr = iscsi_next_hdr(ctask);
+- rlen = cmd->cmd_len - ISCSI_CDB_SIZE;
+-
+- BUG_ON(rlen > sizeof(ecdb_ahdr->ecdb));
+- ahslength = rlen + sizeof(ecdb_ahdr->reserved);
+-
+- pad_len = iscsi_padding(rlen);
+-
+- rc = iscsi_add_hdr(ctask, sizeof(ecdb_ahdr->ahslength) +
+- sizeof(ecdb_ahdr->ahstype) + ahslength + pad_len);
+- if (rc)
+- return rc;
+-
+- if (pad_len)
+- memset(&ecdb_ahdr->ecdb[rlen], 0, pad_len);
+-
+- ecdb_ahdr->ahslength = cpu_to_be16(ahslength);
+- ecdb_ahdr->ahstype = ISCSI_AHSTYPE_CDB;
+- ecdb_ahdr->reserved = 0;
+- memcpy(ecdb_ahdr->ecdb, cmd->cmnd + ISCSI_CDB_SIZE, rlen);
+-
+- debug_scsi("iscsi_prep_ecdb_ahs: varlen_cdb_len %d "
+- "rlen %d pad_len %d ahs_length %d iscsi_headers_size %u\n",
+- cmd->cmd_len, rlen, pad_len, ahslength, ctask->hdr_len);
+-
+- return 0;
+-}
+-
+-static int iscsi_prep_bidi_ahs(struct iscsi_cmd_task *ctask)
+-{
+- struct scsi_cmnd *sc = ctask->sc;
+- struct iscsi_rlength_ahdr *rlen_ahdr;
+- int rc;
+-
+- rlen_ahdr = iscsi_next_hdr(ctask);
+- rc = iscsi_add_hdr(ctask, sizeof(*rlen_ahdr));
+- if (rc)
+- return rc;
+-
+- rlen_ahdr->ahslength =
+- cpu_to_be16(sizeof(rlen_ahdr->read_length) +
+- sizeof(rlen_ahdr->reserved));
+- rlen_ahdr->ahstype = ISCSI_AHSTYPE_RLENGTH;
+- rlen_ahdr->reserved = 0;
+- rlen_ahdr->read_length = cpu_to_be32(scsi_in(sc)->length);
+-
+- debug_scsi("bidi-in rlen_ahdr->read_length(%d) "
+- "rlen_ahdr->ahslength(%d)\n",
+- be32_to_cpu(rlen_ahdr->read_length),
+- be16_to_cpu(rlen_ahdr->ahslength));
+- return 0;
+-}
+-
+ /**
+ * iscsi_prep_scsi_cmd_pdu - prep iscsi scsi cmd pdu
+ * @ctask: iscsi cmd task
+@@ -208,47 +104,26 @@ static int iscsi_prep_bidi_ahs(struct iscsi_cmd_task *ctask)
+ * 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 int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)
++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;
+- unsigned hdrlength, cmd_len;
+- int rc;
+-
+- ctask->hdr_len = 0;
+- rc = iscsi_add_hdr(ctask, sizeof(*hdr));
+- if (rc)
+- return rc;
+- 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, session->age);
+- hdr->cmdsn = cpu_to_be32(session->cmdsn);
+- session->cmdsn++;
+- hdr->exp_statsn = cpu_to_be32(conn->exp_statsn);
+- cmd_len = sc->cmd_len;
+- if (cmd_len < ISCSI_CDB_SIZE)
+- memset(&hdr->cdb[cmd_len], 0, ISCSI_CDB_SIZE - cmd_len);
+- else if (cmd_len > ISCSI_CDB_SIZE) {
+- rc = iscsi_prep_ecdb_ahs(ctask);
+- if (rc)
+- return rc;
+- cmd_len = ISCSI_CDB_SIZE;
+- }
+- memcpy(hdr->cdb, sc->cmnd, cmd_len);
+
+- ctask->imm_count = 0;
+- if (scsi_bidi_cmnd(sc)) {
+- hdr->flags |= ISCSI_FLAG_CMD_READ;
+- rc = iscsi_prep_bidi_ahs(ctask);
+- if (rc)
+- return rc;
+- }
++ 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) {
+- unsigned out_len = scsi_out(sc)->length;
+- hdr->data_length = cpu_to_be32(out_len);
+ hdr->flags |= ISCSI_FLAG_CMD_WRITE;
+ /*
+ * Write counters:
+@@ -264,61 +139,43 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)
+ *
+ * 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 (out_len >= session->first_burst)
++ if (ctask->total_length >= session->first_burst)
+ ctask->imm_count = min(session->first_burst,
+ conn->max_xmit_dlength);
+ else
+- ctask->imm_count = min(out_len,
++ ctask->imm_count = min(ctask->total_length,
+ conn->max_xmit_dlength);
+- hton24(hdr->dlength, ctask->imm_count);
++ hton24(ctask->hdr->dlength, ctask->imm_count);
+ } else
+- zero_data(hdr->dlength);
++ zero_data(ctask->hdr->dlength);
+
+ if (!session->initial_r2t_en) {
+- ctask->unsol_count = min(session->first_burst, out_len)
+- - ctask->imm_count;
++ 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 */
+- hdr->flags |= ISCSI_FLAG_CMD_FINAL;
++ ctask->hdr->flags |= ISCSI_FLAG_CMD_FINAL;
+ } else {
++ ctask->datasn = 0;
+ hdr->flags |= ISCSI_FLAG_CMD_FINAL;
+ zero_data(hdr->dlength);
+- hdr->data_length = cpu_to_be32(scsi_in(sc)->length);
+
+ if (sc->sc_data_direction == DMA_FROM_DEVICE)
+ hdr->flags |= ISCSI_FLAG_CMD_READ;
+ }
+
+- /* calculate size of additional header segments (AHSs) */
+- hdrlength = ctask->hdr_len - sizeof(*hdr);
+-
+- WARN_ON(hdrlength & (ISCSI_PAD_LEN-1));
+- hdrlength /= ISCSI_PAD_LEN;
+-
+- WARN_ON(hdrlength >= 256);
+- hdr->hlength = hdrlength & 0xFF;
+-
+- if (conn->session->tt->init_cmd_task(conn->ctask))
+- return EIO;
+-
+ conn->scsicmd_pdus_cnt++;
+- debug_scsi("iscsi prep [%s cid %d sc %p cdb 0x%x itt 0x%x "
+- "len %d bidi_len %d cmdsn %d win %d]\n",
+- scsi_bidi_cmnd(sc) ? "bidirectional" :
+- sc->sc_data_direction == DMA_TO_DEVICE ? "write" : "read",
+- conn->id, sc, sc->cmnd[0], ctask->itt,
+- scsi_bufflen(sc), scsi_bidi_cmnd(sc) ? scsi_in(sc)->length : 0,
+- session->cmdsn, session->max_cmdsn - session->exp_cmdsn + 1);
+- return 0;
+ }
++EXPORT_SYMBOL_GPL(iscsi_prep_scsi_cmd_pdu);
+
+ /**
+ * iscsi_complete_command - return command back to scsi-ml
+@@ -330,16 +187,13 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)
+ */
+ static void iscsi_complete_command(struct iscsi_cmd_task *ctask)
+ {
+- struct iscsi_conn *conn = ctask->conn;
+- struct iscsi_session *session = conn->session;
++ 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;
+- if (conn->ctask == ctask)
+- conn->ctask = NULL;
+ list_del_init(&ctask->running);
+ __kfifo_put(session->cmdpool.queue, (void*)&ctask, sizeof(void*));
+ sc->scsi_done(sc);
+@@ -350,124 +204,27 @@ 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);
+ }
+
+-/*
+- * session lock must be held
+- */
+-static void fail_command(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
+- int err)
++static void iscsi_put_ctask(struct iscsi_cmd_task *ctask)
+ {
+- struct scsi_cmnd *sc;
+-
+- sc = ctask->sc;
+- if (!sc)
+- return;
+-
+- if (ctask->state == ISCSI_TASK_PENDING)
+- /*
+- * cmd never made it to the xmit thread, so we should not count
+- * the cmd in the sequencing
+- */
+- conn->session->queued_cmdsn--;
+- else
+- conn->session->tt->cleanup_cmd_task(conn, ctask);
+-
+- sc->result = err;
+- if (!scsi_bidi_cmnd(sc))
+- scsi_set_resid(sc, scsi_bufflen(sc));
+- else {
+- scsi_out(sc)->resid = scsi_out(sc)->length;
+- scsi_in(sc)->resid = scsi_in(sc)->length;
+- }
+- if (conn->ctask == ctask)
+- conn->ctask = NULL;
+- /* release ref from queuecommand */
++ spin_lock_bh(&ctask->conn->session->lock);
+ __iscsi_put_ctask(ctask);
++ spin_unlock_bh(&ctask->conn->session->lock);
+ }
+
+ /**
+- * iscsi_free_mgmt_task - return mgmt task back to pool
+- * @conn: iscsi connection
+- * @mtask: mtask
+- *
+- * Must be called with session lock.
+- */
+-void iscsi_free_mgmt_task(struct iscsi_conn *conn,
+- struct iscsi_mgmt_task *mtask)
+-{
+- list_del_init(&mtask->running);
+- if (conn->login_mtask == mtask)
+- return;
+-
+- if (conn->ping_mtask == mtask)
+- conn->ping_mtask = NULL;
+- __kfifo_put(conn->session->mgmtpool.queue,
+- (void*)&mtask, sizeof(void*));
+-}
+-EXPORT_SYMBOL_GPL(iscsi_free_mgmt_task);
+-
+-static struct iscsi_mgmt_task *
+-__iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+- char *data, uint32_t data_size)
+-{
+- struct iscsi_session *session = conn->session;
+- struct iscsi_mgmt_task *mtask;
+-
+- if (session->state == ISCSI_STATE_TERMINATE)
+- return NULL;
+-
+- 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);
+-
+- if (!__kfifo_get(session->mgmtpool.queue,
+- (void*)&mtask, sizeof(void*)))
+- return NULL;
+- }
+-
+- if (data_size) {
+- memcpy(mtask->data, data, data_size);
+- mtask->data_count = data_size;
+- } else
+- mtask->data_count = 0;
+-
+- memcpy(mtask->hdr, hdr, sizeof(struct iscsi_hdr));
+- INIT_LIST_HEAD(&mtask->running);
+- list_add_tail(&mtask->running, &conn->mgmtqueue);
+- return mtask;
+-}
+-
+-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;
+- struct iscsi_session *session = conn->session;
+- int err = 0;
+-
+- spin_lock_bh(&session->lock);
+- if (!__iscsi_conn_send_pdu(conn, hdr, data, data_size))
+- err = -EPERM;
+- spin_unlock_bh(&session->lock);
+- scsi_queue_work(session->host, &conn->xmitwork);
+- return err;
+-}
+-EXPORT_SYMBOL_GPL(iscsi_conn_send_pdu);
+-
+-/**
+ * iscsi_cmd_rsp - SCSI Command Response processing
+ * @conn: iscsi connection
+ * @hdr: iscsi header
+@@ -478,15 +235,21 @@ EXPORT_SYMBOL_GPL(iscsi_conn_send_pdu);
+ * iscsi_cmd_rsp sets up the scsi_cmnd fields based on the PDU and
+ * then completes the command and task.
+ **/
+-static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+- struct iscsi_cmd_task *ctask, char *data,
+- int datalen)
++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;
+
+- iscsi_update_cmdsn(session, (struct iscsi_nopin*)rhdr);
++ 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;
+@@ -501,9 +264,8 @@ static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+
+ if (datalen < 2) {
+ invalid_datalen:
+- iscsi_conn_printk(KERN_ERR, conn,
+- "Got CHECK_CONDITION but invalid data "
+- "buffer size of %d\n", 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;
+ }
+@@ -518,36 +280,28 @@ invalid_datalen:
+ min_t(uint16_t, senselen, SCSI_SENSE_BUFFERSIZE));
+ }
+
+- if (rhdr->flags & (ISCSI_FLAG_CMD_BIDI_UNDERFLOW |
+- ISCSI_FLAG_CMD_BIDI_OVERFLOW)) {
+- int res_count = be32_to_cpu(rhdr->bi_residual_count);
+-
+- if (scsi_bidi_cmnd(sc) && res_count > 0 &&
+- (rhdr->flags & ISCSI_FLAG_CMD_BIDI_OVERFLOW ||
+- res_count <= scsi_in(sc)->length))
+- scsi_in(sc)->resid = res_count;
+- else
+- sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status;
+- }
++ if (sc->sc_data_direction == DMA_TO_DEVICE)
++ goto out;
+
+- if (rhdr->flags & (ISCSI_FLAG_CMD_UNDERFLOW |
+- ISCSI_FLAG_CMD_OVERFLOW)) {
++ if (rhdr->flags & ISCSI_FLAG_CMD_UNDERFLOW) {
+ int res_count = be32_to_cpu(rhdr->residual_count);
+
+- if (res_count > 0 &&
+- (rhdr->flags & ISCSI_FLAG_CMD_OVERFLOW ||
+- res_count <= scsi_bufflen(sc)))
+- /* write side for bidi or uni-io set_resid */
+- scsi_set_resid(sc, res_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)
+@@ -557,51 +311,18 @@ static void iscsi_tmf_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
+ conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1;
+ conn->tmfrsp_pdus_cnt++;
+
+- if (conn->tmf_state != TMF_QUEUED)
++ if (conn->tmabort_state != TMABORT_INITIAL)
+ return;
+
+ if (tmf->response == ISCSI_TMF_RSP_COMPLETE)
+- conn->tmf_state = TMF_SUCCESS;
++ conn->tmabort_state = TMABORT_SUCCESS;
+ else if (tmf->response == ISCSI_TMF_RSP_NO_TASK)
+- conn->tmf_state = TMF_NOT_FOUND;
++ conn->tmabort_state = TMABORT_NOT_FOUND;
+ else
+- conn->tmf_state = TMF_FAILED;
++ conn->tmabort_state = TMABORT_FAILED;
+ wake_up(&conn->ehwait);
+ }
+
+-static void iscsi_send_nopout(struct iscsi_conn *conn, struct iscsi_nopin *rhdr)
+-{
+- struct iscsi_nopout hdr;
+- struct iscsi_mgmt_task *mtask;
+-
+- if (!rhdr && conn->ping_mtask)
+- return;
+-
+- memset(&hdr, 0, sizeof(struct iscsi_nopout));
+- hdr.opcode = ISCSI_OP_NOOP_OUT | ISCSI_OP_IMMEDIATE;
+- hdr.flags = ISCSI_FLAG_CMD_FINAL;
+-
+- if (rhdr) {
+- memcpy(hdr.lun, rhdr->lun, 8);
+- hdr.ttt = rhdr->ttt;
+- hdr.itt = RESERVED_ITT;
+- } else
+- hdr.ttt = RESERVED_ITT;
+-
+- mtask = __iscsi_conn_send_pdu(conn, (struct iscsi_hdr *)&hdr, NULL, 0);
+- if (!mtask) {
+- iscsi_conn_printk(KERN_ERR, conn, "Could not send nopout\n");
+- return;
+- }
+-
+- /* only track our nops */
+- if (!rhdr) {
+- conn->ping_mtask = mtask;
+- conn->last_ping = jiffies;
+- }
+- scsi_queue_work(conn->session->host, &conn->xmitwork);
+-}
+-
+ static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ char *data, int datalen)
+ {
+@@ -618,10 +339,9 @@ static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ if (ntoh24(reject->dlength) >= sizeof(struct iscsi_hdr)) {
+ memcpy(&rejected_pdu, data, sizeof(struct iscsi_hdr));
+ itt = get_itt(rejected_pdu.itt);
+- iscsi_conn_printk(KERN_ERR, conn,
+- "itt 0x%x had pdu (op 0x%x) rejected "
+- "due to DataDigest error.\n", itt,
+- rejected_pdu.opcode);
++ printk(KERN_ERR "itt 0x%x had pdu (op 0x%x) rejected "
++ "due to DataDigest error.\n", itt,
++ rejected_pdu.opcode);
+ }
+ }
+ return 0;
+@@ -638,8 +358,8 @@ static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ * queuecommand or send generic. session lock must be held and verify
+ * itt must have been called.
+ */
+-static int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+- char *data, int datalen)
++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;
+@@ -647,7 +367,6 @@ static int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ struct iscsi_mgmt_task *mtask;
+ uint32_t itt;
+
+- conn->last_recv = jiffies;
+ if (hdr->itt != RESERVED_ITT)
+ itt = get_itt(hdr->itt);
+ else
+@@ -662,8 +381,8 @@ static int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ switch(opcode) {
+ case ISCSI_OP_SCSI_CMD_RSP:
+ BUG_ON((void*)ctask != ctask->sc->SCp.ptr);
+- iscsi_scsi_cmd_rsp(conn, hdr, ctask, data,
+- datalen);
++ 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);
+@@ -686,7 +405,11 @@ static int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ debug_scsi("immrsp [op 0x%x cid %d itt 0x%x len %d]\n",
+ opcode, conn->id, mtask->itt, datalen);
+
+- iscsi_update_cmdsn(session, (struct iscsi_nopin*)hdr);
++ rc = iscsi_check_assign_cmdsn(session,
++ (struct iscsi_nopin*)hdr);
++ if (rc)
++ goto done;
++
+ switch(opcode) {
+ case ISCSI_OP_LOGOUT_RSP:
+ if (datalen) {
+@@ -703,7 +426,10 @@ static int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ */
+ if (iscsi_recv_pdu(conn->cls_conn, hdr, data, datalen))
+ rc = ISCSI_ERR_CONN_FAILED;
+- iscsi_free_mgmt_task(conn, mtask);
++ 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) {
+@@ -712,35 +438,30 @@ static int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ }
+
+ iscsi_tmf_rsp(conn, hdr);
+- iscsi_free_mgmt_task(conn, mtask);
+ break;
+ case ISCSI_OP_NOOP_IN:
+- if (hdr->ttt != cpu_to_be32(ISCSI_RESERVED_TAG) ||
+- datalen) {
++ 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 (conn->ping_mtask != mtask) {
+- /*
+- * If this is not in response to one of our
+- * nops then it must be from userspace.
+- */
+- if (iscsi_recv_pdu(conn->cls_conn, hdr, data,
+- datalen))
+- rc = ISCSI_ERR_CONN_FAILED;
+- } else
+- mod_timer(&conn->transport_timer,
+- jiffies + conn->recv_timeout);
+- iscsi_free_mgmt_task(conn, mtask);
++ 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) {
+- iscsi_update_cmdsn(session, (struct iscsi_nopin*)hdr);
++ rc = iscsi_check_assign_cmdsn(session,
++ (struct iscsi_nopin*)hdr);
++ if (rc)
++ goto done;
+
+ switch(opcode) {
+ case ISCSI_OP_NOOP_IN:
+@@ -752,7 +473,8 @@ static int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ if (hdr->ttt == cpu_to_be32(ISCSI_RESERVED_TAG))
+ break;
+
+- iscsi_send_nopout(conn, (struct iscsi_nopin*)hdr);
++ 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);
+@@ -769,8 +491,10 @@ static int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ } 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)
+@@ -795,13 +519,18 @@ int iscsi_verify_itt(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ if (hdr->itt != RESERVED_ITT) {
+ if (((__force u32)hdr->itt & ISCSI_AGE_MASK) !=
+ (session->age << ISCSI_AGE_SHIFT)) {
+- iscsi_conn_printk(KERN_ERR, conn,
+- "received itt %x expected session "
+- "age (%x)\n", (__force u32)hdr->itt,
+- session->age & ISCSI_AGE_MASK);
++ 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;
+@@ -810,17 +539,16 @@ int iscsi_verify_itt(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ ctask = session->cmds[itt];
+
+ if (!ctask->sc) {
+- iscsi_conn_printk(KERN_INFO, conn, "dropping ctask "
+- "with itt 0x%x\n", ctask->itt);
++ 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) {
+- iscsi_conn_printk(KERN_ERR, conn,
+- "iscsi: ctask's session age %d, "
+- "expected %d\n", 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;
+ }
+ }
+@@ -850,110 +578,30 @@ void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err)
+ }
+ EXPORT_SYMBOL_GPL(iscsi_conn_failure);
+
+-static void iscsi_prep_mtask(struct iscsi_conn *conn,
+- struct iscsi_mgmt_task *mtask)
+-{
+- struct iscsi_session *session = conn->session;
+- struct iscsi_hdr *hdr = mtask->hdr;
+- struct iscsi_nopout *nop = (struct iscsi_nopout *)hdr;
+-
+- if (hdr->opcode != (ISCSI_OP_LOGIN | ISCSI_OP_IMMEDIATE) &&
+- hdr->opcode != (ISCSI_OP_TEXT | ISCSI_OP_IMMEDIATE))
+- nop->exp_statsn = cpu_to_be32(conn->exp_statsn);
+- /*
+- * pre-format CmdSN for outgoing PDU.
+- */
+- nop->cmdsn = cpu_to_be32(session->cmdsn);
+- if (hdr->itt != RESERVED_ITT) {
+- hdr->itt = build_itt(mtask->itt, session->age);
+- /*
+- * TODO: We always use immediate, so we never hit this.
+- * If we start to send tmfs or nops as non-immediate then
+- * we should start checking the cmdsn numbers for mgmt tasks.
+- */
+- if (conn->c_stage == ISCSI_CONN_STARTED &&
+- !(hdr->opcode & ISCSI_OP_IMMEDIATE)) {
+- session->queued_cmdsn++;
+- session->cmdsn++;
+- }
+- }
+-
+- if (session->tt->init_mgmt_task)
+- session->tt->init_mgmt_task(conn, mtask);
+-
+- debug_scsi("mgmtpdu [op 0x%x hdr->itt 0x%x datalen %d]\n",
+- hdr->opcode & ISCSI_OPCODE_MASK, hdr->itt,
+- mtask->data_count);
+-}
+-
+ static int iscsi_xmit_mtask(struct iscsi_conn *conn)
+ {
+ struct iscsi_hdr *hdr = conn->mtask->hdr;
+- int rc;
+-
+- if ((hdr->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_LOGOUT)
+- conn->session->state = ISCSI_STATE_LOGGING_OUT;
+- spin_unlock_bh(&conn->session->lock);
++ 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);
+- spin_lock_bh(&conn->session->lock);
+ if (rc)
+ return rc;
+
+ /* done with this in-progress mtask */
+ conn->mtask = NULL;
+- return 0;
+-}
+-
+-static int iscsi_check_cmdsn_window_closed(struct iscsi_conn *conn)
+-{
+- struct iscsi_session *session = conn->session;
+
+- /*
+- * Check for iSCSI window and take care of CmdSN wrap-around
+- */
+- if (!iscsi_sna_lte(session->queued_cmdsn, session->max_cmdsn)) {
+- debug_scsi("iSCSI CmdSN closed. ExpCmdSn %u MaxCmdSN %u "
+- "CmdSN %u/%u\n", session->exp_cmdsn,
+- session->max_cmdsn, session->cmdsn,
+- session->queued_cmdsn);
+- return -ENOSPC;
++ if (was_logout) {
++ set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
++ return -ENODATA;
+ }
+ return 0;
+ }
+
+-static int iscsi_xmit_ctask(struct iscsi_conn *conn)
+-{
+- struct iscsi_cmd_task *ctask = conn->ctask;
+- int rc;
+-
+- __iscsi_get_ctask(ctask);
+- spin_unlock_bh(&conn->session->lock);
+- rc = conn->session->tt->xmit_cmd_task(conn, ctask);
+- spin_lock_bh(&conn->session->lock);
+- __iscsi_put_ctask(ctask);
+- if (!rc)
+- /* done with this ctask */
+- conn->ctask = NULL;
+- return rc;
+-}
+-
+-/**
+- * iscsi_requeue_ctask - requeue ctask to run from session workqueue
+- * @ctask: ctask to requeue
+- *
+- * LLDs that need to run a ctask from the session workqueue should call
+- * this. The session lock must be held.
+- */
+-void iscsi_requeue_ctask(struct iscsi_cmd_task *ctask)
+-{
+- struct iscsi_conn *conn = ctask->conn;
+-
+- list_move_tail(&ctask->running, &conn->requeue);
+- scsi_queue_work(conn->session->host, &conn->xmitwork);
+-}
+-EXPORT_SYMBOL_GPL(iscsi_requeue_ctask);
+-
+ /**
+ * iscsi_data_xmit - xmit any command into the scheduled connection
+ * @conn: iscsi connection
+@@ -965,106 +613,106 @@ EXPORT_SYMBOL_GPL(iscsi_requeue_ctask);
+ **/
+ static int iscsi_data_xmit(struct iscsi_conn *conn)
+ {
++ struct iscsi_transport *tt;
+ int rc = 0;
+
+- spin_lock_bh(&conn->session->lock);
+ if (unlikely(conn->suspend_tx)) {
+ debug_scsi("conn %d Tx suspended!\n", conn->id);
+- spin_unlock_bh(&conn->session->lock);
+ 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) {
+- rc = iscsi_xmit_ctask(conn);
++ 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 mgmt pdus like nops before commands since we should
+- * only have one nop-out as a ping from us and targets should not
+- * overflow us with nop-ins
+- */
+-check_mgmt:
+- while (!list_empty(&conn->mgmtqueue)) {
+- conn->mtask = list_entry(conn->mgmtqueue.next,
+- struct iscsi_mgmt_task, running);
+- if (conn->session->state == ISCSI_STATE_LOGGING_OUT) {
+- iscsi_free_mgmt_task(conn, conn->mtask);
+- conn->mtask = NULL;
+- continue;
+- }
+-
+- iscsi_prep_mtask(conn, conn->mtask);
+- list_move_tail(conn->mgmtqueue.next, &conn->mgmt_run_list);
+- 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 pending command queue */
++ /* process command queue */
++ spin_lock_bh(&conn->session->lock);
+ while (!list_empty(&conn->xmitqueue)) {
+- if (conn->tmf_state == TMF_QUEUED)
+- break;
+-
++ /*
++ * 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);
+- if (conn->session->state == ISCSI_STATE_LOGGING_OUT) {
+- fail_command(conn, conn->ctask, DID_IMM_RETRY << 16);
+- continue;
+- }
+- if (iscsi_prep_scsi_cmd_pdu(conn->ctask)) {
+- fail_command(conn, conn->ctask, DID_ABORT << 16);
+- continue;
+- }
+-
+ conn->ctask->state = ISCSI_TASK_RUNNING;
+ list_move_tail(conn->xmitqueue.next, &conn->run_list);
+- rc = iscsi_xmit_ctask(conn);
+- if (rc)
+- goto again;
+- /*
+- * we could continuously get new ctask requests so
+- * we need to check the mgmt queue for nops that need to
+- * be sent to aviod starvation
+- */
+- if (!list_empty(&conn->mgmtqueue))
+- goto check_mgmt;
+- }
++ __iscsi_get_ctask(conn->ctask);
++ spin_unlock_bh(&conn->session->lock);
+
+- while (!list_empty(&conn->requeue)) {
+- if (conn->session->fast_abort && conn->tmf_state != TMF_INITIAL)
+- break;
++ rc = tt->xmit_cmd_task(conn, conn->ctask);
+
+- /*
+- * we always do fastlogout - conn stop code will clean up.
+- */
+- if (conn->session->state == ISCSI_STATE_LOGGING_OUT)
+- break;
+-
+- conn->ctask = list_entry(conn->requeue.next,
+- struct iscsi_cmd_task, running);
+- conn->ctask->state = ISCSI_TASK_RUNNING;
+- list_move_tail(conn->requeue.next, &conn->run_list);
+- rc = iscsi_xmit_ctask(conn);
+- if (rc)
++ spin_lock_bh(&conn->session->lock);
++ __iscsi_put_ctask(conn->ctask);
++ if (rc) {
++ spin_unlock_bh(&conn->session->lock);
+ goto again;
+- if (!list_empty(&conn->mgmtqueue))
+- goto check_mgmt;
++ }
+ }
+ 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))
+- rc = -ENODATA;
+- spin_unlock_bh(&conn->session->lock);
++ return -ENODATA;
++
+ return rc;
+ }
+
+@@ -1076,9 +724,11 @@ static void iscsi_xmitworker(struct work_struct *work)
+ /*
+ * 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 {
+@@ -1090,8 +740,6 @@ enum {
+ FAILURE_SESSION_TERMINATE,
+ FAILURE_SESSION_IN_RECOVERY,
+ FAILURE_SESSION_RECOVERY_TIMEOUT,
+- FAILURE_SESSION_LOGGING_OUT,
+- FAILURE_SESSION_NOT_READY,
+ };
+
+ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
+@@ -1107,16 +755,9 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
+ sc->SCp.ptr = NULL;
+
+ host = sc->device->host;
+- spin_unlock(host->host_lock);
+-
+ session = iscsi_hostdata(host->hostdata);
+- spin_lock(&session->lock);
+
+- reason = iscsi_session_chkready(session_to_cls(session));
+- if (reason) {
+- sc->result = reason;
+- goto fault;
+- }
++ spin_lock(&session->lock);
+
+ /*
+ * ISCSI_STATE_FAILED is a temp. state. The recovery
+@@ -1131,82 +772,77 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
+ * be entering our queuecommand while a block is starting
+ * up because the block code is not locked)
+ */
+- switch (session->state) {
+- case ISCSI_STATE_IN_RECOVERY:
++ if (session->state == ISCSI_STATE_IN_RECOVERY) {
+ reason = FAILURE_SESSION_IN_RECOVERY;
+- sc->result = DID_IMM_RETRY << 16;
+- break;
+- case ISCSI_STATE_LOGGING_OUT:
+- reason = FAILURE_SESSION_LOGGING_OUT;
+- sc->result = DID_IMM_RETRY << 16;
+- break;
+- case ISCSI_STATE_RECOVERY_FAILED:
++ goto reject;
++ }
++
++ if (session->state == ISCSI_STATE_RECOVERY_FAILED)
+ reason = FAILURE_SESSION_RECOVERY_TIMEOUT;
+- sc->result = DID_NO_CONNECT << 16;
+- break;
+- case ISCSI_STATE_TERMINATE:
++ else if (session->state == ISCSI_STATE_TERMINATE)
+ reason = FAILURE_SESSION_TERMINATE;
+- sc->result = DID_NO_CONNECT << 16;
+- break;
+- default:
++ else
+ reason = FAILURE_SESSION_FREED;
+- sc->result = DID_NO_CONNECT << 16;
+- }
+ 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;
+- sc->result = DID_NO_CONNECT << 16;
+ goto fault;
+ }
+
+- if (iscsi_check_cmdsn_window_closed(conn)) {
+- reason = FAILURE_WINDOW_CLOSED;
+- goto reject;
+- }
+-
+ if (!__kfifo_get(session->cmdpool.queue, (void*)&ctask,
+ sizeof(void*))) {
+ reason = FAILURE_OOM;
+ goto reject;
+ }
+- session->queued_cmdsn++;
+-
+ 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);
+- spin_lock(host->host_lock);
+ return 0;
+
+ reject:
+ spin_unlock(&session->lock);
+ debug_scsi("cmd 0x%x rejected (%d)\n", sc->cmnd[0], reason);
+- spin_lock(host->host_lock);
+ return SCSI_MLQUEUE_HOST_BUSY;
+
+ fault:
+ spin_unlock(&session->lock);
+- debug_scsi("iscsi: cmd 0x%x is not queued (%d)\n", sc->cmnd[0], reason);
+- if (!scsi_bidi_cmnd(sc))
+- scsi_set_resid(sc, scsi_bufflen(sc));
+- else {
+- scsi_out(sc)->resid = scsi_out(sc)->length;
+- scsi_in(sc)->resid = scsi_in(sc)->length;
+- }
++ 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);
+- spin_lock(host->host_lock);
+ return 0;
+ }
+ EXPORT_SYMBOL_GPL(iscsi_queuecommand);
+@@ -1220,15 +856,106 @@ int iscsi_change_queue_depth(struct scsi_device *sdev, int 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 (session->leadconn)
+- wake_up(&session->leadconn->ehwait);
++ if (conn)
++ wake_up(&conn->ehwait);
+ }
+ spin_unlock_bh(&session->lock);
+ }
+@@ -1239,25 +966,30 @@ 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;
+
+- mutex_lock(&session->eh_mutex);
+ 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);
+- mutex_unlock(&session->eh_mutex);
+ 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);
+- mutex_unlock(&session->eh_mutex);
++
+ /*
+ * we drop the lock here but the leadconn cannot be destoyed while
+ * we are in the scsi eh
+ */
+- iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
++ 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,
+@@ -1267,496 +999,324 @@ failed:
+ if (signal_pending(current))
+ flush_signals(current);
+
+- mutex_lock(&session->eh_mutex);
+ spin_lock_bh(&session->lock);
+ if (session->state == ISCSI_STATE_LOGGED_IN)
+- iscsi_session_printk(KERN_INFO, session,
+- "host reset succeeded\n");
++ printk(KERN_INFO "iscsi: host reset succeeded\n");
+ else
+ goto failed;
+ spin_unlock_bh(&session->lock);
+- mutex_unlock(&session->eh_mutex);
++
+ return SUCCESS;
+ }
+ EXPORT_SYMBOL_GPL(iscsi_eh_host_reset);
+
+-static void iscsi_tmf_timedout(unsigned long data)
++static void iscsi_tmabort_timedout(unsigned long data)
+ {
+- struct iscsi_conn *conn = (struct iscsi_conn *)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->tmf_state == TMF_QUEUED) {
+- conn->tmf_state = TMF_TIMEDOUT;
+- debug_scsi("tmf timedout\n");
++ 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);
+ }
+
+-static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn,
+- struct iscsi_tm *hdr, int age,
+- int timeout)
++/* 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_mgmt_task *mtask;
++ struct iscsi_tm *hdr = &conn->tmhdr;
++ int rc;
+
+- mtask = __iscsi_conn_send_pdu(conn, (struct iscsi_hdr *)hdr,
+- NULL, 0);
+- if (!mtask) {
+- spin_unlock_bh(&session->lock);
++ /*
++ * 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);
+- spin_lock_bh(&session->lock);
+- debug_scsi("tmf exec failure\n");
+- return -EPERM;
++ debug_scsi("abort sent failure [itt 0x%x] %d\n", ctask->itt,
++ rc);
++ return rc;
+ }
+- conn->tmfcmd_pdus_cnt++;
+- conn->tmf_timer.expires = timeout * HZ + jiffies;
+- conn->tmf_timer.function = iscsi_tmf_timedout;
+- conn->tmf_timer.data = (unsigned long)conn;
+- add_timer(&conn->tmf_timer);
+- debug_scsi("tmf set timeout\n");
+
++ 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(&session->eh_mutex);
+- scsi_queue_work(session->host, &conn->xmitwork);
++ mutex_unlock(&conn->xmitmutex);
+
+ /*
+ * block eh thread until:
+ *
+- * 1) tmf response
+- * 2) tmf timeout
++ * 1) abort response
++ * 2) abort timeout
+ * 3) session is terminated or restarted or userspace has
+ * given up on recovery
+ */
+- wait_event_interruptible(conn->ehwait, age != session->age ||
++ wait_event_interruptible(conn->ehwait,
++ sc->SCp.phase != session->age ||
+ session->state != ISCSI_STATE_LOGGED_IN ||
+- conn->tmf_state != TMF_QUEUED);
++ conn->tmabort_state != TMABORT_INITIAL);
+ if (signal_pending(current))
+ flush_signals(current);
+- del_timer_sync(&conn->tmf_timer);
++ del_timer_sync(&conn->tmabort_timer);
+
+- mutex_lock(&session->eh_mutex);
+- spin_lock_bh(&session->lock);
+- /* if the session drops it will clean up the mtask */
+- if (age != session->age ||
+- session->state != ISCSI_STATE_LOGGED_IN)
+- return -ENOTCONN;
++ mutex_lock(&conn->xmitmutex);
+ return 0;
+ }
+
+ /*
+- * Fail commands. session lock held and recv side suspended and xmit
+- * thread flushed
++ * xmit mutex and session lock must be held
+ */
+-static void fail_all_commands(struct iscsi_conn *conn, unsigned lun,
+- int error)
++static struct iscsi_mgmt_task *
++iscsi_remove_mgmt_task(struct kfifo *fifo, uint32_t itt)
+ {
+- struct iscsi_cmd_task *ctask, *tmp;
++ int i, nr_tasks = __kfifo_len(fifo) / sizeof(void*);
++ struct iscsi_mgmt_task *task;
+
+- if (conn->ctask && (conn->ctask->sc->device->lun == lun || lun == -1))
+- conn->ctask = NULL;
++ debug_scsi("searching %d tasks\n", nr_tasks);
+
+- /* flush pending */
+- list_for_each_entry_safe(ctask, tmp, &conn->xmitqueue, running) {
+- if (lun == ctask->sc->device->lun || lun == -1) {
+- debug_scsi("failing pending sc %p itt 0x%x\n",
+- ctask->sc, ctask->itt);
+- fail_command(conn, ctask, error << 16);
+- }
+- }
++ for (i = 0; i < nr_tasks; i++) {
++ __kfifo_get(fifo, (void*)&task, sizeof(void*));
++ debug_scsi("check task %u\n", task->itt);
+
+- list_for_each_entry_safe(ctask, tmp, &conn->requeue, running) {
+- if (lun == ctask->sc->device->lun || lun == -1) {
+- debug_scsi("failing requeued sc %p itt 0x%x\n",
+- ctask->sc, ctask->itt);
+- fail_command(conn, ctask, error << 16);
++ if (task->itt == itt) {
++ debug_scsi("matched task\n");
++ return task;
+ }
+- }
+
+- /* fail all other running */
+- list_for_each_entry_safe(ctask, tmp, &conn->run_list, running) {
+- if (lun == ctask->sc->device->lun || lun == -1) {
+- debug_scsi("failing in progress sc %p itt 0x%x\n",
+- ctask->sc, ctask->itt);
+- fail_command(conn, ctask, DID_BUS_BUSY << 16);
+- }
++ __kfifo_put(fifo, (void*)&task, sizeof(void*));
+ }
++ return NULL;
+ }
+
+-static void iscsi_suspend_tx(struct iscsi_conn *conn)
+-{
+- set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
+- scsi_flush_work(conn->session->host);
+-}
+-
+-static void iscsi_start_tx(struct iscsi_conn *conn)
+-{
+- clear_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
+- scsi_queue_work(conn->session->host, &conn->xmitwork);
+-}
+-
+-static enum scsi_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *scmd)
++static int iscsi_ctask_mtask_cleanup(struct iscsi_cmd_task *ctask)
+ {
+- struct iscsi_cls_session *cls_session;
+- struct iscsi_session *session;
+- struct iscsi_conn *conn;
+- enum scsi_eh_timer_return rc = EH_NOT_HANDLED;
+-
+- cls_session = starget_to_session(scsi_target(scmd->device));
+- session = class_to_transport_session(cls_session);
+-
+- debug_scsi("scsi cmd %p timedout\n", scmd);
+-
+- spin_lock(&session->lock);
+- if (session->state != ISCSI_STATE_LOGGED_IN) {
+- /*
+- * We are probably in the middle of iscsi recovery so let
+- * that complete and handle the error.
+- */
+- rc = EH_RESET_TIMER;
+- goto done;
+- }
++ struct iscsi_conn *conn = ctask->conn;
++ struct iscsi_session *session = conn->session;
+
+- conn = session->leadconn;
+- if (!conn) {
+- /* In the middle of shuting down */
+- rc = EH_RESET_TIMER;
+- goto done;
+- }
++ if (!ctask->mtask)
++ return -EINVAL;
+
+- if (!conn->recv_timeout && !conn->ping_timeout)
+- goto done;
+- /*
+- * if the ping timedout then we are in the middle of cleaning up
+- * and can let the iscsi eh handle it
+- */
+- if (time_before_eq(conn->last_recv + (conn->recv_timeout * HZ) +
+- (conn->ping_timeout * HZ), jiffies))
+- rc = EH_RESET_TIMER;
+- /*
+- * if we are about to check the transport then give the command
+- * more time
+- */
+- if (time_before_eq(conn->last_recv + (conn->recv_timeout * HZ),
+- jiffies))
+- rc = EH_RESET_TIMER;
+- /* if in the middle of checking the transport then give us more time */
+- if (conn->ping_mtask)
+- rc = EH_RESET_TIMER;
+-done:
+- spin_unlock(&session->lock);
+- debug_scsi("return %s\n", rc == EH_RESET_TIMER ? "timer reset" : "nh");
+- return rc;
++ 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;
+ }
+
+-static void iscsi_check_transport_timeouts(unsigned long data)
++/*
++ * session lock and xmitmutex must be held
++ */
++static void fail_command(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
++ int err)
+ {
+- struct iscsi_conn *conn = (struct iscsi_conn *)data;
+- struct iscsi_session *session = conn->session;
+- unsigned long recv_timeout, next_timeout = 0, last_recv;
++ struct scsi_cmnd *sc;
+
+- spin_lock(&session->lock);
+- if (session->state != ISCSI_STATE_LOGGED_IN)
+- goto done;
+-
+- recv_timeout = conn->recv_timeout;
+- if (!recv_timeout)
+- goto done;
+-
+- recv_timeout *= HZ;
+- last_recv = conn->last_recv;
+- if (conn->ping_mtask &&
+- time_before_eq(conn->last_ping + (conn->ping_timeout * HZ),
+- jiffies)) {
+- iscsi_conn_printk(KERN_ERR, conn, "ping timeout of %d secs "
+- "expired, last rx %lu, last ping %lu, "
+- "now %lu\n", conn->ping_timeout, last_recv,
+- conn->last_ping, jiffies);
+- spin_unlock(&session->lock);
+- iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
++ sc = ctask->sc;
++ if (!sc)
+ return;
+- }
+-
+- if (time_before_eq(last_recv + recv_timeout, jiffies)) {
+- /* send a ping to try to provoke some traffic */
+- debug_scsi("Sending nopout as ping on conn %p\n", conn);
+- iscsi_send_nopout(conn, NULL);
+- next_timeout = conn->last_ping + (conn->ping_timeout * HZ);
+- } else
+- next_timeout = last_recv + recv_timeout;
+
+- debug_scsi("Setting next tmo %lu\n", next_timeout);
+- mod_timer(&conn->transport_timer, next_timeout);
+-done:
+- spin_unlock(&session->lock);
+-}
++ conn->session->tt->cleanup_cmd_task(conn, ctask);
++ iscsi_ctask_mtask_cleanup(ctask);
+
+-static void iscsi_prep_abort_task_pdu(struct iscsi_cmd_task *ctask,
+- struct iscsi_tm *hdr)
+-{
+- memset(hdr, 0, sizeof(*hdr));
+- hdr->opcode = ISCSI_OP_SCSI_TMFUNC | ISCSI_OP_IMMEDIATE;
+- hdr->flags = ISCSI_TM_FUNC_ABORT_TASK & ISCSI_FLAG_TM_FUNC_MASK;
+- 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;
++ 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 Scsi_Host *host = sc->device->host;
+- struct iscsi_session *session = iscsi_hostdata(host->hostdata);
+- struct iscsi_conn *conn;
+ struct iscsi_cmd_task *ctask;
+- struct iscsi_tm *hdr;
+- int rc, age;
++ struct iscsi_conn *conn;
++ struct iscsi_session *session;
++ int rc;
+
+- mutex_lock(&session->eh_mutex);
+- spin_lock_bh(&session->lock);
+ /*
+ * 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");
+- spin_unlock_bh(&session->lock);
+- mutex_unlock(&session->eh_mutex);
+ 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->leadconn || session->state != ISCSI_STATE_LOGGED_IN ||
+- sc->SCp.phase != session->age) {
+- spin_unlock_bh(&session->lock);
+- mutex_unlock(&session->eh_mutex);
+- return FAILED;
+- }
+-
+- conn = session->leadconn;
+- conn->eh_abort_cnt++;
+- age = session->age;
+-
+- ctask = (struct iscsi_cmd_task *)sc->SCp.ptr;
+- debug_scsi("aborting [sc %p itt 0x%x]\n", sc, ctask->itt);
++ 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;
++ goto success_rel_mutex;
+ }
+
+- if (ctask->state == ISCSI_TASK_PENDING) {
+- fail_command(conn, ctask, DID_ABORT << 16);
+- goto success;
++ /* 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;
+ }
+
+- /* only have one tmf outstanding at a time */
+- if (conn->tmf_state != TMF_INITIAL)
+- goto failed;
+- conn->tmf_state = TMF_QUEUED;
++ if (ctask->state == ISCSI_TASK_PENDING)
++ goto success_cleanup;
+
+- hdr = &conn->tmhdr;
+- iscsi_prep_abort_task_pdu(ctask, hdr);
++ conn->tmabort_state = TMABORT_INITIAL;
+
+- if (iscsi_exec_task_mgmt_fn(conn, hdr, age, session->abort_timeout)) {
+- rc = FAILED;
++ 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->tmf_state) {
+- case TMF_SUCCESS:
+- spin_unlock_bh(&session->lock);
+- iscsi_suspend_tx(conn);
+- /*
+- * clean up task if aborted. grab the recv lock as a writer
+- */
+- write_lock_bh(conn->recv_lock);
+- spin_lock(&session->lock);
+- fail_command(conn, ctask, DID_ABORT << 16);
+- conn->tmf_state = TMF_INITIAL;
+- spin_unlock(&session->lock);
+- write_unlock_bh(conn->recv_lock);
+- iscsi_start_tx(conn);
+- goto success_unlocked;
+- case TMF_TIMEDOUT:
+- spin_unlock_bh(&session->lock);
+- iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
+- goto failed_unlocked;
+- case TMF_NOT_FOUND:
+- if (!sc->SCp.ptr) {
+- conn->tmf_state = TMF_INITIAL;
++ 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;
++ goto success_rel_mutex;
+ }
+ /* fall through */
+ default:
+- conn->tmf_state = TMF_INITIAL;
++ /* timedout or failed */
++ spin_unlock_bh(&session->lock);
++ iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
++ spin_lock_bh(&session->lock);
+ goto failed;
+ }
+
+-success:
+- spin_unlock_bh(&session->lock);
+-success_unlocked:
++success_cleanup:
+ debug_scsi("abort success [sc %lx itt 0x%x]\n", (long)sc, ctask->itt);
+- mutex_unlock(&session->eh_mutex);
+- return SUCCESS;
+-
+-failed:
+ spin_unlock_bh(&session->lock);
+-failed_unlocked:
+- debug_scsi("abort failed [sc %p itt 0x%x]\n", sc,
+- ctask ? ctask->itt : 0);
+- mutex_unlock(&session->eh_mutex);
+- return FAILED;
+-}
+-EXPORT_SYMBOL_GPL(iscsi_eh_abort);
+
+-static void iscsi_prep_lun_reset_pdu(struct scsi_cmnd *sc, struct iscsi_tm *hdr)
+-{
+- memset(hdr, 0, sizeof(*hdr));
+- hdr->opcode = ISCSI_OP_SCSI_TMFUNC | ISCSI_OP_IMMEDIATE;
+- hdr->flags = ISCSI_TM_FUNC_LOGICAL_UNIT_RESET & ISCSI_FLAG_TM_FUNC_MASK;
+- hdr->flags |= ISCSI_FLAG_CMD_FINAL;
+- int_to_scsilun(sc->device->lun, (struct scsi_lun *)hdr->lun);
+- hdr->rtt = RESERVED_ITT;
+-}
+-
+-int iscsi_eh_device_reset(struct scsi_cmnd *sc)
+-{
+- struct Scsi_Host *host = sc->device->host;
+- struct iscsi_session *session = iscsi_hostdata(host->hostdata);
+- struct iscsi_conn *conn;
+- struct iscsi_tm *hdr;
+- int rc = FAILED;
+-
+- debug_scsi("LU Reset [sc %p lun %u]\n", sc, sc->device->lun);
+-
+- mutex_lock(&session->eh_mutex);
+- spin_lock_bh(&session->lock);
+ /*
+- * Just check if we are not logged in. We cannot check for
+- * the phase because the reset could come from a ioctl.
++ * clean up task if aborted. we have the xmitmutex so grab
++ * the recv lock as a writer
+ */
+- if (!session->leadconn || session->state != ISCSI_STATE_LOGGED_IN)
+- goto unlock;
+- conn = session->leadconn;
+-
+- /* only have one tmf outstanding at a time */
+- if (conn->tmf_state != TMF_INITIAL)
+- goto unlock;
+- conn->tmf_state = TMF_QUEUED;
+-
+- hdr = &conn->tmhdr;
+- iscsi_prep_lun_reset_pdu(sc, hdr);
+-
+- if (iscsi_exec_task_mgmt_fn(conn, hdr, session->age,
+- session->lu_reset_timeout)) {
+- rc = FAILED;
+- goto unlock;
+- }
+-
+- switch (conn->tmf_state) {
+- case TMF_SUCCESS:
+- break;
+- case TMF_TIMEDOUT:
+- spin_unlock_bh(&session->lock);
+- iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
+- goto done;
+- default:
+- conn->tmf_state = TMF_INITIAL;
+- goto unlock;
+- }
+-
+- rc = SUCCESS;
+- spin_unlock_bh(&session->lock);
+-
+- iscsi_suspend_tx(conn);
+- /* need to grab the recv lock then session lock */
+ write_lock_bh(conn->recv_lock);
+ spin_lock(&session->lock);
+- fail_all_commands(conn, sc->device->lun, DID_ERROR);
+- conn->tmf_state = TMF_INITIAL;
++ fail_command(conn, ctask, DID_ABORT << 16);
+ spin_unlock(&session->lock);
+ write_unlock_bh(conn->recv_lock);
+
+- iscsi_start_tx(conn);
+- goto done;
++success_rel_mutex:
++ mutex_unlock(&conn->xmitmutex);
++ return SUCCESS;
+
+-unlock:
++failed:
+ spin_unlock_bh(&session->lock);
+-done:
+- debug_scsi("iscsi_eh_device_reset %s\n",
+- rc == SUCCESS ? "SUCCESS" : "FAILED");
+- mutex_unlock(&session->eh_mutex);
+- return rc;
++ 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_device_reset);
++EXPORT_SYMBOL_GPL(iscsi_eh_abort);
+
+-/*
+- * Pre-allocate a pool of @max items of @item_size. By default, the pool
+- * should be accessed via kfifo_{get,put} on q->queue.
+- * Optionally, the caller can obtain the array of object pointers
+- * by passing in a non-NULL @items pointer
+- */
+ int
+-iscsi_pool_init(struct iscsi_pool *q, int max, void ***items, int item_size)
++iscsi_pool_init(struct iscsi_queue *q, int max, void ***items, int item_size)
+ {
+- int i, num_arrays = 1;
++ int i;
+
+- memset(q, 0, sizeof(*q));
++ *items = kmalloc(max * sizeof(void*), GFP_KERNEL);
++ if (*items == NULL)
++ return -ENOMEM;
+
+ q->max = max;
+-
+- /* If the user passed an items pointer, he wants a copy of
+- * the array. */
+- if (items)
+- num_arrays++;
+- q->pool = kzalloc(num_arrays * max * sizeof(void*), GFP_KERNEL);
+- if (q->pool == NULL)
+- goto enomem;
++ 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))
+- goto enomem;
++ if (q->queue == ERR_PTR(-ENOMEM)) {
++ kfree(q->pool);
++ kfree(*items);
++ return -ENOMEM;
++ }
+
+ for (i = 0; i < max; i++) {
+- q->pool[i] = kzalloc(item_size, GFP_KERNEL);
++ q->pool[i] = kmalloc(item_size, GFP_KERNEL);
+ if (q->pool[i] == NULL) {
+- q->max = i;
+- goto enomem;
++ 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*));
+ }
+-
+- if (items) {
+- *items = q->pool + max;
+- memcpy(*items, q->pool, max * sizeof(void *));
+- }
+-
+ return 0;
+-
+-enomem:
+- iscsi_pool_free(q);
+- return -ENOMEM;
+ }
+ EXPORT_SYMBOL_GPL(iscsi_pool_init);
+
+-void iscsi_pool_free(struct iscsi_pool *q)
++void iscsi_pool_free(struct iscsi_queue *q, void **items)
+ {
+ int i;
+
+ for (i = 0; i < q->max; i++)
+- kfree(q->pool[i]);
+- if (q->pool)
+- kfree(q->pool);
++ kfree(items[i]);
++ kfree(q->pool);
++ kfree(items);
+ }
+ EXPORT_SYMBOL_GPL(iscsi_pool_free);
+
+@@ -1779,10 +1339,6 @@ EXPORT_SYMBOL_GPL(iscsi_pool_free);
+ * iscsi_session_setup - create iscsi cls session and host and session
+ * @scsit: scsi transport template
+ * @iscsit: iscsi transport template
+- * @cmds_max: scsi host can queue
+- * @qdepth: scsi host cmds per lun
+- * @cmd_task_size: LLD ctask private data size
+- * @mgmt_task_size: LLD mtask private data size
+ * @initial_cmdsn: initial CmdSN
+ * @hostno: host no allocated
+ *
+@@ -1792,7 +1348,6 @@ EXPORT_SYMBOL_GPL(iscsi_pool_free);
+ struct iscsi_cls_session *
+ iscsi_session_setup(struct iscsi_transport *iscsit,
+ struct scsi_transport_template *scsit,
+- uint16_t cmds_max, uint16_t qdepth,
+ int cmd_task_size, int mgmt_task_size,
+ uint32_t initial_cmdsn, uint32_t *hostno)
+ {
+@@ -1801,56 +1356,30 @@ iscsi_session_setup(struct iscsi_transport *iscsit,
+ struct iscsi_cls_session *cls_session;
+ int cmd_i;
+
+- if (qdepth > ISCSI_MAX_CMD_PER_LUN || qdepth < 1) {
+- if (qdepth != 0)
+- printk(KERN_ERR "iscsi: invalid queue depth of %d. "
+- "Queue depth must be between 1 and %d.\n",
+- qdepth, ISCSI_MAX_CMD_PER_LUN);
+- qdepth = ISCSI_DEF_CMD_PER_LUN;
+- }
+-
+- if (!is_power_of_2(cmds_max) || cmds_max >= ISCSI_MGMT_ITT_OFFSET ||
+- cmds_max < 2) {
+- if (cmds_max != 0)
+- printk(KERN_ERR "iscsi: invalid can_queue of %d. "
+- "can_queue must be a power of 2 and between "
+- "2 and %d - setting to %d.\n", cmds_max,
+- ISCSI_MGMT_ITT_OFFSET, ISCSI_DEF_XMIT_CMDS_MAX);
+- cmds_max = ISCSI_DEF_XMIT_CMDS_MAX;
+- }
+-
+ shost = scsi_host_alloc(iscsit->host_template,
+ hostdata_privsize(sizeof(*session)));
+ if (!shost)
+ return NULL;
+
+- /* the iscsi layer takes one task for reserve */
+- shost->can_queue = cmds_max - 1;
+- shost->cmd_per_lun = qdepth;
+ 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;
+- shost->transportt->eh_timed_out = iscsi_eh_cmd_timed_out;
+ *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->fast_abort = 1;
+- session->lu_reset_timeout = 15;
+- session->abort_timeout = 10;
+ session->mgmtpool_max = ISCSI_MGMT_CMDS_MAX;
+- session->cmds_max = cmds_max;
+- session->queued_cmdsn = session->cmdsn = initial_cmdsn;
++ 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;
+- mutex_init(&session->eh_mutex);
+
+ /* initialize SCSI PDU commands pool */
+ if (iscsi_pool_init(&session->cmdpool, session->cmds_max,
+@@ -1905,9 +1434,9 @@ module_put:
+ cls_session_fail:
+ scsi_remove_host(shost);
+ add_host_fail:
+- iscsi_pool_free(&session->mgmtpool);
++ iscsi_pool_free(&session->mgmtpool, (void**)session->mgmt_cmds);
+ mgmtpool_alloc_fail:
+- iscsi_pool_free(&session->cmdpool);
++ iscsi_pool_free(&session->cmdpool, (void**)session->cmds);
+ cmdpool_alloc_fail:
+ scsi_host_put(shost);
+ return NULL;
+@@ -1927,22 +1456,14 @@ void iscsi_session_teardown(struct iscsi_cls_session *cls_session)
+ struct iscsi_session *session = iscsi_hostdata(shost->hostdata);
+ struct module *owner = cls_session->transport->owner;
+
+- iscsi_remove_session(cls_session);
+ scsi_remove_host(shost);
+
+- iscsi_pool_free(&session->mgmtpool);
+- iscsi_pool_free(&session->cmdpool);
++ iscsi_pool_free(&session->mgmtpool, (void**)session->mgmt_cmds);
++ iscsi_pool_free(&session->cmdpool, (void**)session->cmds);
+
+- kfree(session->password);
+- kfree(session->password_in);
+- kfree(session->username);
+- kfree(session->username_in);
+ kfree(session->targetname);
+- kfree(session->netdev);
+- kfree(session->hwaddress);
+- kfree(session->initiatorname);
+
+- iscsi_free_session(cls_session);
++ iscsi_destroy_session(cls_session);
+ scsi_host_put(shost);
+ module_put(owner);
+ }
+@@ -1972,17 +1493,22 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
+ conn->c_stage = ISCSI_CONN_INITIAL_STAGE;
+ conn->id = conn_idx;
+ conn->exp_statsn = 0;
+- conn->tmf_state = TMF_INITIAL;
+-
+- init_timer(&conn->transport_timer);
+- conn->transport_timer.data = (unsigned long)conn;
+- conn->transport_timer.function = iscsi_check_transport_timeouts;
+-
++ conn->tmabort_state = TMABORT_INITIAL;
+ INIT_LIST_HEAD(&conn->run_list);
+ INIT_LIST_HEAD(&conn->mgmt_run_list);
+- INIT_LIST_HEAD(&conn->mgmtqueue);
+ INIT_LIST_HEAD(&conn->xmitqueue);
+- INIT_LIST_HEAD(&conn->requeue);
++
++ /* 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 */
+@@ -2000,7 +1526,8 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
+ goto login_mtask_data_alloc_fail;
+ conn->login_mtask->data = conn->data = data;
+
+- init_timer(&conn->tmf_timer);
++ init_timer(&conn->tmabort_timer);
++ mutex_init(&conn->xmitmutex);
+ init_waitqueue_head(&conn->ehwait);
+
+ return cls_conn;
+@@ -2009,6 +1536,10 @@ 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;
+ }
+@@ -2027,7 +1558,8 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn)
+ struct iscsi_session *session = conn->session;
+ unsigned long flags;
+
+- del_timer_sync(&conn->transport_timer);
++ set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
++ mutex_lock(&conn->xmitmutex);
+
+ spin_lock_bh(&session->lock);
+ conn->c_stage = ISCSI_CONN_CLEANUP_WAIT;
+@@ -2040,6 +1572,8 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn)
+ }
+ spin_unlock_bh(&session->lock);
+
++ mutex_unlock(&conn->xmitmutex);
++
+ /*
+ * Block until all in-progress commands for this connection
+ * time out or fail.
+@@ -2052,10 +1586,9 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn)
+ }
+ spin_unlock_irqrestore(session->host->host_lock, flags);
+ msleep_interruptible(500);
+- iscsi_conn_printk(KERN_INFO, conn, "iscsi conn_destroy(): "
+- "host_busy %d host_failed %d\n",
+- session->host->host_busy,
+- session->host->host_failed);
++ 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
+ */
+@@ -2063,17 +1596,23 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn)
+ }
+
+ /* flush queued up work because we free the connection below */
+- iscsi_suspend_tx(conn);
++ 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)
++ 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);
+@@ -2084,41 +1623,21 @@ int iscsi_conn_start(struct iscsi_cls_conn *cls_conn)
+ struct iscsi_session *session = conn->session;
+
+ if (!session) {
+- iscsi_conn_printk(KERN_ERR, conn,
+- "can't start unbound connection\n");
++ 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) {
+- iscsi_conn_printk(KERN_INFO, conn, "invalid burst lengths: "
+- "first_burst %d max_burst %d\n",
+- 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;
+ }
+
+- if (conn->ping_timeout && !conn->recv_timeout) {
+- iscsi_conn_printk(KERN_ERR, conn, "invalid recv timeout of "
+- "zero. Using 5 seconds\n.");
+- conn->recv_timeout = 5;
+- }
+-
+- if (conn->recv_timeout && !conn->ping_timeout) {
+- iscsi_conn_printk(KERN_ERR, conn, "invalid ping timeout of "
+- "zero. Using 5 seconds.\n");
+- conn->ping_timeout = 5;
+- }
+-
+ spin_lock_bh(&session->lock);
+ conn->c_stage = ISCSI_CONN_STARTED;
+ session->state = ISCSI_STATE_LOGGED_IN;
+- session->queued_cmdsn = session->cmdsn;
+-
+- conn->last_recv = jiffies;
+- conn->last_ping = jiffies;
+- if (conn->recv_timeout && conn->ping_timeout)
+- mod_timer(&conn->transport_timer,
+- jiffies + (conn->recv_timeout * HZ));
+
+ switch(conn->stop_stage) {
+ case STOP_CONN_RECOVER:
+@@ -2127,11 +1646,13 @@ int iscsi_conn_start(struct iscsi_cls_conn *cls_conn)
+ * commands after successful recovery
+ */
+ conn->stop_stage = 0;
+- conn->tmf_state = TMF_INITIAL;
++ conn->tmabort_state = TMABORT_INITIAL;
+ session->age++;
+- if (session->age == 16)
+- session->age = 0;
+- break;
++ 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;
+@@ -2140,8 +1661,6 @@ int iscsi_conn_start(struct iscsi_cls_conn *cls_conn)
+ }
+ spin_unlock_bh(&session->lock);
+
+- iscsi_unblock_session(session_to_cls(session));
+- wake_up(&conn->ehwait);
+ return 0;
+ }
+ EXPORT_SYMBOL_GPL(iscsi_conn_start);
+@@ -2152,43 +1671,59 @@ flush_control_queues(struct iscsi_session *session, struct iscsi_conn *conn)
+ struct iscsi_mgmt_task *mtask, *tmp;
+
+ /* handle pending */
+- list_for_each_entry_safe(mtask, tmp, &conn->mgmtqueue, running) {
++ 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);
+- iscsi_free_mgmt_task(conn, mtask);
++ __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);
+- iscsi_free_mgmt_task(conn, mtask);
++ 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;
+
+- del_timer_sync(&conn->transport_timer);
+-
+- mutex_lock(&session->eh_mutex);
+ spin_lock_bh(&session->lock);
+ if (conn->stop_stage == STOP_CONN_TERM) {
+ spin_unlock_bh(&session->lock);
+- mutex_unlock(&session->eh_mutex);
+- return;
+- }
+-
+- /*
+- * The LLD either freed/unset the lock on us, or userspace called
+- * stop but did not create a proper connection (connection was never
+- * bound or it was unbound then stop was called).
+- */
+- if (!conn->recv_lock) {
+- spin_unlock_bh(&session->lock);
+- mutex_unlock(&session->eh_mutex);
+ return;
+ }
+
+@@ -2205,14 +1740,14 @@ static void iscsi_start_session_recovery(struct iscsi_session *session,
+ 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);
+
+- iscsi_suspend_tx(conn);
+-
+ 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
+@@ -2233,11 +1768,11 @@ static void iscsi_start_session_recovery(struct iscsi_session *session,
+ * flush queues.
+ */
+ spin_lock_bh(&session->lock);
+- fail_all_commands(conn, -1,
+- STOP_CONN_RECOVER ? DID_BUS_BUSY : DID_ERROR);
++ fail_all_commands(conn);
+ flush_control_queues(session, conn);
+ spin_unlock_bh(&session->lock);
+- mutex_unlock(&session->eh_mutex);
++
++ mutex_unlock(&conn->xmitmutex);
+ }
+
+ void iscsi_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
+@@ -2251,8 +1786,7 @@ void iscsi_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
+ iscsi_start_session_recovery(session, conn, flag);
+ break;
+ default:
+- iscsi_conn_printk(KERN_ERR, conn,
+- "invalid stop flag %d\n", flag);
++ printk(KERN_ERR "iscsi: invalid stop flag %d\n", flag);
+ }
+ }
+ EXPORT_SYMBOL_GPL(iscsi_conn_stop);
+@@ -2286,21 +1820,6 @@ int iscsi_set_param(struct iscsi_cls_conn *cls_conn,
+ uint32_t value;
+
+ switch(param) {
+- case ISCSI_PARAM_FAST_ABORT:
+- sscanf(buf, "%d", &session->fast_abort);
+- break;
+- case ISCSI_PARAM_ABORT_TMO:
+- sscanf(buf, "%d", &session->abort_timeout);
+- break;
+- case ISCSI_PARAM_LU_RESET_TMO:
+- sscanf(buf, "%d", &session->lu_reset_timeout);
+- break;
+- case ISCSI_PARAM_PING_TMO:
+- sscanf(buf, "%d", &conn->ping_timeout);
+- break;
+- case ISCSI_PARAM_RECV_TMO:
+- sscanf(buf, "%d", &conn->recv_timeout);
+- break;
+ case ISCSI_PARAM_MAX_RECV_DLENGTH:
+ sscanf(buf, "%d", &conn->max_recv_dlength);
+ break;
+@@ -2348,30 +1867,6 @@ int iscsi_set_param(struct iscsi_cls_conn *cls_conn,
+ case ISCSI_PARAM_EXP_STATSN:
+ sscanf(buf, "%u", &conn->exp_statsn);
+ break;
+- case ISCSI_PARAM_USERNAME:
+- kfree(session->username);
+- session->username = kstrdup(buf, GFP_KERNEL);
+- if (!session->username)
+- return -ENOMEM;
+- break;
+- case ISCSI_PARAM_USERNAME_IN:
+- kfree(session->username_in);
+- session->username_in = kstrdup(buf, GFP_KERNEL);
+- if (!session->username_in)
+- return -ENOMEM;
+- break;
+- case ISCSI_PARAM_PASSWORD:
+- kfree(session->password);
+- session->password = kstrdup(buf, GFP_KERNEL);
+- if (!session->password)
+- return -ENOMEM;
+- break;
+- case ISCSI_PARAM_PASSWORD_IN:
+- kfree(session->password_in);
+- session->password_in = kstrdup(buf, GFP_KERNEL);
+- if (!session->password_in)
+- return -ENOMEM;
+- break;
+ case ISCSI_PARAM_TARGET_NAME:
+ /* this should not change between logins */
+ if (session->targetname)
+@@ -2415,15 +1910,6 @@ int iscsi_session_get_param(struct iscsi_cls_session *cls_session,
+ int len;
+
+ switch(param) {
+- case ISCSI_PARAM_FAST_ABORT:
+- len = sprintf(buf, "%d\n", session->fast_abort);
+- break;
+- case ISCSI_PARAM_ABORT_TMO:
+- len = sprintf(buf, "%d\n", session->abort_timeout);
+- break;
+- case ISCSI_PARAM_LU_RESET_TMO:
+- len = sprintf(buf, "%d\n", session->lu_reset_timeout);
+- break;
+ case ISCSI_PARAM_INITIAL_R2T_EN:
+ len = sprintf(buf, "%d\n", session->initial_r2t_en);
+ break;
+@@ -2454,18 +1940,6 @@ int iscsi_session_get_param(struct iscsi_cls_session *cls_session,
+ case ISCSI_PARAM_TPGT:
+ len = sprintf(buf, "%d\n", session->tpgt);
+ break;
+- case ISCSI_PARAM_USERNAME:
+- len = sprintf(buf, "%s\n", session->username);
+- break;
+- case ISCSI_PARAM_USERNAME_IN:
+- len = sprintf(buf, "%s\n", session->username_in);
+- break;
+- case ISCSI_PARAM_PASSWORD:
+- len = sprintf(buf, "%s\n", session->password);
+- break;
+- case ISCSI_PARAM_PASSWORD_IN:
+- len = sprintf(buf, "%s\n", session->password_in);
+- break;
+ default:
+ return -ENOSYS;
+ }
+@@ -2481,12 +1955,6 @@ int iscsi_conn_get_param(struct iscsi_cls_conn *cls_conn,
+ int len;
+
+ switch(param) {
+- case ISCSI_PARAM_PING_TMO:
+- len = sprintf(buf, "%u\n", conn->ping_timeout);
+- break;
+- case ISCSI_PARAM_RECV_TMO:
+- len = sprintf(buf, "%u\n", conn->recv_timeout);
+- break;
+ case ISCSI_PARAM_MAX_RECV_DLENGTH:
+ len = sprintf(buf, "%u\n", conn->max_recv_dlength);
+ break;
+@@ -2522,66 +1990,6 @@ int iscsi_conn_get_param(struct iscsi_cls_conn *cls_conn,
+ }
+ EXPORT_SYMBOL_GPL(iscsi_conn_get_param);
+
+-int iscsi_host_get_param(struct Scsi_Host *shost, enum iscsi_host_param param,
+- char *buf)
+-{
+- struct iscsi_session *session = iscsi_hostdata(shost->hostdata);
+- int len;
+-
+- switch (param) {
+- case ISCSI_HOST_PARAM_NETDEV_NAME:
+- if (!session->netdev)
+- len = sprintf(buf, "%s\n", "default");
+- else
+- len = sprintf(buf, "%s\n", session->netdev);
+- break;
+- case ISCSI_HOST_PARAM_HWADDRESS:
+- if (!session->hwaddress)
+- len = sprintf(buf, "%s\n", "default");
+- else
+- len = sprintf(buf, "%s\n", session->hwaddress);
+- break;
+- case ISCSI_HOST_PARAM_INITIATOR_NAME:
+- if (!session->initiatorname)
+- len = sprintf(buf, "%s\n", "unknown");
+- else
+- len = sprintf(buf, "%s\n", session->initiatorname);
+- break;
+-
+- default:
+- return -ENOSYS;
+- }
+-
+- return len;
+-}
+-EXPORT_SYMBOL_GPL(iscsi_host_get_param);
+-
+-int iscsi_host_set_param(struct Scsi_Host *shost, enum iscsi_host_param param,
+- char *buf, int buflen)
+-{
+- struct iscsi_session *session = iscsi_hostdata(shost->hostdata);
+-
+- switch (param) {
+- case ISCSI_HOST_PARAM_NETDEV_NAME:
+- if (!session->netdev)
+- session->netdev = kstrdup(buf, GFP_KERNEL);
+- break;
+- case ISCSI_HOST_PARAM_HWADDRESS:
+- if (!session->hwaddress)
+- session->hwaddress = kstrdup(buf, GFP_KERNEL);
+- break;
+- case ISCSI_HOST_PARAM_INITIATOR_NAME:
+- if (!session->initiatorname)
+- session->initiatorname = kstrdup(buf, GFP_KERNEL);
+- break;
+- default:
+- return -ENOSYS;
+- }
+-
+- return 0;
+-}
+-EXPORT_SYMBOL_GPL(iscsi_host_set_param);
+-
+ MODULE_AUTHOR("Mike Christie");
+ MODULE_DESCRIPTION("iSCSI library functions");
+ MODULE_LICENSE("GPL");
+diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c
+index 65d1737..caf1836 100644
+--- a/drivers/scsi/scsi_transport_iscsi.c
++++ b/drivers/scsi/scsi_transport_iscsi.c
+@@ -30,27 +30,26 @@
+ #include <scsi/scsi_transport_iscsi.h>
+ #include <scsi/iscsi_if.h>
+
+-#define ISCSI_SESSION_ATTRS 19
+-#define ISCSI_CONN_ATTRS 13
+-#define ISCSI_HOST_ATTRS 4
+-#define ISCSI_TRANSPORT_VERSION "2.0-869"
++#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 device dev;
++ struct class_device cdev;
+
+- struct device_attribute *host_attrs[ISCSI_HOST_ATTRS + 1];
++ struct class_device_attribute *host_attrs[ISCSI_HOST_ATTRS + 1];
+ struct transport_container conn_cont;
+- struct device_attribute *conn_attrs[ISCSI_CONN_ATTRS + 1];
++ struct class_device_attribute *conn_attrs[ISCSI_CONN_ATTRS + 1];
+ struct transport_container session_cont;
+- struct device_attribute *session_attrs[ISCSI_SESSION_ATTRS + 1];
++ struct class_device_attribute *session_attrs[ISCSI_SESSION_ATTRS + 1];
+ };
+
+ static atomic_t iscsi_session_nr; /* sysfs session id for next new session */
+-static struct workqueue_struct *iscsi_eh_timer_workq;
+
+ /*
+ * list of registered transports and lock that must
+@@ -63,12 +62,12 @@ static DEFINE_SPINLOCK(iscsi_transport_lock);
+ #define to_iscsi_internal(tmpl) \
+ container_of(tmpl, struct iscsi_internal, t)
+
+-#define dev_to_iscsi_internal(_dev) \
+- container_of(_dev, struct iscsi_internal, dev)
++#define cdev_to_iscsi_internal(_cdev) \
++ container_of(_cdev, struct iscsi_internal, cdev)
+
+-static void iscsi_transport_release(struct device *dev)
++static void iscsi_transport_release(struct class_device *cdev)
+ {
+- struct iscsi_internal *priv = dev_to_iscsi_internal(dev);
++ struct iscsi_internal *priv = cdev_to_iscsi_internal(cdev);
+ kfree(priv);
+ }
+
+@@ -78,27 +77,25 @@ static void iscsi_transport_release(struct device *dev)
+ */
+ static struct class iscsi_transport_class = {
+ .name = "iscsi_transport",
+- .dev_release = iscsi_transport_release,
++ .release = iscsi_transport_release,
+ };
+
+ static ssize_t
+-show_transport_handle(struct device *dev, struct device_attribute *attr,
+- char *buf)
++show_transport_handle(struct class_device *cdev, char *buf)
+ {
+- struct iscsi_internal *priv = dev_to_iscsi_internal(dev);
++ struct iscsi_internal *priv = cdev_to_iscsi_internal(cdev);
+ return sprintf(buf, "%llu\n", (unsigned long long)iscsi_handle(priv->iscsi_transport));
+ }
+-static DEVICE_ATTR(handle, S_IRUGO, show_transport_handle, NULL);
++static CLASS_DEVICE_ATTR(handle, S_IRUGO, show_transport_handle, NULL);
+
+ #define show_transport_attr(name, format) \
+ static ssize_t \
+-show_transport_##name(struct device *dev, \
+- struct device_attribute *attr,char *buf) \
++show_transport_##name(struct class_device *cdev, char *buf) \
+ { \
+- struct iscsi_internal *priv = dev_to_iscsi_internal(dev); \
++ struct iscsi_internal *priv = cdev_to_iscsi_internal(cdev); \
+ return sprintf(buf, format"\n", priv->iscsi_transport->name); \
+ } \
+-static DEVICE_ATTR(name, S_IRUGO, show_transport_##name, NULL);
++static CLASS_DEVICE_ATTR(name, S_IRUGO, show_transport_##name, NULL);
+
+ show_transport_attr(caps, "0x%x");
+ show_transport_attr(max_lun, "%d");
+@@ -106,11 +103,11 @@ show_transport_attr(max_conn, "%d");
+ show_transport_attr(max_cmd_len, "%d");
+
+ static struct attribute *iscsi_transport_attrs[] = {
+- &dev_attr_handle.attr,
+- &dev_attr_caps.attr,
+- &dev_attr_max_lun.attr,
+- &dev_attr_max_conn.attr,
+- &dev_attr_max_cmd_len.attr,
++ &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,
+ };
+
+@@ -118,10 +115,8 @@ static struct attribute_group iscsi_transport_group = {
+ .attrs = iscsi_transport_attrs,
+ };
+
+-
+-
+ static int iscsi_setup_host(struct transport_container *tc, struct device *dev,
+- struct device *cdev)
++ struct class_device *cdev)
+ {
+ struct Scsi_Host *shost = dev_to_shost(dev);
+ struct iscsi_host *ihost = shost->shost_data;
+@@ -129,31 +124,13 @@ static int iscsi_setup_host(struct transport_container *tc, struct device *dev,
+ memset(ihost, 0, sizeof(*ihost));
+ INIT_LIST_HEAD(&ihost->sessions);
+ mutex_init(&ihost->mutex);
+- atomic_set(&ihost->nr_scans, 0);
+-
+- snprintf(ihost->scan_workq_name, KOBJ_NAME_LEN, "iscsi_scan_%d",
+- shost->host_no);
+- ihost->scan_workq = create_singlethread_workqueue(
+- ihost->scan_workq_name);
+- if (!ihost->scan_workq)
+- return -ENOMEM;
+- return 0;
+-}
+-
+-static int iscsi_remove_host(struct transport_container *tc, struct device *dev,
+- struct device *cdev)
+-{
+- struct Scsi_Host *shost = dev_to_shost(dev);
+- struct iscsi_host *ihost = shost->shost_data;
+-
+- destroy_workqueue(ihost->scan_workq);
+ return 0;
+ }
+
+ static DECLARE_TRANSPORT_CLASS(iscsi_host_class,
+ "iscsi_host",
+ iscsi_setup_host,
+- iscsi_remove_host,
++ NULL,
+ NULL);
+
+ static DECLARE_TRANSPORT_CLASS(iscsi_session_class,
+@@ -224,54 +201,6 @@ static struct iscsi_cls_conn *iscsi_conn_lookup(uint32_t sid, uint32_t cid)
+ * The following functions can be used by LLDs that allocate
+ * their own scsi_hosts or by software iscsi LLDs
+ */
+-static struct {
+- int value;
+- char *name;
+-} iscsi_session_state_names[] = {
+- { ISCSI_SESSION_LOGGED_IN, "LOGGED_IN" },
+- { ISCSI_SESSION_FAILED, "FAILED" },
+- { ISCSI_SESSION_FREE, "FREE" },
+-};
+-
+-static const char *iscsi_session_state_name(int state)
+-{
+- int i;
+- char *name = NULL;
+-
+- for (i = 0; i < ARRAY_SIZE(iscsi_session_state_names); i++) {
+- if (iscsi_session_state_names[i].value == state) {
+- name = iscsi_session_state_names[i].name;
+- break;
+- }
+- }
+- return name;
+-}
+-
+-int iscsi_session_chkready(struct iscsi_cls_session *session)
+-{
+- unsigned long flags;
+- int err;
+-
+- spin_lock_irqsave(&session->lock, flags);
+- switch (session->state) {
+- case ISCSI_SESSION_LOGGED_IN:
+- err = 0;
+- break;
+- case ISCSI_SESSION_FAILED:
+- err = DID_IMM_RETRY << 16;
+- break;
+- case ISCSI_SESSION_FREE:
+- err = DID_NO_CONNECT << 16;
+- break;
+- default:
+- err = DID_NO_CONNECT << 16;
+- break;
+- }
+- spin_unlock_irqrestore(&session->lock, flags);
+- return err;
+-}
+-EXPORT_SYMBOL_GPL(iscsi_session_chkready);
+-
+ static void iscsi_session_release(struct device *dev)
+ {
+ struct iscsi_cls_session *session = iscsi_dev_to_session(dev);
+@@ -287,25 +216,6 @@ static int iscsi_is_session_dev(const struct device *dev)
+ return dev->release == iscsi_session_release;
+ }
+
+-/**
+- * iscsi_scan_finished - helper to report when running scans are done
+- * @shost: scsi host
+- * @time: scan run time
+- *
+- * This function can be used by drives like qla4xxx to report to the scsi
+- * layer when the scans it kicked off at module load time are done.
+- */
+-int iscsi_scan_finished(struct Scsi_Host *shost, unsigned long time)
+-{
+- struct iscsi_host *ihost = shost->shost_data;
+- /*
+- * qla4xxx will have kicked off some session unblocks before calling
+- * scsi_scan_host, so just wait for them to complete.
+- */
+- return !atomic_read(&ihost->nr_scans);
+-}
+-EXPORT_SYMBOL_GPL(iscsi_scan_finished);
+-
+ static int iscsi_user_scan(struct Scsi_Host *shost, uint channel,
+ uint id, uint lun)
+ {
+@@ -324,50 +234,14 @@ static int iscsi_user_scan(struct Scsi_Host *shost, uint channel,
+ return 0;
+ }
+
+-static void iscsi_scan_session(struct work_struct *work)
+-{
+- struct iscsi_cls_session *session =
+- container_of(work, struct iscsi_cls_session, scan_work);
+- struct Scsi_Host *shost = iscsi_session_to_shost(session);
+- struct iscsi_host *ihost = shost->shost_data;
+- unsigned long flags;
+-
+- spin_lock_irqsave(&session->lock, flags);
+- if (session->state != ISCSI_SESSION_LOGGED_IN) {
+- spin_unlock_irqrestore(&session->lock, flags);
+- goto done;
+- }
+- spin_unlock_irqrestore(&session->lock, flags);
+-
+- scsi_scan_target(&session->dev, 0, session->target_id,
+- SCAN_WILD_CARD, 1);
+-done:
+- atomic_dec(&ihost->nr_scans);
+-}
+-
+ static void session_recovery_timedout(struct work_struct *work)
+ {
+ struct iscsi_cls_session *session =
+ container_of(work, struct iscsi_cls_session,
+ recovery_work.work);
+- unsigned long flags;
+-
+- iscsi_cls_session_printk(KERN_INFO, session,
+- "session recovery timed out after %d secs\n",
+- session->recovery_tmo);
+
+- spin_lock_irqsave(&session->lock, flags);
+- switch (session->state) {
+- case ISCSI_SESSION_FAILED:
+- session->state = ISCSI_SESSION_FREE;
+- break;
+- case ISCSI_SESSION_LOGGED_IN:
+- case ISCSI_SESSION_FREE:
+- /* we raced with the unblock's flush */
+- spin_unlock_irqrestore(&session->lock, flags);
+- return;
+- }
+- spin_unlock_irqrestore(&session->lock, flags);
++ 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);
+@@ -375,103 +249,22 @@ static void session_recovery_timedout(struct work_struct *work)
+ scsi_target_unblock(&session->dev);
+ }
+
+-static void __iscsi_unblock_session(struct work_struct *work)
+-{
+- struct iscsi_cls_session *session =
+- container_of(work, struct iscsi_cls_session,
+- unblock_work);
+- struct Scsi_Host *shost = iscsi_session_to_shost(session);
+- struct iscsi_host *ihost = shost->shost_data;
+- unsigned long flags;
+-
+- /*
+- * The recovery and unblock work get run from the same workqueue,
+- * so try to cancel it if it was going to run after this unblock.
+- */
+- cancel_delayed_work(&session->recovery_work);
+- spin_lock_irqsave(&session->lock, flags);
+- session->state = ISCSI_SESSION_LOGGED_IN;
+- spin_unlock_irqrestore(&session->lock, flags);
+- /* start IO */
+- scsi_target_unblock(&session->dev);
+- /*
+- * Only do kernel scanning if the driver is properly hooked into
+- * the async scanning code (drivers like iscsi_tcp do login and
+- * scanning from userspace).
+- */
+- if (shost->hostt->scan_finished) {
+- if (queue_work(ihost->scan_workq, &session->scan_work))
+- atomic_inc(&ihost->nr_scans);
+- }
+-}
+-
+-/**
+- * iscsi_unblock_session - set a session as logged in and start IO.
+- * @session: iscsi session
+- *
+- * Mark a session as ready to accept IO.
+- */
+ void iscsi_unblock_session(struct iscsi_cls_session *session)
+ {
+- queue_work(iscsi_eh_timer_workq, &session->unblock_work);
+- /*
+- * make sure all the events have completed before tell the driver
+- * it is safe
+- */
+- flush_workqueue(iscsi_eh_timer_workq);
++ if (!cancel_delayed_work(&session->recovery_work))
++ flush_scheduled_work();
++ scsi_target_unblock(&session->dev);
+ }
+ EXPORT_SYMBOL_GPL(iscsi_unblock_session);
+
+-static void __iscsi_block_session(struct work_struct *work)
+-{
+- struct iscsi_cls_session *session =
+- container_of(work, struct iscsi_cls_session,
+- block_work);
+- unsigned long flags;
+-
+- spin_lock_irqsave(&session->lock, flags);
+- session->state = ISCSI_SESSION_FAILED;
+- spin_unlock_irqrestore(&session->lock, flags);
+- scsi_target_block(&session->dev);
+- queue_delayed_work(iscsi_eh_timer_workq, &session->recovery_work,
+- session->recovery_tmo * HZ);
+-}
+-
+ void iscsi_block_session(struct iscsi_cls_session *session)
+ {
+- queue_work(iscsi_eh_timer_workq, &session->block_work);
++ scsi_target_block(&session->dev);
++ schedule_delayed_work(&session->recovery_work,
++ session->recovery_tmo * HZ);
+ }
+ EXPORT_SYMBOL_GPL(iscsi_block_session);
+
+-static void __iscsi_unbind_session(struct work_struct *work)
+-{
+- struct iscsi_cls_session *session =
+- container_of(work, struct iscsi_cls_session,
+- unbind_work);
+- struct Scsi_Host *shost = iscsi_session_to_shost(session);
+- struct iscsi_host *ihost = shost->shost_data;
+-
+- /* Prevent new scans and make sure scanning is not in progress */
+- mutex_lock(&ihost->mutex);
+- if (list_empty(&session->host_list)) {
+- mutex_unlock(&ihost->mutex);
+- return;
+- }
+- list_del_init(&session->host_list);
+- mutex_unlock(&ihost->mutex);
+-
+- scsi_remove_target(&session->dev);
+- iscsi_session_event(session, ISCSI_KEVENT_UNBIND_SESSION);
+-}
+-
+-static int iscsi_unbind_session(struct iscsi_cls_session *session)
+-{
+- struct Scsi_Host *shost = iscsi_session_to_shost(session);
+- struct iscsi_host *ihost = shost->shost_data;
+-
+- return queue_work(ihost->scan_workq, &session->unbind_work);
+-}
+-
+ struct iscsi_cls_session *
+ iscsi_alloc_session(struct Scsi_Host *shost,
+ struct iscsi_transport *transport)
+@@ -485,15 +278,9 @@ iscsi_alloc_session(struct Scsi_Host *shost,
+
+ session->transport = transport;
+ session->recovery_tmo = 120;
+- session->state = ISCSI_SESSION_FREE;
+ INIT_DELAYED_WORK(&session->recovery_work, session_recovery_timedout);
+ INIT_LIST_HEAD(&session->host_list);
+ INIT_LIST_HEAD(&session->sess_list);
+- INIT_WORK(&session->unblock_work, __iscsi_unblock_session);
+- INIT_WORK(&session->block_work, __iscsi_block_session);
+- INIT_WORK(&session->unbind_work, __iscsi_unbind_session);
+- INIT_WORK(&session->scan_work, iscsi_scan_session);
+- spin_lock_init(&session->lock);
+
+ /* this is released in the dev's release function */
+ scsi_host_get(shost);
+@@ -510,7 +297,6 @@ 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;
+- unsigned long flags;
+ int err;
+
+ ihost = shost->shost_data;
+@@ -521,21 +307,15 @@ int iscsi_add_session(struct iscsi_cls_session *session, unsigned int target_id)
+ session->sid);
+ err = device_add(&session->dev);
+ if (err) {
+- iscsi_cls_session_printk(KERN_ERR, session,
+- "could not register session's dev\n");
++ dev_printk(KERN_ERR, &session->dev, "iscsi: could not "
++ "register session's dev\n");
+ goto release_host;
+ }
+ transport_register_device(&session->dev);
+
+- spin_lock_irqsave(&sesslock, flags);
+- list_add(&session->sess_list, &sesslist);
+- spin_unlock_irqrestore(&sesslock, flags);
+-
+ mutex_lock(&ihost->mutex);
+ list_add(&session->host_list, &ihost->sessions);
+ mutex_unlock(&ihost->mutex);
+-
+- iscsi_session_event(session, ISCSI_KEVENT_CREATE_SESSION);
+ return 0;
+
+ release_host:
+@@ -548,10 +328,9 @@ EXPORT_SYMBOL_GPL(iscsi_add_session);
+ * iscsi_create_session - create iscsi class session
+ * @shost: scsi host
+ * @transport: iscsi transport
+- * @target_id: which target
+ *
+ * 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,
+@@ -571,65 +350,19 @@ iscsi_create_session(struct Scsi_Host *shost,
+ }
+ EXPORT_SYMBOL_GPL(iscsi_create_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;
+-}
+-
+-static int iscsi_iter_destroy_conn_fn(struct device *dev, void *data)
+-{
+- if (!iscsi_is_conn_dev(dev))
+- return 0;
+- return iscsi_destroy_conn(iscsi_dev_to_conn(dev));
+-}
+-
+ 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;
+- unsigned long flags;
+- int err;
+-
+- spin_lock_irqsave(&sesslock, flags);
+- list_del(&session->sess_list);
+- spin_unlock_irqrestore(&sesslock, flags);
+
+- /* make sure there are no blocks/unblocks queued */
+- flush_workqueue(iscsi_eh_timer_workq);
+- /* make sure the timedout callout is not running */
+ if (!cancel_delayed_work(&session->recovery_work))
+- flush_workqueue(iscsi_eh_timer_workq);
+- /*
+- * If we are blocked let commands flow again. The lld or iscsi
+- * layer should set up the queuecommand to fail commands.
+- * We assume that LLD will not be calling block/unblock while
+- * removing the session.
+- */
+- spin_lock_irqsave(&session->lock, flags);
+- session->state = ISCSI_SESSION_FREE;
+- spin_unlock_irqrestore(&session->lock, flags);
++ flush_scheduled_work();
+
+- scsi_target_unblock(&session->dev);
+- /* flush running scans then delete devices */
+- flush_workqueue(ihost->scan_workq);
+- __iscsi_unbind_session(&session->unbind_work);
++ mutex_lock(&ihost->mutex);
++ list_del(&session->host_list);
++ mutex_unlock(&ihost->mutex);
+
+- /* hw iscsi may not have removed all connections from session */
+- err = device_for_each_child(&session->dev, NULL,
+- iscsi_iter_destroy_conn_fn);
+- if (err)
+- iscsi_cls_session_printk(KERN_ERR, session,
+- "Could not delete all connections "
+- "for session. Error %d.\n", err);
++ scsi_remove_target(&session->dev);
+
+ transport_unregister_device(&session->dev);
+ device_del(&session->dev);
+@@ -638,9 +371,9 @@ EXPORT_SYMBOL_GPL(iscsi_remove_session);
+
+ void iscsi_free_session(struct iscsi_cls_session *session)
+ {
+- iscsi_session_event(session, ISCSI_KEVENT_DESTROY_SESSION);
+ put_device(&session->dev);
+ }
++
+ EXPORT_SYMBOL_GPL(iscsi_free_session);
+
+ /**
+@@ -649,7 +382,7 @@ EXPORT_SYMBOL_GPL(iscsi_free_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);
+@@ -658,6 +391,20 @@ int iscsi_destroy_session(struct iscsi_cls_session *session)
+ }
+ 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
+@@ -671,13 +418,12 @@ EXPORT_SYMBOL_GPL(iscsi_destroy_session);
+ * 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;
+- unsigned long flags;
+ int err;
+
+ conn = kzalloc(sizeof(*conn) + transport->conndata_size, GFP_KERNEL);
+@@ -701,16 +447,11 @@ iscsi_create_conn(struct iscsi_cls_session *session, uint32_t cid)
+ conn->dev.release = iscsi_conn_release;
+ err = device_register(&conn->dev);
+ if (err) {
+- iscsi_cls_session_printk(KERN_ERR, session, "could not "
+- "register connection's dev\n");
++ dev_printk(KERN_ERR, &conn->dev, "iscsi: could not register "
++ "connection's dev\n");
+ goto release_parent_ref;
+ }
+ transport_register_device(&conn->dev);
+-
+- spin_lock_irqsave(&connlock, flags);
+- list_add(&conn->conn_list, &connlist);
+- conn->active = 1;
+- spin_unlock_irqrestore(&connlock, flags);
+ return conn;
+
+ release_parent_ref:
+@@ -724,23 +465,17 @@ EXPORT_SYMBOL_GPL(iscsi_create_conn);
+
+ /**
+ * iscsi_destroy_conn - destroy iscsi class connection
+- * @conn: iscsi cls session
++ * @session: iscsi cls session
+ *
+ * This can be called from a LLD or iscsi_transport.
+- */
++ **/
+ int iscsi_destroy_conn(struct iscsi_cls_conn *conn)
+ {
+- unsigned long flags;
+-
+- spin_lock_irqsave(&connlock, flags);
+- conn->active = 0;
+- list_del(&conn->conn_list);
+- spin_unlock_irqrestore(&connlock, flags);
+-
+ transport_unregister_device(&conn->dev);
+ device_unregister(&conn->dev);
+ return 0;
+ }
++
+ EXPORT_SYMBOL_GPL(iscsi_destroy_conn);
+
+ /*
+@@ -809,8 +544,8 @@ int iscsi_recv_pdu(struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr,
+ skb = alloc_skb(len, GFP_ATOMIC);
+ if (!skb) {
+ iscsi_conn_error(conn, ISCSI_ERR_CONN_FAILED);
+- iscsi_cls_conn_printk(KERN_ERR, conn, "can not deliver "
+- "control PDU: OOM\n");
++ dev_printk(KERN_ERR, &conn->dev, "iscsi: can not deliver "
++ "control PDU: OOM\n");
+ return -ENOMEM;
+ }
+
+@@ -843,8 +578,8 @@ void iscsi_conn_error(struct iscsi_cls_conn *conn, enum iscsi_err error)
+
+ skb = alloc_skb(len, GFP_ATOMIC);
+ if (!skb) {
+- iscsi_cls_conn_printk(KERN_ERR, conn, "gracefully ignored "
+- "conn error (%d)\n", error);
++ dev_printk(KERN_ERR, &conn->dev, "iscsi: gracefully ignored "
++ "conn error (%d)\n", error);
+ return;
+ }
+
+@@ -858,8 +593,8 @@ void iscsi_conn_error(struct iscsi_cls_conn *conn, enum iscsi_err error)
+
+ iscsi_broadcast_skb(skb, GFP_ATOMIC);
+
+- iscsi_cls_conn_printk(KERN_INFO, conn, "detected conn error (%d)\n",
+- error);
++ dev_printk(KERN_INFO, &conn->dev, "iscsi: detected conn error (%d)\n",
++ error);
+ }
+ EXPORT_SYMBOL_GPL(iscsi_conn_error);
+
+@@ -874,10 +609,12 @@ iscsi_if_send_reply(int pid, int seq, int type, int done, int multi,
+ 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;
+- }
++ /*
++ * FIXME:
++ * user is supposed to react on iferror == -ENOMEM;
++ * see iscsi_if_rx().
++ */
++ BUG_ON(!skb);
+
+ nlh = __nlmsg_put(skb, pid, seq, t, (len - sizeof(*nlh)), 0);
+ nlh->nlmsg_flags = flags;
+@@ -914,8 +651,8 @@ iscsi_if_get_stats(struct iscsi_transport *transport, struct nlmsghdr *nlh)
+
+ skbstat = alloc_skb(len, GFP_ATOMIC);
+ if (!skbstat) {
+- iscsi_cls_conn_printk(KERN_ERR, conn, "can not "
+- "deliver stats: OOM\n");
++ dev_printk(KERN_ERR, &conn->dev, "iscsi: can not "
++ "deliver stats: OOM\n");
+ return -ENOMEM;
+ }
+
+@@ -950,87 +687,144 @@ iscsi_if_get_stats(struct iscsi_transport *transport, struct nlmsghdr *nlh)
+ }
+
+ /**
+- * iscsi_session_event - send session destr. completion event
+- * @session: iscsi class session
+- * @event: type of event
+- */
+-int iscsi_session_event(struct iscsi_cls_session *session,
+- enum iscsi_uevent_e event)
++ * 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(session->transport);
++ 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) {
+- iscsi_cls_session_printk(KERN_ERR, session,
+- "Cannot notify userspace of session "
+- "event %u\n", event);
++ 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(session->transport);
++ 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;
+
+- ev->type = event;
+- switch (event) {
+- case ISCSI_KEVENT_DESTROY_SESSION:
+- ev->r.d_session.host_no = shost->host_no;
+- ev->r.d_session.sid = session->sid;
+- break;
+- case ISCSI_KEVENT_CREATE_SESSION:
+- ev->r.c_session_ret.host_no = shost->host_no;
+- ev->r.c_session_ret.sid = session->sid;
+- break;
+- case ISCSI_KEVENT_UNBIND_SESSION:
+- ev->r.unbind_session.host_no = shost->host_no;
+- ev->r.unbind_session.sid = session->sid;
+- break;
+- default:
+- iscsi_cls_session_printk(KERN_ERR, session, "Invalid event "
+- "%u.\n", event);
+- kfree_skb(skb);
++ /*
++ * 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)
+- iscsi_cls_session_printk(KERN_ERR, session,
+- "Cannot notify userspace of session "
+- "event %u. Check iscsi daemon\n",
+- event);
++ 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_session_event);
++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.cmds_max,
+- ev->u.c_session.queue_depth,
+ 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;
+@@ -1041,34 +835,47 @@ 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",
++ 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) {
+- iscsi_cls_session_printk(KERN_ERR, session,
+- "couldn't create a new connection.");
++ 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);
+@@ -1140,50 +947,15 @@ static int
+ iscsi_tgt_dscvr(struct iscsi_transport *transport,
+ struct iscsi_uevent *ev)
+ {
+- struct Scsi_Host *shost;
+ struct sockaddr *dst_addr;
+- int err;
+
+ if (!transport->tgt_dscvr)
+ return -EINVAL;
+
+- shost = scsi_host_lookup(ev->u.tgt_dscvr.host_no);
+- if (IS_ERR(shost)) {
+- printk(KERN_ERR "target discovery could not find host no %u\n",
+- ev->u.tgt_dscvr.host_no);
+- return -ENODEV;
+- }
+-
+-
+ dst_addr = (struct sockaddr *)((char*)ev + sizeof(*ev));
+- err = transport->tgt_dscvr(shost, ev->u.tgt_dscvr.type,
+- ev->u.tgt_dscvr.enable, dst_addr);
+- scsi_host_put(shost);
+- return err;
+-}
+-
+-static int
+-iscsi_set_host_param(struct iscsi_transport *transport,
+- struct iscsi_uevent *ev)
+-{
+- char *data = (char*)ev + sizeof(*ev);
+- struct Scsi_Host *shost;
+- int err;
+-
+- if (!transport->set_host_param)
+- return -ENOSYS;
+-
+- shost = scsi_host_lookup(ev->u.set_host_param.host_no);
+- if (IS_ERR(shost)) {
+- printk(KERN_ERR "set_host_param could not find host no %u\n",
+- ev->u.set_host_param.host_no);
+- return -ENODEV;
+- }
+-
+- err = transport->set_host_param(shost, ev->u.set_host_param.param,
+- data, ev->u.set_host_param.len);
+- scsi_host_put(shost);
+- return err;
++ return transport->tgt_dscvr(ev->u.tgt_dscvr.type,
++ ev->u.tgt_dscvr.host_no,
++ ev->u.tgt_dscvr.enable, dst_addr);
+ }
+
+ static int
+@@ -1195,6 +967,7 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
+ 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)
+@@ -1212,16 +985,13 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
+ break;
+ case ISCSI_UEVENT_DESTROY_SESSION:
+ session = iscsi_session_lookup(ev->u.d_session.sid);
+- if (session)
++ 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_UNBIND_SESSION:
+- session = iscsi_session_lookup(ev->u.d_session.sid);
+- if (session)
+- iscsi_unbind_session(session);
+- else
++ } else
+ err = -EINVAL;
+ break;
+ case ISCSI_UEVENT_CREATE_CONN:
+@@ -1279,11 +1049,8 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
+ case ISCSI_UEVENT_TGT_DSCVR:
+ err = iscsi_tgt_dscvr(transport, ev);
+ break;
+- case ISCSI_UEVENT_SET_HOST_PARAM:
+- err = iscsi_set_host_param(transport, ev);
+- break;
+ default:
+- err = -ENOSYS;
++ err = -EINVAL;
+ break;
+ }
+
+@@ -1292,55 +1059,70 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
+ }
+
+ /*
+- * Get message from skb. Each message is processed by iscsi_if_recv_msg.
+- * Malformed skbs with wrong lengths or invalid creds are not processed.
++ * 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 sk_buff *skb)
++iscsi_if_rx(struct sock *sk, int len)
+ {
++ struct sk_buff *skb;
++
+ mutex_lock(&rx_queue_mutex);
+- while (skb->len >= NLMSG_SPACE(0)) {
+- int err;
+- uint32_t rlen;
+- struct nlmsghdr *nlh;
+- struct iscsi_uevent *ev;
+-
+- nlh = nlmsg_hdr(skb);
+- if (nlh->nlmsg_len < sizeof(*nlh) ||
+- skb->len < nlh->nlmsg_len) {
+- break;
++ while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) {
++ if (NETLINK_CREDS(skb)->uid) {
++ skb_pull(skb, skb->len);
++ goto free_skb;
+ }
+
+- ev = NLMSG_DATA(nlh);
+- rlen = NLMSG_ALIGN(nlh->nlmsg_len);
+- if (rlen > skb->len)
+- rlen = skb->len;
++ while (skb->len >= NLMSG_SPACE(0)) {
++ int err;
++ uint32_t rlen;
++ struct nlmsghdr *nlh;
++ struct iscsi_uevent *ev;
+
+- 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)
++ nlh = nlmsg_hdr(skb);
++ if (nlh->nlmsg_len < sizeof(*nlh) ||
++ skb->len < nlh->nlmsg_len) {
+ 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);
++ }
++
++ 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 device_attribute dev_attr_##_prefix##_##_name = \
++struct class_device_attribute class_device_attr_##_prefix##_##_name = \
+ __ATTR(_name,_mode,_show,_store)
+
+ /*
+@@ -1348,10 +1130,9 @@ struct device_attribute dev_attr_##_prefix##_##_name = \
+ */
+ #define iscsi_conn_attr_show(param) \
+ static ssize_t \
+-show_conn_param_##param(struct device *dev, \
+- struct device_attribute *attr, char *buf) \
++show_conn_param_##param(struct class_device *cdev, char *buf) \
+ { \
+- struct iscsi_cls_conn *conn = iscsi_dev_to_conn(dev->parent); \
++ struct iscsi_cls_conn *conn = iscsi_cdev_to_conn(cdev); \
+ struct iscsi_transport *t = conn->transport; \
+ return t->get_conn_param(conn, param, buf); \
+ }
+@@ -1372,66 +1153,43 @@ 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);
+-iscsi_conn_attr(ping_tmo, ISCSI_PARAM_PING_TMO);
+-iscsi_conn_attr(recv_tmo, ISCSI_PARAM_RECV_TMO);
++
++#define iscsi_cdev_to_session(_cdev) \
++ iscsi_dev_to_session(_cdev->dev)
+
+ /*
+ * iSCSI session attrs
+ */
+-#define iscsi_session_attr_show(param, perm) \
++#define iscsi_session_attr_show(param) \
+ static ssize_t \
+-show_session_param_##param(struct device *dev, \
+- struct device_attribute *attr, char *buf) \
++show_session_param_##param(struct class_device *cdev, char *buf) \
+ { \
+- struct iscsi_cls_session *session = \
+- iscsi_dev_to_session(dev->parent); \
++ struct iscsi_cls_session *session = iscsi_cdev_to_session(cdev); \
+ struct iscsi_transport *t = session->transport; \
+- \
+- if (perm && !capable(CAP_SYS_ADMIN)) \
+- return -EACCES; \
+ return t->get_session_param(session, param, buf); \
+ }
+
+-#define iscsi_session_attr(field, param, perm) \
+- iscsi_session_attr_show(param, perm) \
++#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, 0);
+-iscsi_session_attr(initial_r2t, ISCSI_PARAM_INITIAL_R2T_EN, 0);
+-iscsi_session_attr(max_outstanding_r2t, ISCSI_PARAM_MAX_R2T, 0);
+-iscsi_session_attr(immediate_data, ISCSI_PARAM_IMM_DATA_EN, 0);
+-iscsi_session_attr(first_burst_len, ISCSI_PARAM_FIRST_BURST, 0);
+-iscsi_session_attr(max_burst_len, ISCSI_PARAM_MAX_BURST, 0);
+-iscsi_session_attr(data_pdu_in_order, ISCSI_PARAM_PDU_INORDER_EN, 0);
+-iscsi_session_attr(data_seq_in_order, ISCSI_PARAM_DATASEQ_INORDER_EN, 0);
+-iscsi_session_attr(erl, ISCSI_PARAM_ERL, 0);
+-iscsi_session_attr(tpgt, ISCSI_PARAM_TPGT, 0);
+-iscsi_session_attr(username, ISCSI_PARAM_USERNAME, 1);
+-iscsi_session_attr(username_in, ISCSI_PARAM_USERNAME_IN, 1);
+-iscsi_session_attr(password, ISCSI_PARAM_PASSWORD, 1);
+-iscsi_session_attr(password_in, ISCSI_PARAM_PASSWORD_IN, 1);
+-iscsi_session_attr(fast_abort, ISCSI_PARAM_FAST_ABORT, 0);
+-iscsi_session_attr(abort_tmo, ISCSI_PARAM_ABORT_TMO, 0);
+-iscsi_session_attr(lu_reset_tmo, ISCSI_PARAM_LU_RESET_TMO, 0);
+-
+-static ssize_t
+-show_priv_session_state(struct device *dev, struct device_attribute *attr,
+- char *buf)
+-{
+- struct iscsi_cls_session *session = iscsi_dev_to_session(dev->parent);
+- return sprintf(buf, "%s\n", iscsi_session_state_name(session->state));
+-}
+-static ISCSI_CLASS_ATTR(priv_sess, state, S_IRUGO, show_priv_session_state,
+- 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 device *dev, \
+- struct device_attribute *attr, char *buf) \
++show_priv_session_##field(struct class_device *cdev, char *buf) \
+ { \
+- struct iscsi_cls_session *session = \
+- iscsi_dev_to_session(dev->parent); \
++ struct iscsi_cls_session *session = iscsi_cdev_to_session(cdev);\
+ return sprintf(buf, format"\n", session->field); \
+ }
+
+@@ -1441,32 +1199,9 @@ static ISCSI_CLASS_ATTR(priv_sess, field, S_IRUGO, show_priv_session_##field, \
+ NULL)
+ iscsi_priv_session_attr(recovery_tmo, "%d");
+
+-/*
+- * iSCSI host attrs
+- */
+-#define iscsi_host_attr_show(param) \
+-static ssize_t \
+-show_host_param_##param(struct device *dev, \
+- struct device_attribute *attr, char *buf) \
+-{ \
+- struct Scsi_Host *shost = transport_class_to_shost(dev); \
+- struct iscsi_internal *priv = to_iscsi_internal(shost->transportt); \
+- return priv->iscsi_transport->get_host_param(shost, param, buf); \
+-}
+-
+-#define iscsi_host_attr(field, param) \
+- iscsi_host_attr_show(param) \
+-static ISCSI_CLASS_ATTR(host, field, S_IRUGO, show_host_param_##param, \
+- NULL);
+-
+-iscsi_host_attr(netdev, ISCSI_HOST_PARAM_NETDEV_NAME);
+-iscsi_host_attr(hwaddress, ISCSI_HOST_PARAM_HWADDRESS);
+-iscsi_host_attr(ipaddress, ISCSI_HOST_PARAM_IPADDRESS);
+-iscsi_host_attr(initiatorname, ISCSI_HOST_PARAM_INITIATOR_NAME);
+-
+ #define SETUP_PRIV_SESSION_RD_ATTR(field) \
+ do { \
+- priv->session_attrs[count] = &dev_attr_priv_sess_##field; \
++ priv->session_attrs[count] = &class_device_attr_priv_sess_##field; \
+ count++; \
+ } while (0)
+
+@@ -1474,7 +1209,7 @@ do { \
+ #define SETUP_SESSION_RD_ATTR(field, param_flag) \
+ do { \
+ if (tt->param_mask & param_flag) { \
+- priv->session_attrs[count] = &dev_attr_sess_##field; \
++ priv->session_attrs[count] = &class_device_attr_sess_##field; \
+ count++; \
+ } \
+ } while (0)
+@@ -1482,15 +1217,7 @@ do { \
+ #define SETUP_CONN_RD_ATTR(field, param_flag) \
+ do { \
+ if (tt->param_mask & param_flag) { \
+- priv->conn_attrs[count] = &dev_attr_conn_##field; \
+- count++; \
+- } \
+-} while (0)
+-
+-#define SETUP_HOST_RD_ATTR(field, param_flag) \
+-do { \
+- if (tt->host_param_mask & param_flag) { \
+- priv->host_attrs[count] = &dev_attr_host_##field; \
++ priv->conn_attrs[count] = &class_device_attr_conn_##field; \
+ count++; \
+ } \
+ } while (0)
+@@ -1581,31 +1308,24 @@ iscsi_register_transport(struct iscsi_transport *tt)
+ priv->iscsi_transport = tt;
+ priv->t.user_scan = iscsi_user_scan;
+
+- priv->dev.class = &iscsi_transport_class;
+- snprintf(priv->dev.bus_id, BUS_ID_SIZE, "%s", tt->name);
+- err = device_register(&priv->dev);
++ 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->dev.kobj, &iscsi_transport_group);
++ err = sysfs_create_group(&priv->cdev.kobj, &iscsi_transport_group);
+ if (err)
+- goto unregister_dev;
++ 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);
+
+- SETUP_HOST_RD_ATTR(netdev, ISCSI_HOST_NETDEV_NAME);
+- SETUP_HOST_RD_ATTR(ipaddress, ISCSI_HOST_IPADDRESS);
+- SETUP_HOST_RD_ATTR(hwaddress, ISCSI_HOST_HWADDRESS);
+- SETUP_HOST_RD_ATTR(initiatorname, ISCSI_HOST_INITIATOR_NAME);
+- BUG_ON(count > ISCSI_HOST_ATTRS);
+- priv->host_attrs[count] = NULL;
+- count = 0;
+-
+ /* connection parameters */
+ priv->conn_cont.ac.attrs = &priv->conn_attrs[0];
+ priv->conn_cont.ac.class = &iscsi_connection_class.class;
+@@ -1623,8 +1343,6 @@ iscsi_register_transport(struct iscsi_transport *tt)
+ 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);
+- SETUP_CONN_RD_ATTR(ping_tmo, ISCSI_PING_TMO);
+- SETUP_CONN_RD_ATTR(recv_tmo, ISCSI_RECV_TMO);
+
+ BUG_ON(count > ISCSI_CONN_ATTRS);
+ priv->conn_attrs[count] = NULL;
+@@ -1646,15 +1364,7 @@ iscsi_register_transport(struct iscsi_transport *tt)
+ SETUP_SESSION_RD_ATTR(erl, ISCSI_ERL);
+ SETUP_SESSION_RD_ATTR(targetname, ISCSI_TARGET_NAME);
+ SETUP_SESSION_RD_ATTR(tpgt, ISCSI_TPGT);
+- SETUP_SESSION_RD_ATTR(password, ISCSI_USERNAME);
+- SETUP_SESSION_RD_ATTR(password_in, ISCSI_USERNAME_IN);
+- SETUP_SESSION_RD_ATTR(username, ISCSI_PASSWORD);
+- SETUP_SESSION_RD_ATTR(username_in, ISCSI_PASSWORD_IN);
+- SETUP_SESSION_RD_ATTR(fast_abort, ISCSI_FAST_ABORT);
+- SETUP_SESSION_RD_ATTR(abort_tmo, ISCSI_ABORT_TMO);
+- SETUP_SESSION_RD_ATTR(lu_reset_tmo,ISCSI_LU_RESET_TMO);
+ SETUP_PRIV_SESSION_RD_ATTR(recovery_tmo);
+- SETUP_PRIV_SESSION_RD_ATTR(state);
+
+ BUG_ON(count > ISCSI_SESSION_ATTRS);
+ priv->session_attrs[count] = NULL;
+@@ -1666,8 +1376,8 @@ iscsi_register_transport(struct iscsi_transport *tt)
+ printk(KERN_NOTICE "iscsi: registered transport (%s)\n", tt->name);
+ return &priv->t;
+
+-unregister_dev:
+- device_unregister(&priv->dev);
++unregister_cdev:
++ class_device_unregister(&priv->cdev);
+ free_priv:
+ kfree(priv);
+ return NULL;
+@@ -1694,8 +1404,8 @@ int iscsi_unregister_transport(struct iscsi_transport *tt)
+ transport_container_unregister(&priv->session_cont);
+ transport_container_unregister(&priv->t.host_attrs);
+
+- sysfs_remove_group(&priv->dev.kobj, &iscsi_transport_group);
+- device_unregister(&priv->dev);
++ sysfs_remove_group(&priv->cdev.kobj, &iscsi_transport_group);
++ class_device_unregister(&priv->cdev);
+ mutex_unlock(&rx_queue_mutex);
+
+ return 0;
+@@ -1727,21 +1437,15 @@ static __init int iscsi_transport_init(void)
+ if (err)
+ goto unregister_conn_class;
+
+- nls = netlink_kernel_create(&init_net, NETLINK_ISCSI, 1, iscsi_if_rx, NULL,
++ nls = netlink_kernel_create(NETLINK_ISCSI, 1, iscsi_if_rx, NULL,
+ THIS_MODULE);
+ if (!nls) {
+ err = -ENOBUFS;
+ goto unregister_session_class;
+ }
+
+- iscsi_eh_timer_workq = create_singlethread_workqueue("iscsi_eh");
+- if (!iscsi_eh_timer_workq)
+- goto release_nls;
+-
+ return 0;
+
+-release_nls:
+- netlink_kernel_release(nls);
+ unregister_session_class:
+ transport_class_unregister(&iscsi_session_class);
+ unregister_conn_class:
+@@ -1755,8 +1459,7 @@ unregister_transport_class:
+
+ static void __exit iscsi_transport_exit(void)
+ {
+- destroy_workqueue(iscsi_eh_timer_workq);
+- netlink_kernel_release(nls);
++ sock_release(nls->sk_socket);
+ transport_class_unregister(&iscsi_connection_class);
+ transport_class_unregister(&iscsi_session_class);
+ transport_class_unregister(&iscsi_host_class);
+diff --git a/include/scsi/iscsi_if.h b/include/scsi/iscsi_if.h
+index e19e584..55ebf03 100644
+--- a/include/scsi/iscsi_if.h
++++ b/include/scsi/iscsi_if.h
+@@ -48,16 +48,12 @@ enum iscsi_uevent_e {
+ ISCSI_UEVENT_TRANSPORT_EP_DISCONNECT = UEVENT_BASE + 14,
+
+ ISCSI_UEVENT_TGT_DSCVR = UEVENT_BASE + 15,
+- ISCSI_UEVENT_SET_HOST_PARAM = UEVENT_BASE + 16,
+- ISCSI_UEVENT_UNBIND_SESSION = UEVENT_BASE + 17,
+
+ /* up events */
+ ISCSI_KEVENT_RECV_PDU = KEVENT_BASE + 1,
+ ISCSI_KEVENT_CONN_ERROR = KEVENT_BASE + 2,
+ ISCSI_KEVENT_IF_ERROR = KEVENT_BASE + 3,
+ ISCSI_KEVENT_DESTROY_SESSION = KEVENT_BASE + 4,
+- ISCSI_KEVENT_UNBIND_SESSION = KEVENT_BASE + 5,
+- ISCSI_KEVENT_CREATE_SESSION = KEVENT_BASE + 6,
+ };
+
+ enum iscsi_tgt_dscvr {
+@@ -75,8 +71,6 @@ struct iscsi_uevent {
+ /* messages u -> k */
+ struct msg_create_session {
+ uint32_t initial_cmdsn;
+- uint16_t cmds_max;
+- uint16_t queue_depth;
+ } c_session;
+ struct msg_destroy_session {
+ uint32_t sid;
+@@ -142,11 +136,6 @@ struct iscsi_uevent {
+ */
+ uint32_t enable;
+ } tgt_dscvr;
+- struct msg_set_host_param {
+- uint32_t host_no;
+- uint32_t param; /* enum iscsi_host_param */
+- uint32_t len;
+- } set_host_param;
+ } u;
+ union {
+ /* messages k -> u */
+@@ -159,10 +148,6 @@ struct iscsi_uevent {
+ uint32_t sid;
+ uint32_t cid;
+ } c_conn_ret;
+- struct msg_unbind_session {
+- uint32_t sid;
+- uint32_t host_no;
+- } unbind_session;
+ struct msg_recv_req {
+ uint32_t sid;
+ uint32_t cid;
+@@ -238,18 +223,6 @@ enum iscsi_param {
+ ISCSI_PARAM_CONN_PORT,
+ ISCSI_PARAM_CONN_ADDRESS,
+
+- ISCSI_PARAM_USERNAME,
+- ISCSI_PARAM_USERNAME_IN,
+- ISCSI_PARAM_PASSWORD,
+- ISCSI_PARAM_PASSWORD_IN,
+-
+- ISCSI_PARAM_FAST_ABORT,
+- ISCSI_PARAM_ABORT_TMO,
+- ISCSI_PARAM_LU_RESET_TMO,
+- ISCSI_PARAM_HOST_RESET_TMO,
+-
+- ISCSI_PARAM_PING_TMO,
+- ISCSI_PARAM_RECV_TMO,
+ /* must always be last */
+ ISCSI_PARAM_MAX,
+ };
+@@ -276,30 +249,6 @@ enum iscsi_param {
+ #define ISCSI_SESS_RECOVERY_TMO (1 << ISCSI_PARAM_SESS_RECOVERY_TMO)
+ #define ISCSI_CONN_PORT (1 << ISCSI_PARAM_CONN_PORT)
+ #define ISCSI_CONN_ADDRESS (1 << ISCSI_PARAM_CONN_ADDRESS)
+-#define ISCSI_USERNAME (1 << ISCSI_PARAM_USERNAME)
+-#define ISCSI_USERNAME_IN (1 << ISCSI_PARAM_USERNAME_IN)
+-#define ISCSI_PASSWORD (1 << ISCSI_PARAM_PASSWORD)
+-#define ISCSI_PASSWORD_IN (1 << ISCSI_PARAM_PASSWORD_IN)
+-#define ISCSI_FAST_ABORT (1 << ISCSI_PARAM_FAST_ABORT)
+-#define ISCSI_ABORT_TMO (1 << ISCSI_PARAM_ABORT_TMO)
+-#define ISCSI_LU_RESET_TMO (1 << ISCSI_PARAM_LU_RESET_TMO)
+-#define ISCSI_HOST_RESET_TMO (1 << ISCSI_PARAM_HOST_RESET_TMO)
+-#define ISCSI_PING_TMO (1 << ISCSI_PARAM_PING_TMO)
+-#define ISCSI_RECV_TMO (1 << ISCSI_PARAM_RECV_TMO)
+-
+-/* iSCSI HBA params */
+-enum iscsi_host_param {
+- ISCSI_HOST_PARAM_HWADDRESS,
+- ISCSI_HOST_PARAM_INITIATOR_NAME,
+- ISCSI_HOST_PARAM_NETDEV_NAME,
+- ISCSI_HOST_PARAM_IPADDRESS,
+- ISCSI_HOST_PARAM_MAX,
+-};
+-
+-#define ISCSI_HOST_HWADDRESS (1 << ISCSI_HOST_PARAM_HWADDRESS)
+-#define ISCSI_HOST_INITIATOR_NAME (1 << ISCSI_HOST_PARAM_INITIATOR_NAME)
+-#define ISCSI_HOST_NETDEV_NAME (1 << ISCSI_HOST_PARAM_NETDEV_NAME)
+-#define ISCSI_HOST_IPADDRESS (1 << ISCSI_HOST_PARAM_IPADDRESS)
+
+ #define iscsi_ptr(_handle) ((void*)(unsigned long)_handle)
+ #define iscsi_handle(_ptr) ((uint64_t)(unsigned long)_ptr)
+@@ -323,9 +272,6 @@ enum iscsi_host_param {
+ #define CAP_MULTI_CONN 0x40
+ #define CAP_TEXT_NEGO 0x80
+ #define CAP_MARKERS 0x100
+-#define CAP_FW_DB 0x200
+-#define CAP_SENDTARGETS_OFFLOAD 0x400
+-#define CAP_DATA_PATH_OFFLOAD 0x800
+
+ /*
+ * These flags describes reason of stop_conn() call
+diff --git a/include/scsi/iscsi_proto.h b/include/scsi/iscsi_proto.h
+index e0593bf..8d1e4e8 100644
+--- a/include/scsi/iscsi_proto.h
++++ b/include/scsi/iscsi_proto.h
+@@ -21,15 +21,13 @@
+ #ifndef ISCSI_PROTO_H
+ #define ISCSI_PROTO_H
+
+-#include <linux/types.h>
+-
+ #define ISCSI_DRAFT20_VERSION 0x00
+
+ /* default iSCSI listen port for incoming connections */
+ #define ISCSI_LISTEN_PORT 3260
+
+ /* Padding word length */
+-#define ISCSI_PAD_LEN 4
++#define PAD_WORD_LEN 4
+
+ /*
+ * useful common(control and data pathes) macro
+@@ -45,8 +43,8 @@
+ /* 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, age) ((__force itt_t)\
+- ((itt) | ((age) << ISCSI_AGE_SHIFT)))
++#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)
+
+@@ -112,7 +110,6 @@ struct iscsi_ahs_hdr {
+
+ #define ISCSI_AHSTYPE_CDB 1
+ #define ISCSI_AHSTYPE_RLENGTH 2
+-#define ISCSI_CDB_SIZE 16
+
+ /* iSCSI PDU Header */
+ struct iscsi_cmd {
+@@ -126,7 +123,7 @@ struct iscsi_cmd {
+ __be32 data_length;
+ __be32 cmdsn;
+ __be32 exp_statsn;
+- uint8_t cdb[ISCSI_CDB_SIZE]; /* SCSI Command Block */
++ uint8_t cdb[16]; /* SCSI Command Block */
+ /* Additional Data (Command Dependent) */
+ };
+
+@@ -150,15 +147,6 @@ struct iscsi_rlength_ahdr {
+ __be32 read_length;
+ };
+
+-/* Extended CDB AHS */
+-struct iscsi_ecdb_ahdr {
+- __be16 ahslength; /* CDB length - 15, including reserved byte */
+- uint8_t ahstype;
+- uint8_t reserved;
+- /* 4-byte aligned extended CDB spillover */
+- uint8_t ecdb[260 - ISCSI_CDB_SIZE];
+-};
+-
+ /* SCSI Response Header */
+ struct iscsi_cmd_rsp {
+ uint8_t opcode;
+@@ -612,8 +600,6 @@ struct iscsi_reject {
+ #define ISCSI_MIN_MAX_BURST_LEN 512
+ #define ISCSI_MAX_MAX_BURST_LEN 16777215
+
+-#define ISCSI_DEF_TIME2WAIT 2
+-
+ /************************* RFC 3720 End *****************************/
+
+ #endif /* ISCSI_PROTO_H */
+diff --git a/include/scsi/libiscsi.h b/include/scsi/libiscsi.h
+index cd3ca63..ea0816d 100644
+--- a/include/scsi/libiscsi.h
++++ b/include/scsi/libiscsi.h
+@@ -48,8 +48,9 @@ struct iscsi_nopin;
+ #define debug_scsi(fmt...)
+ #endif
+
+-#define ISCSI_DEF_XMIT_CMDS_MAX 128 /* must be power of 2 */
+-#define ISCSI_MGMT_CMDS_MAX 16 /* must be power of 2 */
++#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
+
+@@ -57,31 +58,21 @@ struct iscsi_nopin;
+ #define ISCSI_MAX_CMD_PER_LUN 128
+
+ /* Task Mgmt states */
+-enum {
+- TMF_INITIAL,
+- TMF_QUEUED,
+- TMF_SUCCESS,
+- TMF_FAILED,
+- TMF_TIMEDOUT,
+- TMF_NOT_FOUND,
+-};
++#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)
+
+-#define ISCSI_ADDRESS_BUF_LEN 64
+-
+-enum {
+- /* this is the maximum possible storage for AHSs */
+- ISCSI_MAX_AHS_SIZE = sizeof(struct iscsi_ecdb_ahdr) +
+- sizeof(struct iscsi_rlength_ahdr),
+- ISCSI_DIGEST_SIZE = sizeof(__u32),
+-};
+-
+ struct iscsi_mgmt_task {
+ /*
+ * Becuae LLDs allocate their hdr differently, this is a pointer to
+@@ -89,7 +80,7 @@ struct iscsi_mgmt_task {
+ */
+ struct iscsi_hdr *hdr;
+ char *data; /* mgmt payload */
+- unsigned data_count; /* counts data to be sent */
++ int data_count; /* counts data to be sent */
+ uint32_t itt; /* this ITT */
+ void *dd_data; /* driver/transport data */
+ struct list_head running;
+@@ -103,23 +94,23 @@ enum {
+
+ struct iscsi_cmd_task {
+ /*
+- * Because LLDs allocate their hdr differently, this is a pointer
+- * and length to that storage. It must be setup at session
+- * creation time.
++ * 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;
+- unsigned short hdr_max;
+- unsigned short hdr_len; /* accumulated size of hdr used */
+ int itt; /* this ITT */
++ int datasn; /* DataSN */
+
+ uint32_t unsol_datasn;
+- unsigned imm_count; /* imm-data (bytes) */
+- unsigned unsol_count; /* unsolicited (bytes)*/
++ int imm_count; /* imm-data (bytes) */
++ int unsol_count; /* unsolicited (bytes)*/
+ /* offset in unsolicited stream (bytes); */
+- unsigned unsol_offset;
+- unsigned data_count; /* remaining Data-Out */
++ 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;
+@@ -128,19 +119,6 @@ struct iscsi_cmd_task {
+ void *dd_data; /* driver/transport data */
+ };
+
+-static inline void* iscsi_next_hdr(struct iscsi_cmd_task *ctask)
+-{
+- return (void*)ctask->hdr + ctask->hdr_len;
+-}
+-
+-/* Connection's states */
+-enum {
+- ISCSI_CONN_INITIAL_STAGE,
+- ISCSI_CONN_STARTED,
+- ISCSI_CONN_STOPPED,
+- ISCSI_CONN_CLEANUP_WAIT,
+-};
+-
+ struct iscsi_conn {
+ struct iscsi_cls_conn *cls_conn; /* ptr to class connection */
+ void *dd_data; /* iscsi_transport data */
+@@ -154,12 +132,6 @@ struct iscsi_conn {
+ * conn_stop() flag: stop to recover, stop to terminate
+ */
+ int stop_stage;
+- struct timer_list transport_timer;
+- unsigned long last_recv;
+- unsigned long last_ping;
+- int ping_timeout;
+- int recv_timeout;
+- struct iscsi_mgmt_task *ping_mtask;
+
+ /* iSCSI connection-wide sequencing */
+ uint32_t exp_statsn;
+@@ -180,24 +152,30 @@ struct iscsi_conn {
+ struct iscsi_cmd_task *ctask; /* xmit ctask in progress */
+
+ /* xmit */
+- struct list_head mgmtqueue; /* mgmt (control) xmit queue */
++ 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 list_head requeue; /* tasks needing another run */
+ 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 tmf_timer;
+- int tmf_state; /* see TMF_INITIAL, etc.*/
++ struct timer_list tmabort_timer;
++ int tmabort_state; /* see TMABORT_INITIAL, etc.*/
+
+ /* negotiated params */
+- unsigned max_recv_dlength; /* initiator_max_recv_dsl*/
+- unsigned max_xmit_dlength; /* target_max_recv_dsl */
++ 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;
+@@ -205,12 +183,6 @@ struct iscsi_conn {
+ /* values userspace uses to id a conn */
+ int persistent_port;
+ char *persistent_address;
+- /* remote portal currently connected to */
+- int portal_port;
+- char portal_address[ISCSI_ADDRESS_BUF_LEN];
+- /* local address */
+- int local_port;
+- char local_address[ISCSI_ADDRESS_BUF_LEN];
+
+ /* MIB-statistics */
+ uint64_t txdata_octets;
+@@ -225,66 +197,34 @@ struct iscsi_conn {
+
+ /* custom statistics */
+ uint32_t eh_abort_cnt;
+- uint32_t fmr_unalign_cnt;
+ };
+
+-struct iscsi_pool {
++struct iscsi_queue {
+ struct kfifo *queue; /* FIFO Queue */
+ void **pool; /* Pool of elements */
+ int max; /* Max number of elements */
+ };
+
+-/* Session's states */
+-enum {
+- ISCSI_STATE_FREE = 1,
+- ISCSI_STATE_LOGGED_IN,
+- ISCSI_STATE_FAILED,
+- ISCSI_STATE_TERMINATE,
+- ISCSI_STATE_IN_RECOVERY,
+- ISCSI_STATE_RECOVERY_FAILED,
+- ISCSI_STATE_LOGGING_OUT,
+-};
+-
+ struct iscsi_session {
+- /*
+- * Syncs up the scsi eh thread with the iscsi eh thread when sending
+- * task management functions. This must be taken before the session
+- * and recv lock.
+- */
+- struct mutex eh_mutex;
+-
+ /* iSCSI session-wide sequencing */
+ uint32_t cmdsn;
+ uint32_t exp_cmdsn;
+ uint32_t max_cmdsn;
+
+- /* This tracks the reqs queued into the initiator */
+- uint32_t queued_cmdsn;
+-
+ /* configuration */
+- int abort_timeout;
+- int lu_reset_timeout;
+ int initial_r2t_en;
+- unsigned max_r2t;
++ int max_r2t;
+ int imm_data_en;
+- unsigned first_burst;
+- unsigned max_burst;
++ int first_burst;
++ int max_burst;
+ int time2wait;
+ int time2retain;
+ int pdu_inorder_en;
+ int dataseq_inorder_en;
+ int erl;
+- int fast_abort;
+ int tpgt;
+- char *username;
+- char *username_in;
+- char *password;
+- char *password_in;
+ char *targetname;
+- char *initiatorname;
+- /* hw address or netdev iscsi connection is bound to */
+- char *hwaddress;
+- char *netdev;
++
+ /* control data */
+ struct iscsi_transport *tt;
+ struct Scsi_Host *host;
+@@ -300,10 +240,10 @@ struct iscsi_session {
+
+ int cmds_max; /* size of cmds array */
+ struct iscsi_cmd_task **cmds; /* Original Cmds arr */
+- struct iscsi_pool cmdpool; /* PDU's pool */
++ 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_pool mgmtpool; /* Mgmt PDU's pool */
++ struct iscsi_queue mgmtpool; /* Mgmt PDU's pool */
+ };
+
+ /*
+@@ -312,26 +252,15 @@ struct iscsi_session {
+ 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_eh_device_reset(struct scsi_cmnd *sc);
+ extern int iscsi_queuecommand(struct scsi_cmnd *sc,
+ void (*done)(struct scsi_cmnd *));
+
+-
+-/*
+- * iSCSI host helpers.
+- */
+-extern int iscsi_host_set_param(struct Scsi_Host *shost,
+- enum iscsi_host_param param, char *buf,
+- int buflen);
+-extern int iscsi_host_get_param(struct Scsi_Host *shost,
+- enum iscsi_host_param param, char *buf);
+-
+ /*
+ * session management
+ */
+ extern struct iscsi_cls_session *
+ iscsi_session_setup(struct iscsi_transport *, struct scsi_transport_template *,
+- uint16_t, uint16_t, int, int, uint32_t, uint32_t *);
++ 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 *);
+@@ -343,10 +272,6 @@ extern int iscsi_session_get_param(struct iscsi_cls_session *cls_session,
+ #define session_to_cls(_sess) \
+ hostdata_session(_sess->host->hostdata)
+
+-#define iscsi_session_printk(prefix, _sess, fmt, a...) \
+- iscsi_cls_session_printk(prefix, \
+- (struct iscsi_cls_session *)session_to_cls(_sess), fmt, ##a)
+-
+ /*
+ * connection management
+ */
+@@ -361,47 +286,26 @@ 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);
+
+-#define iscsi_conn_printk(prefix, _c, fmt, a...) \
+- iscsi_cls_conn_printk(prefix, _c->cls_conn, fmt, ##a)
+-
+ /*
+ * pdu and task processing
+ */
+-extern void iscsi_update_cmdsn(struct iscsi_session *, struct iscsi_nopin *);
++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 *);
+-extern void iscsi_requeue_ctask(struct iscsi_cmd_task *ctask);
+-extern void iscsi_free_mgmt_task(struct iscsi_conn *conn,
+- struct iscsi_mgmt_task *mtask);
+
+ /*
+ * generic helpers
+ */
+-extern void iscsi_pool_free(struct iscsi_pool *);
+-extern int iscsi_pool_init(struct iscsi_pool *, int, void ***, int);
+-
+-/*
+- * inline functions to deal with padding.
+- */
+-static inline unsigned int
+-iscsi_padded(unsigned int len)
+-{
+- return (len + ISCSI_PAD_LEN - 1) & ~(ISCSI_PAD_LEN - 1);
+-}
+-
+-static inline unsigned int
+-iscsi_padding(unsigned int len)
+-{
+- len &= (ISCSI_PAD_LEN - 1);
+- if (len)
+- len = ISCSI_PAD_LEN - len;
+- return len;
+-}
++extern void iscsi_pool_free(struct iscsi_queue *, void **);
++extern int iscsi_pool_init(struct iscsi_queue *, int, void ***, int);
+
+ #endif
+diff --git a/include/scsi/scsi_transport_iscsi.h b/include/scsi/scsi_transport_iscsi.h
+index aab1eae..d5c218d 100644
+--- a/include/scsi/scsi_transport_iscsi.h
++++ b/include/scsi/scsi_transport_iscsi.h
+@@ -24,8 +24,6 @@
+ #define SCSI_TRANSPORT_ISCSI_H
+
+ #include <linux/device.h>
+-#include <linux/list.h>
+-#include <linux/mutex.h>
+ #include <scsi/iscsi_if.h>
+
+ struct scsi_transport_template;
+@@ -81,8 +79,7 @@ struct iscsi_transport {
+ char *name;
+ unsigned int caps;
+ /* LLD sets this to indicate what values it can export to sysfs */
+- uint64_t param_mask;
+- uint64_t host_param_mask;
++ unsigned int param_mask;
+ struct scsi_host_template *host_template;
+ /* LLD connection data size */
+ int conndata_size;
+@@ -92,8 +89,7 @@ struct iscsi_transport {
+ unsigned int max_conn;
+ unsigned int max_cmd_len;
+ struct iscsi_cls_session *(*create_session) (struct iscsi_transport *it,
+- struct scsi_transport_template *t, uint16_t, uint16_t,
+- uint32_t sn, uint32_t *hn);
++ 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);
+@@ -109,18 +105,14 @@ struct iscsi_transport {
+ enum iscsi_param param, char *buf);
+ int (*get_session_param) (struct iscsi_cls_session *session,
+ enum iscsi_param param, char *buf);
+- int (*get_host_param) (struct Scsi_Host *shost,
+- enum iscsi_host_param param, char *buf);
+- int (*set_host_param) (struct Scsi_Host *shost,
+- enum iscsi_host_param param, char *buf,
+- int buflen);
+ 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);
+- int (*init_cmd_task) (struct iscsi_cmd_task *ctask);
++ void (*init_cmd_task) (struct iscsi_cmd_task *ctask);
+ void (*init_mgmt_task) (struct iscsi_conn *conn,
+- struct iscsi_mgmt_task *mtask);
++ 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,
+@@ -132,7 +124,7 @@ struct iscsi_transport {
+ uint64_t *ep_handle);
+ int (*ep_poll) (uint64_t ep_handle, int timeout_ms);
+ void (*ep_disconnect) (uint64_t ep_handle);
+- int (*tgt_dscvr) (struct Scsi_Host *shost, enum iscsi_tgt_dscvr type,
++ int (*tgt_dscvr) (enum iscsi_tgt_dscvr type, uint32_t host_no,
+ uint32_t enable, struct sockaddr *dst_addr);
+ };
+
+@@ -149,6 +141,13 @@ 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 */
+@@ -162,25 +161,18 @@ struct iscsi_cls_conn {
+ #define iscsi_dev_to_conn(_dev) \
+ container_of(_dev, struct iscsi_cls_conn, dev)
+
+-#define iscsi_conn_to_session(_conn) \
+- iscsi_dev_to_session(_conn->dev.parent)
+-
+-/* iscsi class session state */
+-enum {
+- ISCSI_SESSION_LOGGED_IN,
+- ISCSI_SESSION_FAILED,
+- ISCSI_SESSION_FREE,
+-};
++/* 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;
+- spinlock_t lock;
+- struct work_struct block_work;
+- struct work_struct unblock_work;
+- struct work_struct scan_work;
+- struct work_struct unbind_work;
+
+ /* recovery fields */
+ int recovery_tmo;
+@@ -188,7 +180,6 @@ struct iscsi_cls_session {
+
+ int target_id;
+
+- int state;
+ int sid; /* session id */
+ void *dd_data; /* LLD private data */
+ struct device dev; /* sysfs transport/container device */
+@@ -205,28 +196,18 @@ struct iscsi_cls_session {
+
+ struct iscsi_host {
+ struct list_head sessions;
+- atomic_t nr_scans;
+ struct mutex mutex;
+- struct workqueue_struct *scan_workq;
+- char scan_workq_name[KOBJ_NAME_LEN];
+ };
+
+ /*
+ * session and connection functions that can be used by HW iSCSI LLDs
+ */
+-#define iscsi_cls_session_printk(prefix, _cls_session, fmt, a...) \
+- dev_printk(prefix, &(_cls_session)->dev, fmt, ##a)
+-
+-#define iscsi_cls_conn_printk(prefix, _cls_conn, fmt, a...) \
+- dev_printk(prefix, &(_cls_conn)->dev, fmt, ##a)
+-
+-extern int iscsi_session_chkready(struct iscsi_cls_session *session);
+ 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_session_event(struct iscsi_cls_session *session,
+- enum iscsi_uevent_e event);
++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);
+@@ -238,6 +219,6 @@ extern struct iscsi_cls_conn *iscsi_create_conn(struct iscsi_cls_session *sess,
+ 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);
+-extern int iscsi_scan_finished(struct Scsi_Host *shost, unsigned long time);
++
+
+ #endif
+--
+1.5.3.8
+
diff --git a/kernel_patches/backport/2.6.9_U5/iscsi_02_add_to_2_6_9.patch b/kernel_patches/backport/2.6.9_U5/iscsi_02_add_to_2_6_9.patch
new file mode 100644
index 0000000..1f05d95
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U5/iscsi_02_add_to_2_6_9.patch
@@ -0,0 +1,180 @@
+diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c
+index 4376840..11dfaf9 100644
+--- a/drivers/scsi/iscsi_tcp.c
++++ b/drivers/scsi/iscsi_tcp.c
+@@ -2145,7 +2145,6 @@ static void iscsi_tcp_session_destroy(struct iscsi_cls_session *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,
+diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
+index d37048c..60f5846 100644
+--- a/drivers/scsi/libiscsi.c
++++ b/drivers/scsi/libiscsi.c
+@@ -1366,7 +1366,6 @@ iscsi_session_setup(struct iscsi_transport *iscsit,
+ 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);
+diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c
+index 8133c22..f1c68f7 100644
+--- a/drivers/scsi/scsi_transport_iscsi.c
++++ b/drivers/scsi/scsi_transport_iscsi.c
+@@ -65,6 +65,8 @@ static DEFINE_SPINLOCK(iscsi_transport_lock);
+ #define cdev_to_iscsi_internal(_cdev) \
+ container_of(_cdev, struct iscsi_internal, cdev)
+
++extern int attribute_container_init(void);
++
+ static void iscsi_transport_release(struct class_device *cdev)
+ {
+ struct iscsi_internal *priv = cdev_to_iscsi_internal(cdev);
+@@ -80,6 +82,17 @@ static struct class iscsi_transport_class = {
+ .release = iscsi_transport_release,
+ };
+
++static void iscsi_host_class_release(struct class_device *class_dev)
++{
++ struct Scsi_Host *shost = transport_class_to_shost(class_dev);
++ put_device(&shost->shost_gendev);
++}
++
++struct class iscsi_host_class = {
++ .name = "iscsi_host",
++ .release = iscsi_host_class_release,
++};
++
+ static ssize_t
+ show_transport_handle(struct class_device *cdev, char *buf)
+ {
+@@ -115,10 +128,8 @@ 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)
++static int iscsi_setup_host(struct Scsi_Host *shost)
+ {
+- struct Scsi_Host *shost = dev_to_shost(dev);
+ struct iscsi_host *ihost = shost->shost_data;
+
+ memset(ihost, 0, sizeof(*ihost));
+@@ -127,12 +138,6 @@ static int iscsi_setup_host(struct transport_container *tc, struct device *dev,
+ 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,
+@@ -216,24 +221,6 @@ 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 =
+@@ -362,8 +349,6 @@ void iscsi_remove_session(struct iscsi_cls_session *session)
+ list_del(&session->host_list);
+ mutex_unlock(&ihost->mutex);
+
+- scsi_remove_target(&session->dev);
+-
+ transport_unregister_device(&session->dev);
+ device_del(&session->dev);
+ }
+@@ -1269,24 +1254,6 @@ static int iscsi_conn_match(struct attribute_container *cont,
+ 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)
+ {
+@@ -1306,7 +1273,6 @@ iscsi_register_transport(struct iscsi_transport *tt)
+ 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);
+@@ -1319,12 +1285,10 @@ iscsi_register_transport(struct iscsi_transport *tt)
+ 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_attrs = &priv->host_attrs[0];
++ priv->t.host_class = &iscsi_host_class;
++ priv->t.host_setup = iscsi_setup_host;
+ 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];
+@@ -1402,7 +1366,6 @@ int iscsi_unregister_transport(struct iscsi_transport *tt)
+
+ 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);
+@@ -1420,6 +1420,7 @@ static __init int iscsi_transport_init(void)
+ ISCSI_TRANSPORT_VERSION);
+
+ atomic_set(&iscsi_session_nr, 0);
++ attribute_container_init();
+
+ err = class_register(&iscsi_transport_class);
+ if (err)
+ return err;
diff --git a/kernel_patches/backport/2.6.9_U5/iscsi_03_add_session_wq.patch b/kernel_patches/backport/2.6.9_U5/iscsi_03_add_session_wq.patch
new file mode 100644
index 0000000..55564c5
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U5/iscsi_03_add_session_wq.patch
@@ -0,0 +1,76 @@
+diff --git a/drivers/infiniband/ulp/iser/iser_initiator.c b/drivers/infiniband/ulp/iser/iser_initiator.c
+index a6f2303..5d62cc0 100644
+--- a/drivers/infiniband/ulp/iser/iser_initiator.c
++++ b/drivers/infiniband/ulp/iser/iser_initiator.c
+@@ -610,7 +610,7 @@ void iser_snd_completion(struct iser_desc *tx_desc)
+
+ if (resume_tx) {
+ iser_dbg("%ld resuming tx\n",jiffies);
+- scsi_queue_work(conn->session->host, &conn->xmitwork);
++ queue_work(conn->session->wq, &conn->xmitwork);
+ }
+
+ if (tx_desc->type == ISCSI_TX_CONTROL) {
+diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
+index e8020a5..43e9128 100644
+--- a/drivers/scsi/libiscsi.c
++++ b/drivers/scsi/libiscsi.c
+@@ -828,7 +828,7 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
+ session->cmdsn, session->max_cmdsn - session->exp_cmdsn + 1);
+ spin_unlock(&session->lock);
+
+- scsi_queue_work(host, &conn->xmitwork);
++ queue_work(session->wq, &conn->xmitwork);
+ return 0;
+
+ reject:
+@@ -928,7 +928,7 @@ iscsi_conn_send_generic(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ else
+ __kfifo_put(conn->mgmtqueue, (void*)&mtask, sizeof(void*));
+
+- scsi_queue_work(session->host, &conn->xmitwork);
++ queue_work(session->wq, &conn->xmitwork);
+ return 0;
+ }
+
+@@ -1415,6 +1415,9 @@ iscsi_session_setup(struct iscsi_transport *iscsit,
+ INIT_LIST_HEAD(&mtask->running);
+ }
+
++ session->wq = create_singlethread_workqueue("");
++ BUG_ON(!session->wq);
++
+ if (scsi_add_host(shost, NULL))
+ goto add_host_fail;
+
+@@ -1462,6 +1465,8 @@ void iscsi_session_teardown(struct iscsi_cls_session *cls_session)
+
+ kfree(session->targetname);
+
++ destroy_workqueue(session->wq);
++
+ iscsi_destroy_session(cls_session);
+ scsi_host_put(shost);
+ module_put(owner);
+@@ -1595,7 +1600,7 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn)
+ }
+
+ /* flush queued up work because we free the connection below */
+- scsi_flush_work(session->host);
++ flush_workqueue(session->wq);
+
+ spin_lock_bh(&session->lock);
+ kfree(conn->data);
+diff --git a/include/scsi/libiscsi.h b/include/scsi/libiscsi.h
+index ea0816d..e8a95f5 100644
+--- a/include/scsi/libiscsi.h
++++ b/include/scsi/libiscsi.h
+@@ -244,6 +244,8 @@ struct iscsi_session {
+ int mgmtpool_max; /* size of mgmt array */
+ struct iscsi_mgmt_task **mgmt_cmds; /* Original mgmt arr */
+ struct iscsi_queue mgmtpool; /* Mgmt PDU's pool */
++
++ struct workqueue_struct *wq;
+ };
+
+ /*
diff --git a/kernel_patches/backport/2.6.9_U5/iscsi_04_inet_sock_to_opt.patch b/kernel_patches/backport/2.6.9_U5/iscsi_04_inet_sock_to_opt.patch
new file mode 100644
index 0000000..1fb2376
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U5/iscsi_04_inet_sock_to_opt.patch
@@ -0,0 +1,13 @@
+diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c
+index 905efc4..f73a743 100644
+--- a/drivers/scsi/iscsi_tcp.c
++++ b/drivers/scsi/iscsi_tcp.c
+@@ -2027,7 +2027,7 @@ iscsi_tcp_conn_get_param(struct iscsi_cls_conn *cls_conn,
+ {
+ struct iscsi_conn *conn = cls_conn->dd_data;
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+- struct inet_sock *inet;
++ struct inet_opt *inet;
+ struct ipv6_pinfo *np;
+ struct sock *sk;
+ int len;
diff --git a/kernel_patches/backport/2.6.9_U5/iscsi_05_release_host_lock_before_eh.patch b/kernel_patches/backport/2.6.9_U5/iscsi_05_release_host_lock_before_eh.patch
new file mode 100644
index 0000000..c994506
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U5/iscsi_05_release_host_lock_before_eh.patch
@@ -0,0 +1,60 @@
+diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
+index 7db081b..211944e 100644
+--- a/drivers/scsi/libiscsi.c
++++ b/drivers/scsi/libiscsi.c
+@@ -968,12 +968,14 @@ int iscsi_eh_host_reset(struct scsi_cmnd *sc)
+ struct iscsi_conn *conn = session->leadconn;
+ int fail_session = 0;
+
++ spin_unlock_irq(host->host_lock);
+ 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);
++ spin_lock_irq(host->host_lock);
+ return FAILED;
+ }
+
+@@ -1005,6 +1007,7 @@ failed:
+ else
+ goto failed;
+ spin_unlock_bh(&session->lock);
++ spin_lock_irq(host->host_lock);
+
+ return SUCCESS;
+ }
+@@ -1162,13 +1165,16 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
+ struct iscsi_conn *conn;
+ struct iscsi_session *session;
+ int rc;
++ struct Scsi_Host *shost = sc->device->host;
+
++ spin_unlock_irq(shost->host_lock);
+ /*
+ * 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");
++ spin_lock_irq(shost->host_lock);
+ return SUCCESS;
+ }
+
+@@ -1253,6 +1259,7 @@ success_cleanup:
+
+ success_rel_mutex:
+ mutex_unlock(&conn->xmitmutex);
++ spin_lock_irq(shost->host_lock);
+ return SUCCESS;
+
+ failed:
+@@ -1260,6 +1267,7 @@ failed:
+ mutex_unlock(&conn->xmitmutex);
+
+ debug_scsi("abort failed [sc %lx itt 0x%x]\n", (long)sc, ctask->itt);
++ spin_lock_irq(shost->host_lock);
+ return FAILED;
+ }
+ EXPORT_SYMBOL_GPL(iscsi_eh_abort);
diff --git a/kernel_patches/backport/2.6.9_U5/iscsi_06_scsi_addons.patch b/kernel_patches/backport/2.6.9_U5/iscsi_06_scsi_addons.patch
new file mode 100644
index 0000000..a114696
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U5/iscsi_06_scsi_addons.patch
@@ -0,0 +1,75 @@
+diff --git a/drivers/scsi/init.c b/drivers/scsi/init.c
+new file mode 100644
+index 0000000..58cf933
+--- /dev/null
++++ b/drivers/scsi/init.c
+@@ -0,0 +1 @@
++#include "src/init.c"
+diff --git a/drivers/scsi/attribute_container.c b/drivers/scsi/attribute_container.c
+new file mode 100644
+index 0000000..58cf933
+--- /dev/null
++++ b/drivers/scsi/attribute_container.c
+@@ -0,0 +1 @@
++#include "../drivers/base/attribute_container.c"
+diff --git a/drivers/scsi/transport_class.c b/drivers/scsi/transport_class.c
+new file mode 100644
+index 0000000..58cf933
+--- /dev/null
++++ b/drivers/scsi/transport_class.c
+@@ -0,0 +1 @@
++#include "../drivers/base/transport_class.c"
+diff --git a/drivers/scsi/klist.c b/drivers/scsi/klist.c
+new file mode 100644
+index 0000000..58cf933
+--- /dev/null
++++ b/drivers/scsi/klist.c
+@@ -0,0 +1 @@
++#include "../../lib/klist.c"
+diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
+new file mode 100644
+index 0000000..58cf933
+--- /dev/null
++++ b/drivers/scsi/scsi.c
+@@ -0,0 +1 @@
++#include "src/scsi.c"
+diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
+new file mode 100644
+index 0000000..58cf933
+--- /dev/null
++++ b/drivers/scsi/scsi_lib.c
+@@ -0,0 +1 @@
++#include "src/scsi_lib.c"
+diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
+new file mode 100644
+index 0000000..58cf933
+--- /dev/null
++++ b/drivers/scsi/scsi_scan.c
+@@ -0,0 +1 @@
++#include "src/scsi_scan.c"
+diff --git a/drivers/scsi/libiscsi_f.c b/drivers/scsi/libiscsi_f.c
+new file mode 100644
+index 0000000..58cf933
+--- /dev/null
++++ b/drivers/scsi/libiscsi_f.c
+@@ -0,0 +1 @@
++#include "libiscsi.c"
+diff --git a/drivers/scsi/scsi_transport_iscsi_f.c b/drivers/scsi/scsi_transport_iscsi_f.c
+new file mode 100644
+index 0000000..58cf933
+--- /dev/null
++++ b/drivers/scsi/scsi_transport_iscsi_f.c
+@@ -0,0 +1 @@
++#include "scsi_transport_iscsi.c"
+diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
+index e212608..3bf2015 100644
+--- a/drivers/scsi/Makefile
++++ b/drivers/scsi/Makefile
+@@ -3,2 +3,7 @@
+ obj-$(CONFIG_SCSI_ISCSI_ATTRS) += scsi_transport_iscsi.o
+ obj-$(CONFIG_ISCSI_TCP) += libiscsi.o iscsi_tcp.o
++
++CFLAGS_attribute_container.o = $(BACKPORT_INCLUDES)/src/
++
++scsi_transport_iscsi-y := scsi_transport_iscsi_f.o scsi.o scsi_lib.o init.o klist.o attribute_container.o transport_class.o
++libiscsi-y := libiscsi_f.o scsi_scan.o
diff --git a/kernel_patches/backport/2.6.9_U5/iser_01_revert_da9c0c770e775e655e3f77c96d91ee557b117adb.patch b/kernel_patches/backport/2.6.9_U5/iser_01_revert_da9c0c770e775e655e3f77c96d91ee557b117adb.patch
new file mode 100644
index 0000000..101fdc6
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U5/iser_01_revert_da9c0c770e775e655e3f77c96d91ee557b117adb.patch
@@ -0,0 +1,44 @@
+diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
+index bad8dac..75ecabe 100644
+--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
++++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
+@@ -211,10 +211,10 @@ iscsi_iser_ctask_xmit(struct iscsi_conn *conn,
+ int error = 0;
+
+ if (ctask->sc->sc_data_direction == DMA_TO_DEVICE) {
+- BUG_ON(scsi_bufflen(ctask->sc) == 0);
++ BUG_ON(ctask->sc->request_bufflen == 0);
+
+ debug_scsi("cmd [itt %x total %d imm %d unsol_data %d\n",
+- ctask->itt, scsi_bufflen(ctask->sc),
++ ctask->itt, ctask->sc->request_bufflen,
+ ctask->imm_count, ctask->unsol_count);
+ }
+
+diff --git a/drivers/infiniband/ulp/iser/iser_initiator.c b/drivers/infiniband/ulp/iser/iser_initiator.c
+index 5d62cc0..1ae80d8 100644
+--- a/drivers/infiniband/ulp/iser/iser_initiator.c
++++ b/drivers/infiniband/ulp/iser/iser_initiator.c
+@@ -349,12 +349,18 @@ int iser_send_command(struct iscsi_conn *conn,
+ else
+ data_buf = &iser_ctask->data[ISER_DIR_OUT];
+
+- if (scsi_sg_count(sc)) { /* using a scatter list */
+- data_buf->buf = scsi_sglist(sc);
+- data_buf->size = scsi_sg_count(sc);
++ if (sc->use_sg) { /* using a scatter list */
++ data_buf->buf = sc->request_buffer;
++ data_buf->size = sc->use_sg;
++ } else if (sc->request_bufflen) {
++ /* using a single buffer - convert it into one entry SG */
++ sg_init_one(&data_buf->sg_single,
++ sc->request_buffer, sc->request_bufflen);
++ data_buf->buf = &data_buf->sg_single;
++ data_buf->size = 1;
+ }
+
+- data_buf->data_len = scsi_bufflen(sc);
++ data_buf->data_len = sc->request_bufflen;
+
+ if (hdr->flags & ISCSI_FLAG_CMD_READ) {
+ err = iser_prepare_read_cmd(ctask, edtl);
diff --git a/kernel_patches/backport/2.6.9_U5/iser_02_revert_d8196ed2181b4595eaf464a5bcbddb6c28649a39.patch b/kernel_patches/backport/2.6.9_U5/iser_02_revert_d8196ed2181b4595eaf464a5bcbddb6c28649a39.patch
new file mode 100644
index 0000000..7b21cba
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U5/iser_02_revert_d8196ed2181b4595eaf464a5bcbddb6c28649a39.patch
@@ -0,0 +1,12 @@
+diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
+index bad8dac..933429b 100644
+--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
++++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
+@@ -586,7 +586,6 @@ static struct iscsi_transport iscsi_iser_transport = {
+ ISCSI_FAST_ABORT | ISCSI_ABORT_TMO |
+ ISCSI_PING_TMO | ISCSI_RECV_TMO,
+ .host_param_mask = ISCSI_HOST_HWADDRESS |
+- ISCSI_HOST_NETDEV_NAME |
+ ISCSI_HOST_INITIATOR_NAME,
+ .host_template = &iscsi_iser_sht,
+ .conndata_size = sizeof(struct iscsi_conn),
diff --git a/kernel_patches/backport/2.6.9_U5/iser_03_revert_1548271ece9e9312fd5feb41fd58773b56a71d39.patch b/kernel_patches/backport/2.6.9_U5/iser_03_revert_1548271ece9e9312fd5feb41fd58773b56a71d39.patch
new file mode 100644
index 0000000..d72eb5a
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U5/iser_03_revert_1548271ece9e9312fd5feb41fd58773b56a71d39.patch
@@ -0,0 +1,74 @@
+diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
+index bad8dac..7baac99 100644
+--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
++++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
+@@ -368,8 +368,7 @@ static struct iscsi_transport iscsi_iser_transport;
+ static struct iscsi_cls_session *
+ iscsi_iser_session_create(struct iscsi_transport *iscsit,
+ struct scsi_transport_template *scsit,
+- uint16_t cmds_max, uint16_t qdepth,
+- uint32_t initial_cmdsn, uint32_t *hostno)
++ uint32_t initial_cmdsn, uint32_t *hostno)
+ {
+ struct iscsi_cls_session *cls_session;
+ struct iscsi_session *session;
+@@ -380,13 +380,7 @@ iscsi_iser_session_create(struct iscsi_transport *iscsit,
+ struct iscsi_iser_cmd_task *iser_ctask;
+ struct iser_desc *desc;
+
+- /*
+- * we do not support setting can_queue cmd_per_lun from userspace yet
+- * because we preallocate so many resources
+- */
+ cls_session = iscsi_session_setup(iscsit, scsit,
+- ISCSI_DEF_XMIT_CMDS_MAX,
+- ISCSI_MAX_CMD_PER_LUN,
+ sizeof(struct iscsi_iser_cmd_task),
+ sizeof(struct iser_desc),
+ initial_cmdsn, &hn);
+@@ -550,7 +550,7 @@ static struct scsi_host_template iscsi_iser_sht = {
+ .name = "iSCSI Initiator over iSER, v." DRV_VER,
+ .queuecommand = iscsi_queuecommand,
+ .change_queue_depth = iscsi_change_queue_depth,
+- .can_queue = ISCSI_DEF_XMIT_CMDS_MAX - 1,
++ .can_queue = ISCSI_XMIT_CMDS_MAX - 1,
+ .sg_tablesize = ISCSI_ISER_SG_TABLESIZE,
+ .max_sectors = 1024,
+ .cmd_per_lun = ISCSI_MAX_CMD_PER_LUN,
+diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.h b/drivers/infiniband/ulp/iser/iscsi_iser.h
+index 1ee867b..671faff 100644
+--- a/drivers/infiniband/ulp/iser/iscsi_iser.h
++++ b/drivers/infiniband/ulp/iser/iscsi_iser.h
+@@ -105,7 +105,7 @@
+ #define ISER_MAX_TX_MISC_PDUS 6 /* NOOP_OUT(2), TEXT(1), *
+ * SCSI_TMFUNC(2), LOGOUT(1) */
+
+-#define ISER_QP_MAX_RECV_DTOS (ISCSI_DEF_XMIT_CMDS_MAX + \
++#define ISER_QP_MAX_RECV_DTOS (ISCSI_XMIT_CMDS_MAX + \
+ ISER_MAX_RX_MISC_PDUS + \
+ ISER_MAX_TX_MISC_PDUS)
+
+@@ -117,7 +117,7 @@
+
+ #define ISER_INFLIGHT_DATAOUTS 8
+
+-#define ISER_QP_MAX_REQ_DTOS (ISCSI_DEF_XMIT_CMDS_MAX * \
++#define ISER_QP_MAX_REQ_DTOS (ISCSI_XMIT_CMDS_MAX * \
+ (1 + ISER_INFLIGHT_DATAOUTS) + \
+ ISER_MAX_TX_MISC_PDUS + \
+ ISER_MAX_RX_MISC_PDUS)
+diff --git a/drivers/infiniband/ulp/iser/iser_verbs.c b/drivers/infiniband/ulp/iser/iser_verbs.c
+index 654a4dc..f3d8ba5 100644
+--- a/drivers/infiniband/ulp/iser/iser_verbs.c
++++ b/drivers/infiniband/ulp/iser/iser_verbs.c
+@@ -154,8 +154,8 @@ static int iser_create_ib_conn_res(struct iser_conn *ib_conn)
+ params.max_pages_per_fmr = ISCSI_ISER_SG_TABLESIZE + 1;
+ /* make the pool size twice the max number of SCSI commands *
+ * the ML is expected to queue, watermark for unmap at 50% */
+- params.pool_size = ISCSI_DEF_XMIT_CMDS_MAX * 2;
+- params.dirty_watermark = ISCSI_DEF_XMIT_CMDS_MAX;
++ params.pool_size = ISCSI_XMIT_CMDS_MAX * 2;
++ params.dirty_watermark = ISCSI_XMIT_CMDS_MAX;
+ params.cache = 0;
+ params.flush_function = NULL;
+ params.access = (IB_ACCESS_LOCAL_WRITE |
diff --git a/kernel_patches/backport/2.6.9_U5/iser_04_revert_77a23c21aaa723f6b0ffc4a701be8c8e5a32346d.patch b/kernel_patches/backport/2.6.9_U5/iser_04_revert_77a23c21aaa723f6b0ffc4a701be8c8e5a32346d.patch
new file mode 100644
index 0000000..26fa09c
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U5/iser_04_revert_77a23c21aaa723f6b0ffc4a701be8c8e5a32346d.patch
@@ -0,0 +1,38 @@
+diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
+index 8f7b859..5f82d6c 100644
+--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
++++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
+@@ -134,9 +134,18 @@ iscsi_iser_cmd_init(struct iscsi_cmd_task *ctask)
+ {
+ struct iscsi_iser_conn *iser_conn = ctask->conn->dd_data;
+ struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
++ struct scsi_cmnd *sc = ctask->sc;
+
+ iser_ctask->command_sent = 0;
+ iser_ctask->iser_conn = iser_conn;
++ if (sc->sc_data_direction == DMA_TO_DEVICE) {
++ BUG_ON(sc->request_bufflen == 0);
++
++ debug_scsi("cmd [itt %x total %d imm %d unsol_data %d\n",
++ ctask->itt, sc->request_bufflen, ctask->imm_count,
++ ctask->unsol_count);
++ }
++
+ iser_ctask_rdma_init(iser_ctask);
+ return 0;
+ }
+@@ -210,14 +219,6 @@ iscsi_iser_ctask_xmit(struct iscsi_conn *conn,
+ struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
+ int error = 0;
+
+- if (ctask->sc->sc_data_direction == DMA_TO_DEVICE) {
+- BUG_ON(ctask->sc->request_bufflen == 0);
+-
+- debug_scsi("cmd [itt %x total %d imm %d unsol_data %d\n",
+- ctask->itt, ctask->sc->request_bufflen,
+- ctask->imm_count, ctask->unsol_count);
+- }
+-
+ debug_scsi("ctask deq [cid %d itt 0x%x]\n",
+ conn->id, ctask->itt);
+
diff --git a/kernel_patches/backport/2.6.9_U5/iser_05_revert_b2c6416736b847b91950bd43cc5153e11a1f83ee.patch b/kernel_patches/backport/2.6.9_U5/iser_05_revert_b2c6416736b847b91950bd43cc5153e11a1f83ee.patch
new file mode 100644
index 0000000..417415f
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U5/iser_05_revert_b2c6416736b847b91950bd43cc5153e11a1f83ee.patch
@@ -0,0 +1,18 @@
+diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
+index 5f82d6c..3a67d76 100644
+--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
++++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
+@@ -574,11 +574,8 @@ static struct iscsi_transport iscsi_iser_transport = {
+ ISCSI_EXP_STATSN |
+ ISCSI_PERSISTENT_PORT |
+ ISCSI_PERSISTENT_ADDRESS |
+- ISCSI_TARGET_NAME | ISCSI_TPGT |
+- ISCSI_USERNAME | ISCSI_PASSWORD |
+- ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN |
+- ISCSI_FAST_ABORT | ISCSI_ABORT_TMO |
+- ISCSI_PING_TMO | ISCSI_RECV_TMO,
++ ISCSI_TARGET_NAME |
++ ISCSI_TPGT,
+ .host_param_mask = ISCSI_HOST_HWADDRESS |
+ ISCSI_HOST_INITIATOR_NAME,
+ .host_template = &iscsi_iser_sht,
diff --git a/kernel_patches/backport/2.6.9_U5/iser_06_revert_857ae0bdb72999936a28ce621e38e2e288c485da.patch b/kernel_patches/backport/2.6.9_U5/iser_06_revert_857ae0bdb72999936a28ce621e38e2e288c485da.patch
new file mode 100644
index 0000000..0b1a4c4
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U5/iser_06_revert_857ae0bdb72999936a28ce621e38e2e288c485da.patch
@@ -0,0 +1,16 @@
+diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
+index c5941fa..2f4f125 100644
+--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
++++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
+@@ -140,8 +140,8 @@ iscsi_iser_cmd_init(struct iscsi_cmd_task *ctask)
+ iser_ctask->iser_conn = iser_conn;
+ if (sc->sc_data_direction == DMA_TO_DEVICE) {
+- BUG_ON(sc->request_bufflen == 0);
++ BUG_ON(ctask->total_length == 0);
+
+ debug_scsi("cmd [itt %x total %d imm %d unsol_data %d\n",
+- ctask->itt, sc->request_bufflen, ctask->imm_count,
++ ctask->itt, ctask->total_length, ctask->imm_count,
+ ctask->unsol_count);
+ }
+
diff --git a/kernel_patches/backport/2.6.9_U5/iser_07_revert_8ad5781ae9702a8f95cfdf30967752e4297613ee.patch b/kernel_patches/backport/2.6.9_U5/iser_07_revert_8ad5781ae9702a8f95cfdf30967752e4297613ee.patch
new file mode 100644
index 0000000..f207af3
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U5/iser_07_revert_8ad5781ae9702a8f95cfdf30967752e4297613ee.patch
@@ -0,0 +1,14 @@
+diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
+index 2f4f125..940bf98 100644
+--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
++++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
+@@ -576,8 +576,7 @@ static struct iscsi_transport iscsi_iser_transport = {
+ ISCSI_PERSISTENT_ADDRESS |
+ ISCSI_TARGET_NAME |
+ ISCSI_TPGT,
+- .host_param_mask = ISCSI_HOST_HWADDRESS |
+- ISCSI_HOST_INITIATOR_NAME,
++ .host_param_mask = ISCSI_HOST_HWADDRESS,
+ .host_template = &iscsi_iser_sht,
+ .conndata_size = sizeof(struct iscsi_conn),
+ .max_lun = ISCSI_ISER_MAX_LUN,
diff --git a/kernel_patches/backport/2.6.9_U5/iser_08_revert_0801c242a33426fddc005c2f559a3d2fa6fca7eb.patch b/kernel_patches/backport/2.6.9_U5/iser_08_revert_0801c242a33426fddc005c2f559a3d2fa6fca7eb.patch
new file mode 100644
index 0000000..f9dceb1
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U5/iser_08_revert_0801c242a33426fddc005c2f559a3d2fa6fca7eb.patch
@@ -0,0 +1,22 @@
+diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
+index 940bf98..6a35eff 100644
+--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
++++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
+@@ -576,7 +576,6 @@ static struct iscsi_transport iscsi_iser_transport = {
+ ISCSI_PERSISTENT_ADDRESS |
+ ISCSI_TARGET_NAME |
+ ISCSI_TPGT,
+- .host_param_mask = ISCSI_HOST_HWADDRESS,
+ .host_template = &iscsi_iser_sht,
+ .conndata_size = sizeof(struct iscsi_conn),
+ .max_lun = ISCSI_ISER_MAX_LUN,
+@@ -593,9 +593,6 @@ static struct iscsi_transport iscsi_iser_transport = {
+ .get_session_param = iscsi_session_get_param,
+ .start_conn = iscsi_iser_conn_start,
+ .stop_conn = iscsi_conn_stop,
+- /* iscsi host params */
+- .get_host_param = iscsi_host_get_param,
+- .set_host_param = iscsi_host_set_param,
+ /* IO */
+ .send_pdu = iscsi_conn_send_pdu,
+ .get_stats = iscsi_iser_conn_get_stats,
diff --git a/kernel_patches/backport/2.6.9_U5/iser_09_fix_inclusion_order.patch b/kernel_patches/backport/2.6.9_U5/iser_09_fix_inclusion_order.patch
new file mode 100644
index 0000000..3c2a969
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U5/iser_09_fix_inclusion_order.patch
@@ -0,0 +1,13 @@
+--- linux-2.6.20-rc7-orig/drivers/infiniband/ulp/iser/iscsi_iser.c 2007-02-08 09:13:43.000000000 +0200
++++ linux-2.6.20-rc7/drivers/infiniband/ulp/iser/iscsi_iser.c 2007-02-08 09:14:31.000000000 +0200
+@@ -70,9 +70,8 @@
+ #include <scsi/scsi_tcq.h>
+ #include <scsi/scsi_host.h>
+ #include <scsi/scsi.h>
+-#include <scsi/scsi_transport_iscsi.h>
+-
+ #include "iscsi_iser.h"
++#include <scsi/scsi_transport_iscsi.h>
+
+ static unsigned int iscsi_max_lun = 512;
+ module_param_named(max_lun, iscsi_max_lun, uint, S_IRUGO);
diff --git a/kernel_patches/backport/2.6.9_U5/iser_10_fix_struct_scsi_host_template.patch b/kernel_patches/backport/2.6.9_U5/iser_10_fix_struct_scsi_host_template.patch
new file mode 100644
index 0000000..5b28ac4
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U5/iser_10_fix_struct_scsi_host_template.patch
@@ -0,0 +1,31 @@
+From 828e0ad429b92cf75781770ceb9ef7086f34fde2 Mon Sep 17 00:00:00 2001
+From: Doron Shoham <dorons at voltaire.com>
+Date: Thu, 3 Jul 2008 14:31:42 +0300
+Subject: [PATCH] fix_struct_scsi_host_template
+
+Signed-off-by: Doron Shoham <dorons at voltaire.com>
+---
+ drivers/infiniband/ulp/iser/iscsi_iser.c | 2 --
+ 1 files changed, 0 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
+index 9bf24c6..de1e783 100644
+--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
++++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
+@@ -542,13 +542,11 @@ static struct scsi_host_template iscsi_iser_sht = {
+ .module = THIS_MODULE,
+ .name = "iSCSI Initiator over iSER, v." DRV_VER,
+ .queuecommand = iscsi_queuecommand,
+- .change_queue_depth = iscsi_change_queue_depth,
+ .can_queue = ISCSI_XMIT_CMDS_MAX - 1,
+ .sg_tablesize = ISCSI_ISER_SG_TABLESIZE,
+ .max_sectors = 1024,
+ .cmd_per_lun = ISCSI_MAX_CMD_PER_LUN,
+ .eh_abort_handler = iscsi_eh_abort,
+- .eh_device_reset_handler= iscsi_eh_device_reset,
+ .eh_host_reset_handler = iscsi_eh_host_reset,
+ .use_clustering = DISABLE_CLUSTERING,
+ .proc_name = "iscsi_iser",
+--
+1.5.3.8
+
diff --git a/kernel_patches/backport/2.6.9_U5/iser_11_add_fmr_unalign_cnt.patch b/kernel_patches/backport/2.6.9_U5/iser_11_add_fmr_unalign_cnt.patch
new file mode 100644
index 0000000..ef2a2d6
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U5/iser_11_add_fmr_unalign_cnt.patch
@@ -0,0 +1,25 @@
+From 1255c8e5209ce19644e83e353c260f2eddc62cca Mon Sep 17 00:00:00 2001
+From: Doron Shoham <dorons at voltaire.com>
+Date: Thu, 3 Jul 2008 14:54:57 +0300
+Subject: [PATCH] add fmr_unalign_cnt to struct iscsi_conn
+
+Signed-off-by: Doron Shoham <dorons at voltaire.com>
+---
+ include/scsi/libiscsi.h | 1 +
+ 1 files changed, 1 insertions(+), 0 deletions(-)
+
+diff --git a/include/scsi/libiscsi.h b/include/scsi/libiscsi.h
+index ea0816d..182421f 100644
+--- a/include/scsi/libiscsi.h
++++ b/include/scsi/libiscsi.h
+@@ -197,6 +197,7 @@ struct iscsi_conn {
+
+ /* custom statistics */
+ uint32_t eh_abort_cnt;
++ uint32_t fmr_unalign_cnt;
+ };
+
+ struct iscsi_queue {
+--
+1.5.3.8
+
diff --git a/kernel_patches/backport/2.6.9_U5/iser_12_remove_hdr_max.patch b/kernel_patches/backport/2.6.9_U5/iser_12_remove_hdr_max.patch
new file mode 100644
index 0000000..c475001
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U5/iser_12_remove_hdr_max.patch
@@ -0,0 +1,25 @@
+From 97672ef8a29da5e16774d1de9527b2cc29415e36 Mon Sep 17 00:00:00 2001
+From: Doron Shoham <dorons at voltaire.com>
+Date: Thu, 3 Jul 2008 14:59:16 +0300
+Subject: [PATCH] remove hdr_max
+
+Signed-off-by: Doron Shoham <dorons at voltaire.com>
+---
+ drivers/infiniband/ulp/iser/iscsi_iser.c | 1 -
+ 1 files changed, 0 insertions(+), 1 deletions(-)
+
+diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
+index de1e783..6451e9d 100644
+--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
++++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
+@@ -394,7 +394,6 @@ iscsi_iser_session_create(struct iscsi_transport *iscsit,
+ ctask = session->cmds[i];
+ iser_ctask = ctask->dd_data;
+ ctask->hdr = (struct iscsi_cmd *)&iser_ctask->desc.iscsi_header;
+- ctask->hdr_max = sizeof(iser_ctask->desc.iscsi_header);
+ }
+
+ for (i = 0; i < session->mgmtpool_max; i++) {
+--
+1.5.3.8
+
diff --git a/kernel_patches/backport/2.6.9_U5/iser_13_fix_netlink_kernel_create.patch b/kernel_patches/backport/2.6.9_U5/iser_13_fix_netlink_kernel_create.patch
new file mode 100644
index 0000000..d47df44
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U5/iser_13_fix_netlink_kernel_create.patch
@@ -0,0 +1,26 @@
+From db61fe2c3062d8918e793ddc7e1a8cc3694bf620 Mon Sep 17 00:00:00 2001
+From: Doron Shoham <dorons at voltaire.com>
+Date: Sun, 6 Jul 2008 15:20:42 +0300
+Subject: [PATCH] fix netlink_kernel_create
+
+Signed-off-by: Doron Shoham <dorons at voltaire.com>
+---
+ drivers/scsi/scsi_transport_iscsi.c | 2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c
+index e969ef7..a2f4fb7 100644
+--- a/drivers/scsi/scsi_transport_iscsi.c
++++ b/drivers/scsi/scsi_transport_iscsi.c
+@@ -1401,7 +1401,7 @@ static __init int iscsi_transport_init(void)
+ if (err)
+ goto unregister_conn_class;
+
+- nls = netlink_kernel_create(NETLINK_ISCSI, 1, iscsi_if_rx, NULL,
++ nls = netlink_kernel_create(NULL, NETLINK_ISCSI, 1, iscsi_if_rx, NULL,
+ THIS_MODULE);
+ if (!nls) {
+ err = -ENOBUFS;
+--
+1.5.3.8
+
diff --git a/kernel_patches/backport/2.6.9_U5/iser_14_sync_attribute_container.c_from_ofed1.3.patch b/kernel_patches/backport/2.6.9_U5/iser_14_sync_attribute_container.c_from_ofed1.3.patch
new file mode 100644
index 0000000..e926007
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U5/iser_14_sync_attribute_container.c_from_ofed1.3.patch
@@ -0,0 +1,394 @@
+From bed65721f623039a119b5ff03c6c1fe44a1ccfb3 Mon Sep 17 00:00:00 2001
+From: Doron Shoham <dorons at voltaire.com>
+Date: Sun, 6 Jul 2008 15:26:20 +0300
+Subject: [PATCH] sync attribute_container.c from ofed1.3
+
+Signed-off-by: Doron Shoham <dorons at voltaire.com>
+---
+ drivers/base/attribute_container.c | 100 +++++++++++++++++------------------
+ drivers/base/transport_class.c | 21 ++++----
+ 2 files changed, 60 insertions(+), 61 deletions(-)
+
+diff --git a/drivers/base/attribute_container.c b/drivers/base/attribute_container.c
+index f57652d..7370d7c 100644
+--- a/drivers/base/attribute_container.c
++++ b/drivers/base/attribute_container.c
+@@ -27,21 +27,21 @@
+ struct internal_container {
+ struct klist_node node;
+ struct attribute_container *cont;
+- struct device classdev;
++ struct class_device classdev;
+ };
+
+ static void internal_container_klist_get(struct klist_node *n)
+ {
+ struct internal_container *ic =
+ container_of(n, struct internal_container, node);
+- get_device(&ic->classdev);
++ class_device_get(&ic->classdev);
+ }
+
+ static void internal_container_klist_put(struct klist_node *n)
+ {
+ struct internal_container *ic =
+ container_of(n, struct internal_container, node);
+- put_device(&ic->classdev);
++ class_device_put(&ic->classdev);
+ }
+
+
+@@ -53,7 +53,7 @@ static void internal_container_klist_put(struct klist_node *n)
+ * Returns the container associated with this classdev.
+ */
+ struct attribute_container *
+-attribute_container_classdev_to_container(struct device *classdev)
++attribute_container_classdev_to_container(struct class_device *classdev)
+ {
+ struct internal_container *ic =
+ container_of(classdev, struct internal_container, classdev);
+@@ -61,7 +61,7 @@ attribute_container_classdev_to_container(struct device *classdev)
+ }
+ EXPORT_SYMBOL_GPL(attribute_container_classdev_to_container);
+
+-static LIST_HEAD(attribute_container_list);
++static struct list_head attribute_container_list;
+
+ static DEFINE_MUTEX(attribute_container_mutex);
+
+@@ -110,11 +110,11 @@ attribute_container_unregister(struct attribute_container *cont)
+ EXPORT_SYMBOL_GPL(attribute_container_unregister);
+
+ /* private function used as class release */
+-static void attribute_container_release(struct device *classdev)
++static void attribute_container_release(struct class_device *classdev)
+ {
+ struct internal_container *ic
+ = container_of(classdev, struct internal_container, classdev);
+- struct device *dev = classdev->parent;
++ struct device *dev = classdev->dev;
+
+ kfree(ic);
+ put_device(dev);
+@@ -129,12 +129,12 @@ static void attribute_container_release(struct device *classdev)
+ * This function allocates storage for the class device(s) to be
+ * attached to dev (one for each matching attribute_container). If no
+ * fn is provided, the code will simply register the class device via
+- * device_add. If a function is provided, it is expected to add
++ * class_device_add. If a function is provided, it is expected to add
+ * the class device at the appropriate time. One of the things that
+ * might be necessary is to allocate and initialise the classdev and
+ * then add it a later time. To do this, call this routine for
+ * allocation and initialisation and then use
+- * attribute_container_device_trigger() to call device_add() on
++ * attribute_container_device_trigger() to call class_device_add() on
+ * it. Note: after this, the class device contains a reference to dev
+ * which is not relinquished until the release of the classdev.
+ */
+@@ -142,7 +142,7 @@ void
+ attribute_container_add_device(struct device *dev,
+ int (*fn)(struct attribute_container *,
+ struct device *,
+- struct device *))
++ struct class_device *))
+ {
+ struct attribute_container *cont;
+
+@@ -163,11 +163,11 @@ attribute_container_add_device(struct device *dev,
+ }
+
+ ic->cont = cont;
+- device_initialize(&ic->classdev);
+- ic->classdev.parent = get_device(dev);
++ class_device_initialize(&ic->classdev);
++ ic->classdev.dev = get_device(dev);
+ ic->classdev.class = cont->class;
+- cont->class->dev_release = attribute_container_release;
+- strcpy(ic->classdev.bus_id, dev->bus_id);
++ cont->class->release = attribute_container_release;
++ strcpy(ic->classdev.class_id, dev->bus_id);
+ if (fn)
+ fn(cont, dev, &ic->classdev);
+ else
+@@ -195,19 +195,20 @@ attribute_container_add_device(struct device *dev,
+ * @fn: A function to call to remove the device
+ *
+ * This routine triggers device removal. If fn is NULL, then it is
+- * simply done via device_unregister (note that if something
++ * simply done via class_device_unregister (note that if something
+ * still has a reference to the classdev, then the memory occupied
+ * will not be freed until the classdev is released). If you want a
+ * two phase release: remove from visibility and then delete the
+ * device, then you should use this routine with a fn that calls
+- * device_del() and then use attribute_container_device_trigger()
+- * to do the final put on the classdev.
++ * class_device_del() and then use
++ * attribute_container_device_trigger() to do the final put on the
++ * classdev.
+ */
+ void
+ attribute_container_remove_device(struct device *dev,
+ void (*fn)(struct attribute_container *,
+ struct device *,
+- struct device *))
++ struct class_device *))
+ {
+ struct attribute_container *cont;
+
+@@ -223,14 +224,14 @@ attribute_container_remove_device(struct device *dev,
+ continue;
+
+ klist_for_each_entry(ic, &cont->containers, node, &iter) {
+- if (dev != ic->classdev.parent)
++ if (dev != ic->classdev.dev)
+ continue;
+ klist_del(&ic->node);
+ if (fn)
+ fn(cont, dev, &ic->classdev);
+ else {
+ attribute_container_remove_attrs(&ic->classdev);
+- device_unregister(&ic->classdev);
++ class_device_unregister(&ic->classdev);
+ }
+ }
+ }
+@@ -251,7 +252,7 @@ void
+ attribute_container_device_trigger(struct device *dev,
+ int (*fn)(struct attribute_container *,
+ struct device *,
+- struct device *))
++ struct class_device *))
+ {
+ struct attribute_container *cont;
+
+@@ -269,7 +270,7 @@ attribute_container_device_trigger(struct device *dev,
+ }
+
+ klist_for_each_entry(ic, &cont->containers, node, &iter) {
+- if (dev == ic->classdev.parent)
++ if (dev == ic->classdev.dev)
+ fn(cont, dev, &ic->classdev);
+ }
+ }
+@@ -312,23 +313,18 @@ attribute_container_trigger(struct device *dev,
+ * attributes listed in the container
+ */
+ int
+-attribute_container_add_attrs(struct device *classdev)
++attribute_container_add_attrs(struct class_device *classdev)
+ {
+ struct attribute_container *cont =
+ attribute_container_classdev_to_container(classdev);
+- struct device_attribute **attrs = cont->attrs;
++ struct class_device_attribute **attrs = cont->attrs;
+ int i, error;
+
+- BUG_ON(attrs && cont->grp);
+-
+- if (!attrs && !cont->grp)
++ if (!attrs)
+ return 0;
+
+- if (cont->grp)
+- return sysfs_create_group(&classdev->kobj, cont->grp);
+-
+ for (i = 0; attrs[i]; i++) {
+- error = device_create_file(classdev, attrs[i]);
++ error = class_device_create_file(classdev, attrs[i]);
+ if (error)
+ return error;
+ }
+@@ -337,18 +333,18 @@ attribute_container_add_attrs(struct device *classdev)
+ }
+
+ /**
+- * attribute_container_add_class_device - same function as device_add
++ * attribute_container_add_class_device - same function as class_device_add
+ *
+ * @classdev: the class device to add
+ *
+- * This performs essentially the same function as device_add except for
++ * This performs essentially the same function as class_device_add except for
+ * attribute containers, namely add the classdev to the system and then
+ * create the attribute files
+ */
+ int
+-attribute_container_add_class_device(struct device *classdev)
++attribute_container_add_class_device(struct class_device *classdev)
+ {
+- int error = device_add(classdev);
++ int error = class_device_add(classdev);
+ if (error)
+ return error;
+ return attribute_container_add_attrs(classdev);
+@@ -363,7 +359,7 @@ attribute_container_add_class_device(struct device *classdev)
+ int
+ attribute_container_add_class_device_adapter(struct attribute_container *cont,
+ struct device *dev,
+- struct device *classdev)
++ struct class_device *classdev)
+ {
+ return attribute_container_add_class_device(classdev);
+ }
+@@ -375,23 +371,18 @@ attribute_container_add_class_device_adapter(struct attribute_container *cont,
+ *
+ */
+ void
+-attribute_container_remove_attrs(struct device *classdev)
++attribute_container_remove_attrs(struct class_device *classdev)
+ {
+ struct attribute_container *cont =
+ attribute_container_classdev_to_container(classdev);
+- struct device_attribute **attrs = cont->attrs;
++ struct class_device_attribute **attrs = cont->attrs;
+ int i;
+
+- if (!attrs && !cont->grp)
++ if (!attrs)
+ return;
+
+- if (cont->grp) {
+- sysfs_remove_group(&classdev->kobj, cont->grp);
+- return ;
+- }
+-
+ for (i = 0; attrs[i]; i++)
+- device_remove_file(classdev, attrs[i]);
++ class_device_remove_file(classdev, attrs[i]);
+ }
+
+ /**
+@@ -400,13 +391,13 @@ attribute_container_remove_attrs(struct device *classdev)
+ * @classdev: the class device
+ *
+ * This function simply removes all the attribute files and then calls
+- * device_del.
++ * class_device_del.
+ */
+ void
+-attribute_container_class_device_del(struct device *classdev)
++attribute_container_class_device_del(struct class_device *classdev)
+ {
+ attribute_container_remove_attrs(classdev);
+- device_del(classdev);
++ class_device_del(classdev);
+ }
+
+ /**
+@@ -418,16 +409,16 @@ attribute_container_class_device_del(struct device *classdev)
+ * Looks up the device in the container's list of class devices and returns
+ * the corresponding class_device.
+ */
+-struct device *
++struct class_device *
+ attribute_container_find_class_device(struct attribute_container *cont,
+ struct device *dev)
+ {
+- struct device *cdev = NULL;
++ struct class_device *cdev = NULL;
+ struct internal_container *ic;
+ struct klist_iter iter;
+
+ klist_for_each_entry(ic, &cont->containers, node, &iter) {
+- if (ic->classdev.parent == dev) {
++ if (ic->classdev.dev == dev) {
+ cdev = &ic->classdev;
+ /* FIXME: must exit iterator then break */
+ klist_iter_exit(&iter);
+@@ -438,3 +429,10 @@ attribute_container_find_class_device(struct attribute_container *cont,
+ return cdev;
+ }
+ EXPORT_SYMBOL_GPL(attribute_container_find_class_device);
++
++int __init
++attribute_container_init(void)
++{
++ INIT_LIST_HEAD(&attribute_container_list);
++ return 0;
++}
+diff --git a/drivers/base/transport_class.c b/drivers/base/transport_class.c
+index 84997ef..f25e7c6 100644
+--- a/drivers/base/transport_class.c
++++ b/drivers/base/transport_class.c
+@@ -66,7 +66,7 @@ EXPORT_SYMBOL_GPL(transport_class_unregister);
+
+ static int anon_transport_dummy_function(struct transport_container *tc,
+ struct device *dev,
+- struct device *cdev)
++ struct class_device *cdev)
+ {
+ /* do nothing */
+ return 0;
+@@ -108,14 +108,13 @@ EXPORT_SYMBOL_GPL(anon_transport_class_register);
+ */
+ void anon_transport_class_unregister(struct anon_transport_class *atc)
+ {
+- if (unlikely(attribute_container_unregister(&atc->container)))
+- BUG();
++ attribute_container_unregister(&atc->container);
+ }
+ EXPORT_SYMBOL_GPL(anon_transport_class_unregister);
+
+ static int transport_setup_classdev(struct attribute_container *cont,
+ struct device *dev,
+- struct device *classdev)
++ struct class_device *classdev)
+ {
+ struct transport_class *tclass = class_to_transport_class(cont->class);
+ struct transport_container *tcont = attribute_container_to_transport_container(cont);
+@@ -127,7 +126,9 @@ static int transport_setup_classdev(struct attribute_container *cont,
+ }
+
+ /**
+- * transport_setup_device - declare a new dev for transport class association but don't make it visible yet.
++ * transport_setup_device - declare a new dev for transport class association
++ * but don't make it visible yet.
++ *
+ * @dev: the generic device representing the entity being added
+ *
+ * Usually, dev represents some component in the HBA system (either
+@@ -149,7 +150,7 @@ EXPORT_SYMBOL_GPL(transport_setup_device);
+
+ static int transport_add_class_device(struct attribute_container *cont,
+ struct device *dev,
+- struct device *classdev)
++ struct class_device *classdev)
+ {
+ int error = attribute_container_add_class_device(classdev);
+ struct transport_container *tcont =
+@@ -181,7 +182,7 @@ EXPORT_SYMBOL_GPL(transport_add_device);
+
+ static int transport_configure(struct attribute_container *cont,
+ struct device *dev,
+- struct device *cdev)
++ struct class_device *cdev)
+ {
+ struct transport_class *tclass = class_to_transport_class(cont->class);
+ struct transport_container *tcont = attribute_container_to_transport_container(cont);
+@@ -212,7 +213,7 @@ EXPORT_SYMBOL_GPL(transport_configure_device);
+
+ static int transport_remove_classdev(struct attribute_container *cont,
+ struct device *dev,
+- struct device *classdev)
++ struct class_device *classdev)
+ {
+ struct transport_container *tcont =
+ attribute_container_to_transport_container(cont);
+@@ -251,12 +252,12 @@ EXPORT_SYMBOL_GPL(transport_remove_device);
+
+ static void transport_destroy_classdev(struct attribute_container *cont,
+ struct device *dev,
+- struct device *classdev)
++ struct class_device *classdev)
+ {
+ struct transport_class *tclass = class_to_transport_class(cont->class);
+
+ if (tclass->remove != anon_transport_dummy_function)
+- put_device(classdev);
++ class_device_put(classdev);
+ }
+
+
+--
+1.5.3.8
+
diff --git a/kernel_patches/backport/2.6.9_U5/iser_15_fix_iscsi_free_mgmt_task.patch b/kernel_patches/backport/2.6.9_U5/iser_15_fix_iscsi_free_mgmt_task.patch
new file mode 100644
index 0000000..7a3a3ea
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U5/iser_15_fix_iscsi_free_mgmt_task.patch
@@ -0,0 +1,28 @@
+From 5a9fd2300982aca58f1306bdb98cab878998a607 Mon Sep 17 00:00:00 2001
+From: Doron Shoham <dorons at voltaire.com>
+Date: Sun, 6 Jul 2008 15:53:59 +0300
+Subject: [PATCH] fix iscsi_free_mgmt_task
+
+Signed-off-by: Doron Shoham <dorons at voltaire.com>
+---
+ drivers/infiniband/ulp/iser/iser_initiator.c | 4 +++-
+ 1 files changed, 3 insertions(+), 1 deletions(-)
+
+diff --git a/drivers/infiniband/ulp/iser/iser_initiator.c b/drivers/infiniband/ulp/iser/iser_initiator.c
+index 4e20c8b..e7f2399 100644
+--- a/drivers/infiniband/ulp/iser/iser_initiator.c
++++ b/drivers/infiniband/ulp/iser/iser_initiator.c
+@@ -627,7 +627,9 @@ void iser_snd_completion(struct iser_desc *tx_desc)
+ struct iscsi_session *session = conn->session;
+
+ spin_lock(&conn->session->lock);
+- iscsi_free_mgmt_task(conn, mtask);
++ list_del(&mtask->running);
++ __kfifo_put(session->mgmtpool.queue, (void*)&mtask,
++ sizeof(void*));
+ spin_unlock(&session->lock);
+ }
+ }
+--
+1.5.3.8
+
diff --git a/kernel_patches/backport/2.6.9_U6/iscsi_01_sync_kernel_code_with_ofed_1_2_5.patch b/kernel_patches/backport/2.6.9_U6/iscsi_01_sync_kernel_code_with_ofed_1_2_5.patch
new file mode 100644
index 0000000..aeacec6
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U6/iscsi_01_sync_kernel_code_with_ofed_1_2_5.patch
@@ -0,0 +1,7858 @@
+From a2ee7540d79a7a287205fd9b75311a0cfeb64e38 Mon Sep 17 00:00:00 2001
+From: Doron Shoham <dorons at voltaire.com>
+Date: Thu, 3 Jul 2008 13:12:22 +0300
+Subject: [PATCH] sync_kernel_code_with_ofed_1_2_5
+
+Signed-off-by: Doron Shoham <dorons at voltaire.com>
+---
+ drivers/scsi/iscsi_tcp.c | 2313 +++++++++++++++++++----------------
+ drivers/scsi/iscsi_tcp.h | 131 ++-
+ drivers/scsi/libiscsi.c | 1800 +++++++++------------------
+ drivers/scsi/scsi_transport_iscsi.c | 869 +++++---------
+ include/scsi/iscsi_if.h | 54 -
+ include/scsi/iscsi_proto.h | 22 +-
+ include/scsi/libiscsi.h | 188 +---
+ include/scsi/scsi_transport_iscsi.h | 65 +-
+ 8 files changed, 2301 insertions(+), 3141 deletions(-)
+
+diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c
+index 72b9b2a..c9a3abf 100644
+--- a/drivers/scsi/iscsi_tcp.c
++++ b/drivers/scsi/iscsi_tcp.c
+@@ -29,15 +29,14 @@
+ #include <linux/types.h>
+ #include <linux/list.h>
+ #include <linux/inet.h>
+-#include <linux/file.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_device.h>
+ #include <scsi/scsi_host.h>
+ #include <scsi/scsi.h>
+ #include <scsi/scsi_transport_iscsi.h>
+@@ -48,7 +47,7 @@ MODULE_AUTHOR("Dmitry Yusupov <dmitry_yus at yahoo.com>, "
+ "Alex Aizman <itn780 at yahoo.com>");
+ MODULE_DESCRIPTION("iSCSI/TCP data-path");
+ MODULE_LICENSE("GPL");
+-#undef DEBUG_TCP
++/* #define DEBUG_TCP */
+ #define DEBUG_ASSERT
+
+ #ifdef DEBUG_TCP
+@@ -67,429 +66,118 @@ MODULE_LICENSE("GPL");
+ static unsigned int iscsi_max_lun = 512;
+ module_param_named(max_lun, iscsi_max_lun, uint, S_IRUGO);
+
+-static int iscsi_tcp_hdr_recv_done(struct iscsi_tcp_conn *tcp_conn,
+- struct iscsi_segment *segment);
+-
+-/*
+- * Scatterlist handling: inside the iscsi_segment, we
+- * remember an index into the scatterlist, and set data/size
+- * to the current scatterlist entry. For highmem pages, we
+- * kmap as needed.
+- *
+- * Note that the page is unmapped when we return from
+- * TCP's data_ready handler, so we may end up mapping and
+- * unmapping the same page repeatedly. The whole reason
+- * for this is that we shouldn't keep the page mapped
+- * outside the softirq.
+- */
+-
+-/**
+- * iscsi_tcp_segment_init_sg - init indicated scatterlist entry
+- * @segment: the buffer object
+- * @sg: scatterlist
+- * @offset: byte offset into that sg entry
+- *
+- * This function sets up the segment so that subsequent
+- * data is copied to the indicated sg entry, at the given
+- * offset.
+- */
+ static inline void
+-iscsi_tcp_segment_init_sg(struct iscsi_segment *segment,
+- struct scatterlist *sg, unsigned int offset)
++iscsi_buf_init_iov(struct iscsi_buf *ibuf, char *vbuf, int size)
+ {
+- segment->sg = sg;
+- segment->sg_offset = offset;
+- segment->size = min(sg->length - offset,
+- segment->total_size - segment->total_copied);
+- segment->data = NULL;
++ 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;
+ }
+
+-/**
+- * iscsi_tcp_segment_map - map the current S/G page
+- * @segment: iscsi_segment
+- * @recv: 1 if called from recv path
+- *
+- * We only need to possibly kmap data if scatter lists are being used,
+- * because the iscsi passthrough and internal IO paths will never use high
+- * mem pages.
+- */
+ static inline void
+-iscsi_tcp_segment_map(struct iscsi_segment *segment, int recv)
++iscsi_buf_init_sg(struct iscsi_buf *ibuf, struct scatterlist *sg)
+ {
+- struct scatterlist *sg;
+-
+- if (segment->data != NULL || !segment->sg)
+- return;
+-
+- sg = segment->sg;
+- BUG_ON(segment->sg_mapped);
+- BUG_ON(sg->length == 0);
+-
++ ibuf->sg.page = sg->page;
++ ibuf->sg.offset = sg->offset;
++ ibuf->sg.length = sg->length;
+ /*
+- * If the page count is greater than one it is ok to send
+- * to the network layer's zero copy send path. If not we
+- * have to go the slow sendmsg path. We always map for the
+- * recv path.
++ * Fastpath: sg element fits into single page
+ */
+- if (page_count(sg_page(sg)) >= 1 && !recv)
+- return;
+-
+- debug_tcp("iscsi_tcp_segment_map %s %p\n", recv ? "recv" : "xmit",
+- segment);
+- segment->sg_mapped = kmap_atomic(sg_page(sg), KM_SOFTIRQ0);
+- segment->data = segment->sg_mapped + sg->offset + segment->sg_offset;
++ if (sg->length + sg->offset <= PAGE_SIZE && !PageSlab(sg->page))
++ ibuf->use_sendmsg = 0;
++ else
++ ibuf->use_sendmsg = 1;
++ ibuf->sent = 0;
+ }
+
+-static inline void
+-iscsi_tcp_segment_unmap(struct iscsi_segment *segment)
++static inline int
++iscsi_buf_left(struct iscsi_buf *ibuf)
+ {
+- debug_tcp("iscsi_tcp_segment_unmap %p\n", segment);
++ int rc;
+
+- if (segment->sg_mapped) {
+- debug_tcp("iscsi_tcp_segment_unmap valid\n");
+- kunmap_atomic(segment->sg_mapped, KM_SOFTIRQ0);
+- segment->sg_mapped = NULL;
+- segment->data = NULL;
+- }
++ rc = ibuf->sg.length - ibuf->sent;
++ BUG_ON(rc < 0);
++ return rc;
+ }
+
+-/*
+- * Splice the digest buffer into the buffer
+- */
+ static inline void
+-iscsi_tcp_segment_splice_digest(struct iscsi_segment *segment, void *digest)
++iscsi_hdr_digest(struct iscsi_conn *conn, struct iscsi_buf *buf,
++ u8* crc)
+ {
+- segment->data = digest;
+- segment->digest_len = ISCSI_DIGEST_SIZE;
+- segment->total_size += ISCSI_DIGEST_SIZE;
+- segment->size = ISCSI_DIGEST_SIZE;
+- segment->copied = 0;
+- segment->sg = NULL;
+- segment->hash = NULL;
++ 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;
+ }
+
+-/**
+- * iscsi_tcp_segment_done - check whether the segment is complete
+- * @segment: iscsi segment to check
+- * @recv: set to one of this is called from the recv path
+- * @copied: number of bytes copied
+- *
+- * Check if we're done receiving this segment. If the receive
+- * buffer is full but we expect more data, move on to the
+- * next entry in the scatterlist.
+- *
+- * If the amount of data we received isn't a multiple of 4,
+- * we will transparently receive the pad bytes, too.
+- *
+- * This function must be re-entrant.
+- */
+ static inline int
+-iscsi_tcp_segment_done(struct iscsi_segment *segment, int recv, unsigned copied)
++iscsi_hdr_extract(struct iscsi_tcp_conn *tcp_conn)
+ {
+- static unsigned char padbuf[ISCSI_PAD_LEN];
+- struct scatterlist sg;
+- unsigned int pad;
++ struct sk_buff *skb = tcp_conn->in.skb;
++
++ tcp_conn->in.zero_copy_hdr = 0;
+
+- debug_tcp("copied %u %u size %u %s\n", segment->copied, copied,
+- segment->size, recv ? "recv" : "xmit");
+- if (segment->hash && copied) {
++ if (tcp_conn->in.copy >= tcp_conn->hdr_size &&
++ tcp_conn->in_progress == IN_PROGRESS_WAIT_HEADER) {
+ /*
+- * If a segment is kmapd we must unmap it before sending
+- * to the crypto layer since that will try to kmap it again.
++ * Zero-copy PDU Header: using connection context
++ * to store header pointer.
+ */
+- iscsi_tcp_segment_unmap(segment);
+-
+- if (!segment->data) {
+- sg_init_table(&sg, 1);
+- sg_set_page(&sg, sg_page(segment->sg), copied,
+- segment->copied + segment->sg_offset +
+- segment->sg->offset);
+- } else
+- sg_init_one(&sg, segment->data + segment->copied,
+- copied);
+- crypto_hash_update(segment->hash, &sg, copied);
+- }
+-
+- segment->copied += copied;
+- if (segment->copied < segment->size) {
+- iscsi_tcp_segment_map(segment, recv);
+- return 0;
+- }
+-
+- segment->total_copied += segment->copied;
+- segment->copied = 0;
+- segment->size = 0;
+-
+- /* Unmap the current scatterlist page, if there is one. */
+- iscsi_tcp_segment_unmap(segment);
+-
+- /* Do we have more scatterlist entries? */
+- debug_tcp("total copied %u total size %u\n", segment->total_copied,
+- segment->total_size);
+- if (segment->total_copied < segment->total_size) {
+- /* Proceed to the next entry in the scatterlist. */
+- iscsi_tcp_segment_init_sg(segment, sg_next(segment->sg),
+- 0);
+- iscsi_tcp_segment_map(segment, recv);
+- BUG_ON(segment->size == 0);
+- return 0;
+- }
+-
+- /* Do we need to handle padding? */
+- pad = iscsi_padding(segment->total_copied);
+- if (pad != 0) {
+- debug_tcp("consume %d pad bytes\n", pad);
+- segment->total_size += pad;
+- segment->size = pad;
+- segment->data = padbuf;
+- return 0;
+- }
+-
+- /*
+- * Set us up for transferring the data digest. hdr digest
+- * is completely handled in hdr done function.
+- */
+- if (segment->hash) {
+- crypto_hash_final(segment->hash, segment->digest);
+- iscsi_tcp_segment_splice_digest(segment,
+- recv ? segment->recv_digest : segment->digest);
+- return 0;
+- }
+-
+- return 1;
+-}
+-
+-/**
+- * iscsi_tcp_xmit_segment - transmit segment
+- * @tcp_conn: the iSCSI TCP connection
+- * @segment: the buffer to transmnit
+- *
+- * This function transmits as much of the buffer as
+- * the network layer will accept, and returns the number of
+- * bytes transmitted.
+- *
+- * If CRC hashing is enabled, the function will compute the
+- * hash as it goes. When the entire segment has been transmitted,
+- * it will retrieve the hash value and send it as well.
+- */
+-static int
+-iscsi_tcp_xmit_segment(struct iscsi_tcp_conn *tcp_conn,
+- struct iscsi_segment *segment)
+-{
+- struct socket *sk = tcp_conn->sock;
+- unsigned int copied = 0;
+- int r = 0;
+-
+- while (!iscsi_tcp_segment_done(segment, 0, r)) {
+- struct scatterlist *sg;
+- unsigned int offset, copy;
+- int flags = 0;
+-
+- r = 0;
+- offset = segment->copied;
+- copy = segment->size - offset;
+-
+- if (segment->total_copied + segment->size < segment->total_size)
+- flags |= MSG_MORE;
+-
+- /* Use sendpage if we can; else fall back to sendmsg */
+- if (!segment->data) {
+- sg = segment->sg;
+- offset += segment->sg_offset + sg->offset;
+- r = tcp_conn->sendpage(sk, sg_page(sg), offset, copy,
+- flags);
++ 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 {
+- struct msghdr msg = { .msg_flags = flags };
+- struct kvec iov = {
+- .iov_base = segment->data + offset,
+- .iov_len = copy
+- };
+-
+- r = kernel_sendmsg(sk, &msg, &iov, 1, copy);
+- }
+-
+- if (r < 0) {
+- iscsi_tcp_segment_unmap(segment);
+- if (copied || r == -EAGAIN)
+- break;
+- return r;
++ /* 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;
+ }
+- copied += r;
+- }
+- return copied;
+-}
+-
+-/**
+- * iscsi_tcp_segment_recv - copy data to segment
+- * @tcp_conn: the iSCSI TCP connection
+- * @segment: the buffer to copy to
+- * @ptr: data pointer
+- * @len: amount of data available
+- *
+- * This function copies up to @len bytes to the
+- * given buffer, and returns the number of bytes
+- * consumed, which can actually be less than @len.
+- *
+- * If hash digest is enabled, the function will update the
+- * hash while copying.
+- * Combining these two operations doesn't buy us a lot (yet),
+- * but in the future we could implement combined copy+crc,
+- * just way we do for network layer checksums.
+- */
+-static int
+-iscsi_tcp_segment_recv(struct iscsi_tcp_conn *tcp_conn,
+- struct iscsi_segment *segment, const void *ptr,
+- unsigned int len)
+-{
+- unsigned int copy = 0, copied = 0;
+-
+- while (!iscsi_tcp_segment_done(segment, 1, copy)) {
+- if (copied == len) {
+- debug_tcp("iscsi_tcp_segment_recv copied %d bytes\n",
+- len);
+- break;
+- }
+-
+- copy = min(len - copied, segment->size - segment->copied);
+- debug_tcp("iscsi_tcp_segment_recv copying %d\n", copy);
+- memcpy(segment->data + segment->copied, ptr + copied, copy);
+- copied += copy;
+- }
+- return copied;
+-}
+-
+-static inline void
+-iscsi_tcp_dgst_header(struct hash_desc *hash, const void *hdr, size_t hdrlen,
+- unsigned char digest[ISCSI_DIGEST_SIZE])
+-{
+- struct scatterlist sg;
++ tcp_conn->in.offset += tcp_conn->hdr_size;
++ tcp_conn->in.copy -= tcp_conn->hdr_size;
++ } else {
++ int hdr_remains;
++ int copylen;
+
+- sg_init_one(&sg, hdr, hdrlen);
+- crypto_hash_digest(hash, &sg, hdrlen, digest);
+-}
++ /*
++ * PDU header scattered across SKB's,
++ * copying it... This'll happen quite rarely.
++ */
+
+-static inline int
+-iscsi_tcp_dgst_verify(struct iscsi_tcp_conn *tcp_conn,
+- struct iscsi_segment *segment)
+-{
+- if (!segment->digest_len)
+- return 1;
++ if (tcp_conn->in_progress == IN_PROGRESS_WAIT_HEADER)
++ tcp_conn->in.hdr_offset = 0;
+
+- if (memcmp(segment->recv_digest, segment->digest,
+- segment->digest_len)) {
+- debug_scsi("digest mismatch\n");
+- return 0;
+- }
++ hdr_remains = tcp_conn->hdr_size - tcp_conn->in.hdr_offset;
++ BUG_ON(hdr_remains <= 0);
+
+- return 1;
+-}
++ 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);
+
+-/*
+- * Helper function to set up segment buffer
+- */
+-static inline void
+-__iscsi_segment_init(struct iscsi_segment *segment, size_t size,
+- iscsi_segment_done_fn_t *done, struct hash_desc *hash)
+-{
+- memset(segment, 0, sizeof(*segment));
+- segment->total_size = size;
+- segment->done = done;
+-
+- if (hash) {
+- segment->hash = hash;
+- crypto_hash_init(hash);
+- }
+-}
++ 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);
+
+-static inline void
+-iscsi_segment_init_linear(struct iscsi_segment *segment, void *data,
+- size_t size, iscsi_segment_done_fn_t *done,
+- struct hash_desc *hash)
+-{
+- __iscsi_segment_init(segment, size, done, hash);
+- segment->data = data;
+- segment->size = size;
+-}
+-
+-static inline int
+-iscsi_segment_seek_sg(struct iscsi_segment *segment,
+- struct scatterlist *sg_list, unsigned int sg_count,
+- unsigned int offset, size_t size,
+- iscsi_segment_done_fn_t *done, struct hash_desc *hash)
+-{
+- struct scatterlist *sg;
+- unsigned int i;
+-
+- debug_scsi("iscsi_segment_seek_sg offset %u size %llu\n",
+- offset, size);
+- __iscsi_segment_init(segment, size, done, hash);
+- for_each_sg(sg_list, sg, sg_count, i) {
+- debug_scsi("sg %d, len %u offset %u\n", i, sg->length,
+- sg->offset);
+- if (offset < sg->length) {
+- iscsi_tcp_segment_init_sg(segment, sg, offset);
+- return 0;
++ 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;
+ }
+- offset -= sg->length;
++ tcp_conn->in.hdr = &tcp_conn->hdr;
++ tcp_conn->discontiguous_hdr_cnt++;
++ tcp_conn->in_progress = IN_PROGRESS_WAIT_HEADER;
+ }
+
+- return ISCSI_ERR_DATA_OFFSET;
+-}
+-
+-/**
+- * iscsi_tcp_hdr_recv_prep - prep segment for hdr reception
+- * @tcp_conn: iscsi connection to prep for
+- *
+- * This function always passes NULL for the hash argument, because when this
+- * function is called we do not yet know the final size of the header and want
+- * to delay the digest processing until we know that.
+- */
+-static void
+-iscsi_tcp_hdr_recv_prep(struct iscsi_tcp_conn *tcp_conn)
+-{
+- debug_tcp("iscsi_tcp_hdr_recv_prep(%p%s)\n", tcp_conn,
+- tcp_conn->iscsi_conn->hdrdgst_en ? ", digest enabled" : "");
+- iscsi_segment_init_linear(&tcp_conn->in.segment,
+- tcp_conn->in.hdr_buf, sizeof(struct iscsi_hdr),
+- iscsi_tcp_hdr_recv_done, NULL);
+-}
+-
+-/*
+- * Handle incoming reply to any other type of command
+- */
+-static int
+-iscsi_tcp_data_recv_done(struct iscsi_tcp_conn *tcp_conn,
+- struct iscsi_segment *segment)
+-{
+- struct iscsi_conn *conn = tcp_conn->iscsi_conn;
+- int rc = 0;
+-
+- if (!iscsi_tcp_dgst_verify(tcp_conn, segment))
+- return ISCSI_ERR_DATA_DGST;
+-
+- rc = iscsi_complete_pdu(conn, tcp_conn->in.hdr,
+- conn->data, tcp_conn->in.datalen);
+- if (rc)
+- return rc;
+-
+- iscsi_tcp_hdr_recv_prep(tcp_conn);
+ return 0;
+ }
+
+-static void
+-iscsi_tcp_data_recv_prep(struct iscsi_tcp_conn *tcp_conn)
+-{
+- struct iscsi_conn *conn = tcp_conn->iscsi_conn;
+- struct hash_desc *rx_hash = NULL;
+-
+- if (conn->datadgst_en)
+- rx_hash = &tcp_conn->rx_hash;
+-
+- iscsi_segment_init_linear(&tcp_conn->in.segment,
+- conn->data, tcp_conn->in.datalen,
+- iscsi_tcp_data_recv_done, rx_hash);
+-}
+-
+ /*
+ * must be called with session lock
+ */
+@@ -498,6 +186,7 @@ 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*))) {
+@@ -506,12 +195,12 @@ iscsi_tcp_cleanup_ctask(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
+ debug_scsi("iscsi_tcp_cleanup_ctask pending r2t dropped\n");
+ }
+
+- r2t = tcp_ctask->r2t;
+- if (r2t != NULL) {
+- __kfifo_put(tcp_ctask->r2tpool.queue, (void*)&r2t,
+- sizeof(void*));
+- tcp_ctask->r2t = NULL;
+- }
++ sc = ctask->sc;
++ if (unlikely(!sc))
++ return;
++
++ tcp_ctask->xmstate = XMSTATE_IDLE;
++ tcp_ctask->r2t = NULL;
+ }
+
+ /**
+@@ -522,49 +211,52 @@ iscsi_tcp_cleanup_ctask(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
+ 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;
+- struct scsi_cmnd *sc = ctask->sc;
+ int datasn = be32_to_cpu(rhdr->datasn);
+- unsigned total_in_length = scsi_in(sc)->length;
+
+- iscsi_update_cmdsn(session, (struct iscsi_nopin*)rhdr);
++ 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 (tcp_ctask->exp_datasn != datasn) {
+- debug_tcp("%s: ctask->exp_datasn(%d) != rhdr->datasn(%d)\n",
+- __FUNCTION__, tcp_ctask->exp_datasn, datasn);
++ if (ctask->datasn != datasn)
+ return ISCSI_ERR_DATASN;
+- }
+
+- tcp_ctask->exp_datasn++;
++ ctask->datasn++;
+
+ tcp_ctask->data_offset = be32_to_cpu(rhdr->offset);
+- if (tcp_ctask->data_offset + tcp_conn->in.datalen > total_in_length) {
+- debug_tcp("%s: data_offset(%d) + data_len(%d) > total_length_in(%d)\n",
+- __FUNCTION__, tcp_ctask->data_offset,
+- tcp_conn->in.datalen, total_in_length);
++ if (tcp_ctask->data_offset + tcp_conn->in.datalen > ctask->total_length)
+ return ISCSI_ERR_DATA_OFFSET;
+- }
+
+ if (rhdr->flags & ISCSI_FLAG_DATA_STATUS) {
+- sc->result = (DID_OK << 16) | rhdr->cmd_status;
++ struct scsi_cmnd *sc = ctask->sc;
++
+ conn->exp_statsn = be32_to_cpu(rhdr->statsn) + 1;
+- if (rhdr->flags & (ISCSI_FLAG_DATA_UNDERFLOW |
+- ISCSI_FLAG_DATA_OVERFLOW)) {
++ if (rhdr->flags & ISCSI_FLAG_DATA_UNDERFLOW) {
+ int res_count = be32_to_cpu(rhdr->residual_count);
+
+ if (res_count > 0 &&
+- (rhdr->flags & ISCSI_FLAG_CMD_OVERFLOW ||
+- res_count <= total_in_length))
+- scsi_in(sc)->resid = res_count;
+- else
++ 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++;
+@@ -588,6 +280,7 @@ 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));
+@@ -611,6 +304,43 @@ iscsi_solicit_data_init(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
+ 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;
++ }
+ }
+
+ /**
+@@ -630,25 +360,27 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
+ int rc;
+
+ if (tcp_conn->in.datalen) {
+- iscsi_conn_printk(KERN_ERR, conn,
+- "invalid R2t with datalen %d\n",
+- 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_datasn != r2tsn){
+- debug_tcp("%s: ctask->exp_datasn(%d) != rhdr->r2tsn(%d)\n",
+- __FUNCTION__, tcp_ctask->exp_datasn, r2tsn);
++ if (tcp_ctask->exp_r2tsn && tcp_ctask->exp_r2tsn != r2tsn)
+ return ISCSI_ERR_R2TSN;
+- }
+
+- /* fill-in new R2T associated with the task */
+- iscsi_update_cmdsn(session, (struct iscsi_nopin*)rhdr);
++ rc = iscsi_check_assign_cmdsn(session, (struct iscsi_nopin*)rhdr);
++ if (rc)
++ return rc;
+
+- if (!ctask->sc || session->state != ISCSI_STATE_LOGGED_IN) {
+- iscsi_conn_printk(KERN_INFO, conn,
+- "dropping R2T itt %d in recovery.\n",
+- ctask->itt);
++ /* 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;
+ }
+
+@@ -658,10 +390,8 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
+ r2t->exp_statsn = rhdr->statsn;
+ r2t->data_length = be32_to_cpu(rhdr->data_length);
+ if (r2t->data_length == 0) {
+- iscsi_conn_printk(KERN_ERR, conn,
+- "invalid R2T with zero data len\n");
+- __kfifo_put(tcp_ctask->r2tpool.queue, (void*)&r2t,
+- sizeof(void*));
++ printk(KERN_ERR "iscsi_tcp: invalid R2T with zero data len\n");
++ spin_unlock(&session->lock);
+ return ISCSI_ERR_DATALEN;
+ }
+
+@@ -671,13 +401,11 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
+ r2t->data_length, session->max_burst);
+
+ r2t->data_offset = be32_to_cpu(rhdr->data_offset);
+- if (r2t->data_offset + r2t->data_length > scsi_out(ctask->sc)->length) {
+- iscsi_conn_printk(KERN_ERR, conn,
+- "invalid R2T with data len %u at offset %u "
+- "and total length %d\n", r2t->data_length,
+- r2t->data_offset, scsi_out(ctask->sc)->length);
+- __kfifo_put(tcp_ctask->r2tpool.queue, (void*)&r2t,
+- sizeof(void*));
++ 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;
+ }
+
+@@ -686,134 +414,108 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
+
+ iscsi_solicit_data_init(conn, ctask, r2t);
+
+- tcp_ctask->exp_datasn = r2tsn + 1;
++ tcp_ctask->exp_r2tsn = r2tsn + 1;
+ __kfifo_put(tcp_ctask->r2tqueue, (void*)&r2t, sizeof(void*));
+- conn->r2t_pdus_cnt++;
++ tcp_ctask->xmstate |= XMSTATE_SOL_HDR;
++ list_move_tail(&ctask->running, &conn->xmitqueue);
+
+- iscsi_requeue_ctask(ctask);
+- return 0;
+-}
+-
+-/*
+- * Handle incoming reply to DataIn command
+- */
+-static int
+-iscsi_tcp_process_data_in(struct iscsi_tcp_conn *tcp_conn,
+- struct iscsi_segment *segment)
+-{
+- struct iscsi_conn *conn = tcp_conn->iscsi_conn;
+- struct iscsi_hdr *hdr = tcp_conn->in.hdr;
+- int rc;
+-
+- if (!iscsi_tcp_dgst_verify(tcp_conn, segment))
+- return ISCSI_ERR_DATA_DGST;
+-
+- /* check for non-exceptional status */
+- if (hdr->flags & ISCSI_FLAG_DATA_STATUS) {
+- rc = iscsi_complete_pdu(conn, tcp_conn->in.hdr, NULL, 0);
+- if (rc)
+- return rc;
+- }
++ scsi_queue_work(session->host, &conn->xmitwork);
++ conn->r2t_pdus_cnt++;
++ spin_unlock(&session->lock);
+
+- iscsi_tcp_hdr_recv_prep(tcp_conn);
+ return 0;
+ }
+
+-/**
+- * iscsi_tcp_hdr_dissect - process PDU header
+- * @conn: iSCSI connection
+- * @hdr: PDU header
+- *
+- * This function analyzes the header of the PDU received,
+- * and performs several sanity checks. If the PDU is accompanied
+- * by data, the receive buffer is set up to copy the incoming data
+- * to the correct location.
+- */
+ static int
+-iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
++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;
+- struct iscsi_cmd_task *ctask;
+- uint32_t itt;
++ 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) {
+- iscsi_conn_printk(KERN_ERR, conn,
+- "iscsi_tcp: datalen %d > %d\n",
+- 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;
+
+- /* Additional header segments. So far, we don't
+- * process additional headers.
+- */
++ /* 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)
++ 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 ahslen %d datalen %d\n",
+- opcode, ahslen, tcp_conn->in.datalen);
++ 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:
+- ctask = session->cmds[itt];
+- spin_lock(&conn->session->lock);
+- rc = iscsi_data_rsp(conn, ctask);
+- spin_unlock(&conn->session->lock);
++ tcp_conn->in.ctask = session->cmds[itt];
++ rc = iscsi_data_rsp(conn, tcp_conn->in.ctask);
+ if (rc)
+ return rc;
+- if (tcp_conn->in.datalen) {
+- struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+- struct hash_desc *rx_hash = NULL;
+- struct scsi_data_buffer *sdb = scsi_in(ctask->sc);
+-
+- /*
+- * Setup copy of Data-In into the Scsi_Cmnd
+- * Scatterlist case:
+- * We set up the iscsi_segment to point to the next
+- * scatterlist entry to copy to. As we go along,
+- * we move on to the next scatterlist entry and
+- * update the digest per-entry.
+- */
+- if (conn->datadgst_en)
+- rx_hash = &tcp_conn->rx_hash;
+-
+- debug_tcp("iscsi_tcp_begin_data_in(%p, offset=%d, "
+- "datalen=%d)\n", tcp_conn,
+- tcp_ctask->data_offset,
+- tcp_conn->in.datalen);
+- return iscsi_segment_seek_sg(&tcp_conn->in.segment,
+- sdb->table.sgl,
+- sdb->table.nents,
+- tcp_ctask->data_offset,
+- tcp_conn->in.datalen,
+- iscsi_tcp_process_data_in,
+- rx_hash);
+- }
+ /* fall through */
+ case ISCSI_OP_SCSI_CMD_RSP:
+- if (tcp_conn->in.datalen) {
+- iscsi_tcp_data_recv_prep(tcp_conn);
+- return 0;
+- }
+- rc = iscsi_complete_pdu(conn, hdr, NULL, 0);
++ 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:
+- ctask = session->cmds[itt];
++ tcp_conn->in.ctask = session->cmds[itt];
+ if (ahslen)
+ rc = ISCSI_ERR_AHSLEN;
+- else if (ctask->sc->sc_data_direction == DMA_TO_DEVICE) {
+- spin_lock(&session->lock);
+- rc = iscsi_r2t_rsp(conn, ctask);
+- spin_unlock(&session->lock);
+- } else
++ 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:
+@@ -825,24 +527,18 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
+ * 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) {
+- iscsi_conn_printk(KERN_ERR, conn,
+- "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);
++ 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 there's data coming in with the response,
+- * receive it to the connection's buffer.
+- */
+- if (tcp_conn->in.datalen) {
+- iscsi_tcp_data_recv_prep(tcp_conn);
+- return 0;
+- }
++ if (tcp_conn->in.datalen)
++ goto copy_hdr;
+ /* fall through */
+ case ISCSI_OP_LOGOUT_RSP:
+ case ISCSI_OP_NOOP_IN:
+@@ -854,161 +550,457 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
+ break;
+ }
+
+- if (rc == 0) {
+- /* Anything that comes with data should have
+- * been handled above. */
+- if (tcp_conn->in.datalen)
+- return ISCSI_ERR_PROTO;
+- iscsi_tcp_hdr_recv_prep(tcp_conn);
++ 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;
++}
+
+- return rc;
++/**
++ * 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_hdr_recv_done - process PDU header
++ * iscsi_tcp_copy - copy skb bits to the destanation buffer
++ * @conn: iscsi tcp connection
+ *
+- * This is the callback invoked when the PDU header has
+- * been received. If the header is followed by additional
+- * header segments, we go back for more data.
+- */
+-static int
+-iscsi_tcp_hdr_recv_done(struct iscsi_tcp_conn *tcp_conn,
+- struct iscsi_segment *segment)
++ * 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_conn *conn = tcp_conn->iscsi_conn;
+- struct iscsi_hdr *hdr;
++ 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);
++}
+
+- /* Check if there are additional header segments
+- * *prior* to computing the digest, because we
+- * may need to go back to the caller for more.
++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
+ */
+- hdr = (struct iscsi_hdr *) tcp_conn->in.hdr_buf;
+- if (segment->copied == sizeof(struct iscsi_hdr) && hdr->hlength) {
+- /* Bump the header length - the caller will
+- * just loop around and get the AHS for us, and
+- * call again. */
+- unsigned int ahslen = hdr->hlength << 2;
+-
+- /* Make sure we don't overflow */
+- if (sizeof(*hdr) + ahslen > sizeof(tcp_conn->in.hdr_buf))
+- return ISCSI_ERR_AHSLEN;
+-
+- segment->total_size += ahslen;
+- segment->size += ahslen;
+- return 0;
++ 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;
+ }
+
+- /* We're done processing the header. See if we're doing
+- * header digests; if so, set up the recv_digest buffer
+- * and go back for more. */
+- if (conn->hdrdgst_en) {
+- if (segment->digest_len == 0) {
+- iscsi_tcp_segment_splice_digest(segment,
+- segment->recv_digest);
+- return 0;
++ 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++;
+ }
+- iscsi_tcp_dgst_header(&tcp_conn->rx_hash, hdr,
+- segment->total_copied - ISCSI_DIGEST_SIZE,
+- segment->digest);
+
+- if (!iscsi_tcp_dgst_verify(tcp_conn, segment))
+- return ISCSI_ERR_HDR_DGST;
++ 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);
+ }
+
+- tcp_conn->in.hdr = hdr;
+- return iscsi_tcp_hdr_dissect(conn, hdr);
++ 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_recv - TCP receive in sendfile fashion
++ * 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_recv(read_descriptor_t *rd_desc, struct sk_buff *skb,
+- unsigned int offset, size_t len)
++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;
+- struct iscsi_segment *segment = &tcp_conn->in.segment;
+- struct skb_seq_state seq;
+- unsigned int consumed = 0;
+- int rc = 0;
++ int processed;
++ char pad[ISCSI_PAD_LEN];
++ struct scatterlist sg;
+
+- debug_tcp("in %d bytes\n", skb->len - offset);
++ /*
++ * 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;
+ }
+
+- skb_prepare_seq_read(skb, offset, skb->len, &seq);
+- while (1) {
+- unsigned int avail;
+- const u8 *ptr;
++ 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;
++ }
++ }
+
+- avail = skb_seq_read(consumed, &ptr, &seq);
+- if (avail == 0) {
+- debug_tcp("no more data avail. Consumed %d\n",
+- consumed);
+- break;
++ /*
++ * 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;
+ }
+- BUG_ON(segment->copied >= segment->size);
+-
+- debug_tcp("skb %p ptr=%p avail=%u\n", skb, ptr, avail);
+- rc = iscsi_tcp_segment_recv(tcp_conn, segment, ptr, avail);
+- BUG_ON(rc == 0);
+- consumed += rc;
+-
+- if (segment->total_copied >= segment->total_size) {
+- debug_tcp("segment done\n");
+- rc = segment->done(tcp_conn, segment);
+- if (rc != 0) {
+- skb_abort_seq_read(&seq);
+- goto error;
+- }
++ }
+
+- /* The done() functions sets up the
+- * next segment. */
++ 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;
+ }
+ }
+- skb_abort_seq_read(&seq);
+- conn->rxdata_octets += consumed;
+- return consumed;
+
+-error:
+- debug_tcp("Error receiving PDU, errno=%d\n", rc);
+- iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
+- return 0;
++ 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;
+- struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+ read_descriptor_t rd_desc;
+
+ read_lock(&sk->sk_callback_lock);
+
+ /*
+- * Use rd_desc to pass 'conn' to iscsi_tcp_recv.
++ * 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_recv
++ * 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_recv);
++ tcp_read_sock(sk, &rd_desc, iscsi_tcp_data_recv);
+
+ read_unlock(&sk->sk_callback_lock);
+-
+- /* If we had to (atomically) map a highmem page,
+- * unmap it now. */
+- iscsi_tcp_segment_unmap(&tcp_conn->in.segment);
+ }
+
+ static void
+@@ -1088,173 +1080,121 @@ iscsi_conn_restore_callbacks(struct iscsi_tcp_conn *tcp_conn)
+ }
+
+ /**
+- * iscsi_xmit - TCP transmit
+- **/
+-static int
+-iscsi_xmit(struct iscsi_conn *conn)
++ * 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 iscsi_segment *segment = &tcp_conn->out.segment;
+- unsigned int consumed = 0;
+- int rc = 0;
+-
+- while (1) {
+- rc = iscsi_tcp_xmit_segment(tcp_conn, segment);
+- if (rc < 0)
+- goto error;
+- if (rc == 0)
+- break;
+-
+- consumed += rc;
++ struct socket *sk = tcp_conn->sock;
++ int offset = buf->sg.offset + buf->sent, res;
+
+- if (segment->total_copied >= segment->total_size) {
+- if (segment->done != NULL) {
+- rc = segment->done(tcp_conn, segment);
+- if (rc < 0)
+- goto error;
+- }
+- }
++ /*
++ * 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;
+ }
+
+- debug_tcp("xmit %d bytes\n", consumed);
+-
+- conn->txdata_octets += consumed;
+- return consumed;
+-
+-error:
+- /* Transmit error. We could initiate error recovery
+- * here. */
+- debug_tcp("Error sending PDU, errno=%d\n", rc);
+- iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
+- return rc;
++ tcp_conn->sendpage_failures_cnt++;
++ if (res == -EAGAIN)
++ res = -ENOBUFS;
++ else
++ iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
++ return res;
+ }
+
+ /**
+- * iscsi_tcp_xmit_qlen - return the number of bytes queued for xmit
+- */
+-static inline int
+-iscsi_tcp_xmit_qlen(struct iscsi_conn *conn)
+-{
+- struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+- struct iscsi_segment *segment = &tcp_conn->out.segment;
+-
+- return segment->total_copied - segment->total_size;
+-}
+-
++ * 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_tcp_flush(struct iscsi_conn *conn)
++iscsi_sendhdr(struct iscsi_conn *conn, struct iscsi_buf *buf, int datalen)
+ {
+- int rc;
+-
+- while (iscsi_tcp_xmit_qlen(conn)) {
+- rc = iscsi_xmit(conn);
+- if (rc == 0)
++ 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;
+- if (rc < 0)
+- return rc;
++ return 0;
+ }
+
+- return 0;
+-}
+-
+-/*
+- * This is called when we're done sending the header.
+- * Simply copy the data_segment to the send segment, and return.
+- */
+-static int
+-iscsi_tcp_send_hdr_done(struct iscsi_tcp_conn *tcp_conn,
+- struct iscsi_segment *segment)
+-{
+- tcp_conn->out.segment = tcp_conn->out.data_segment;
+- debug_tcp("Header done. Next segment size %u total_size %u\n",
+- tcp_conn->out.segment.size, tcp_conn->out.segment.total_size);
+- return 0;
++ return res;
+ }
+
+-static void
+-iscsi_tcp_send_hdr_prep(struct iscsi_conn *conn, void *hdr, size_t hdrlen)
++/**
++ * 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)
+ {
+- struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+-
+- debug_tcp("%s(%p%s)\n", __FUNCTION__, tcp_conn,
+- conn->hdrdgst_en? ", digest enabled" : "");
+-
+- /* Clear the data segment - needs to be filled in by the
+- * caller using iscsi_tcp_send_data_prep() */
+- memset(&tcp_conn->out.data_segment, 0, sizeof(struct iscsi_segment));
+-
+- /* If header digest is enabled, compute the CRC and
+- * place the digest into the same buffer. We make
+- * sure that both iscsi_tcp_ctask and mtask have
+- * sufficient room.
+- */
+- if (conn->hdrdgst_en) {
+- iscsi_tcp_dgst_header(&tcp_conn->tx_hash, hdr, hdrlen,
+- hdr + hdrlen);
+- hdrlen += ISCSI_DIGEST_SIZE;
++ 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;
+ }
+
+- /* Remember header pointer for later, when we need
+- * to decide whether there's a payload to go along
+- * with the header. */
+- tcp_conn->out.hdr = hdr;
+-
+- iscsi_segment_init_linear(&tcp_conn->out.segment, hdr, hdrlen,
+- iscsi_tcp_send_hdr_done, NULL);
+-}
+-
+-/*
+- * Prepare the send buffer for the payload data.
+- * Padding and checksumming will all be taken care
+- * of by the iscsi_segment routines.
+- */
+-static int
+-iscsi_tcp_send_data_prep(struct iscsi_conn *conn, struct scatterlist *sg,
+- unsigned int count, unsigned int offset,
+- unsigned int len)
+-{
+- struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+- struct hash_desc *tx_hash = NULL;
+- unsigned int hdr_spec_len;
+-
+- debug_tcp("%s(%p, offset=%d, datalen=%d%s)\n", __FUNCTION__,
+- tcp_conn, offset, len,
+- conn->datadgst_en? ", digest enabled" : "");
+-
+- /* Make sure the datalen matches what the caller
+- said he would send. */
+- hdr_spec_len = ntoh24(tcp_conn->out.hdr->dlength);
+- WARN_ON(iscsi_padded(len) != iscsi_padded(hdr_spec_len));
+-
+- if (conn->datadgst_en)
+- tx_hash = &tcp_conn->tx_hash;
+-
+- return iscsi_segment_seek_sg(&tcp_conn->out.data_segment,
+- sg, count, offset, len,
+- NULL, tx_hash);
++ return res;
+ }
+
+-static void
+-iscsi_tcp_send_linear_data_prepare(struct iscsi_conn *conn, void *data,
+- size_t len)
++static inline void
++iscsi_data_digest_init(struct iscsi_tcp_conn *tcp_conn,
++ struct iscsi_tcp_cmd_task *tcp_ctask)
+ {
+- struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+- struct hash_desc *tx_hash = NULL;
+- unsigned int hdr_spec_len;
+-
+- debug_tcp("%s(%p, datalen=%d%s)\n", __FUNCTION__, tcp_conn, len,
+- conn->datadgst_en? ", digest enabled" : "");
+-
+- /* Make sure the datalen matches what the caller
+- said he would send. */
+- hdr_spec_len = ntoh24(tcp_conn->out.hdr->dlength);
+- WARN_ON(iscsi_padded(len) != iscsi_padded(hdr_spec_len));
+-
+- if (conn->datadgst_en)
+- tx_hash = &tcp_conn->tx_hash;
+-
+- iscsi_segment_init_linear(&tcp_conn->out.data_segment,
+- data, len, NULL, tx_hash);
++ crypto_hash_init(&tcp_conn->tx_hash);
++ tcp_ctask->digest_count = 4;
+ }
+
+ /**
+@@ -1270,17 +1210,13 @@ iscsi_tcp_send_linear_data_prepare(struct iscsi_conn *conn, void *data,
+ *
+ * Called under connection lock.
+ **/
+-static int
++static void
+ iscsi_solicit_data_cont(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
+- struct iscsi_r2t_info *r2t)
++ struct iscsi_r2t_info *r2t, int left)
+ {
+ struct iscsi_data *hdr;
+- int new_offset, left;
+-
+- BUG_ON(r2t->data_length - r2t->sent < 0);
+- left = r2t->data_length - r2t->sent;
+- if (left == 0)
+- return 0;
++ struct scsi_cmnd *sc = ctask->sc;
++ int new_offset;
+
+ hdr = &r2t->dtask.hdr;
+ memset(hdr, 0, sizeof(struct iscsi_data));
+@@ -1301,47 +1237,81 @@ iscsi_solicit_data_cont(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
+ r2t->data_count = left;
+ hdr->flags = ISCSI_FLAG_CMD_FINAL;
+ }
+-
+ conn->dataout_pdus_cnt++;
+- return 1;
++
++ 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_ctask - Initialize iSCSI SCSI_READ or SCSI_WRITE commands
++ * iscsi_tcp_cmd_init - Initialize iSCSI SCSI_READ or SCSI_WRITE commands
+ * @conn: iscsi connection
+ * @ctask: scsi command task
+ * @sc: scsi command
+ **/
+-static int
+-iscsi_tcp_ctask_init(struct iscsi_cmd_task *ctask)
++static void
++iscsi_tcp_cmd_init(struct iscsi_cmd_task *ctask)
+ {
+- struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+- struct iscsi_conn *conn = ctask->conn;
+ struct scsi_cmnd *sc = ctask->sc;
+- int err;
++ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+
+ BUG_ON(__kfifo_len(tcp_ctask->r2tqueue));
++
+ tcp_ctask->sent = 0;
+- tcp_ctask->exp_datasn = 0;
++ tcp_ctask->sg_count = 0;
+
+- /* Prepare PDU, optionally w/ immediate data */
+- debug_scsi("ctask deq [cid %d itt 0x%x imm %d unsol %d]\n",
+- conn->id, ctask->itt, ctask->imm_count,
+- ctask->unsol_count);
+- iscsi_tcp_send_hdr_prep(conn, ctask->hdr, ctask->hdr_len);
++ 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 (!ctask->imm_count)
+- return 0;
++ if (sc->use_sg) {
++ struct scatterlist *sg = sc->request_buffer;
+
+- /* If we have immediate data, attach a payload */
+- err = iscsi_tcp_send_data_prep(conn, scsi_out(sc)->table.sgl,
+- scsi_out(sc)->table.nents,
+- 0, ctask->imm_count);
+- if (err)
+- return err;
+- tcp_ctask->sent += ctask->imm_count;
+- ctask->imm_count = 0;
+- return 0;
++ 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));
+ }
+
+ /**
+@@ -1353,130 +1323,428 @@ iscsi_tcp_ctask_init(struct iscsi_cmd_task *ctask)
+ * 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;
+
+- /* Flush any pending data first. */
+- rc = iscsi_tcp_flush(conn);
+- if (rc < 0)
+- return 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);
+- iscsi_free_mgmt_task(conn, mtask);
++ 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;
+ }
+
+-/*
+- * iscsi_tcp_ctask_xmit - xmit normal PDU task
+- * @conn: iscsi connection
+- * @ctask: iscsi command task
+- *
+- * We're expected to return 0 when everything was transmitted succesfully,
+- * -EAGAIN if there's still data in the queue, or != 0 for any other kind
+- * of error.
+- */
+ static int
+-iscsi_tcp_ctask_xmit(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
++iscsi_send_padding(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
+ {
+ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+- struct scsi_cmnd *sc = ctask->sc;
+- struct scsi_data_buffer *sdb = scsi_out(sc);
+- int rc = 0;
++ 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;
+
+-flush:
+- /* Flush any pending data first. */
+- rc = iscsi_tcp_flush(conn);
+- if (rc < 0)
+- return rc;
++ 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;
++}
+
+- /* Are we done already? */
+- if (sc->sc_data_direction != DMA_TO_DEVICE)
++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;
+
+- if (ctask->unsol_count != 0) {
+- struct iscsi_data *hdr = &tcp_ctask->unsol_dtask.hdr;
++ tcp_ctask = ctask->dd_data;
++ tcp_conn = conn->dd_data;
+
+- /* Prepare a header for the unsolicited PDU.
+- * The amount of data we want to send will be
+- * in ctask->data_count.
+- * FIXME: return the data count instead.
+- */
+- iscsi_prep_unsolicit_data_pdu(ctask, hdr);
++ 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;
++}
+
+- debug_tcp("unsol dout [itt 0x%x doff %d dlen %d]\n",
+- ctask->itt, tcp_ctask->sent, ctask->data_count);
++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;
++ }
+
+- iscsi_tcp_send_hdr_prep(conn, hdr, sizeof(*hdr));
+- rc = iscsi_tcp_send_data_prep(conn, sdb->table.sgl,
+- sdb->table.nents, tcp_ctask->sent,
+- ctask->data_count);
+ if (rc)
+- goto fail;
+- tcp_ctask->sent += ctask->data_count;
+- ctask->unsol_count -= ctask->data_count;
+- goto flush;
+- } else {
+- struct iscsi_session *session = conn->session;
+- struct iscsi_r2t_info *r2t;
++ 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;
++}
+
+- /* All unsolicited PDUs sent. Check for solicited PDUs.
++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.
+ */
+- spin_lock_bh(&session->lock);
+- r2t = tcp_ctask->r2t;
+- if (r2t != NULL) {
+- /* Continue with this R2T? */
+- if (!iscsi_solicit_data_cont(conn, ctask, r2t)) {
+- debug_scsi(" done with r2t %p\n", r2t);
+-
+- __kfifo_put(tcp_ctask->r2tpool.queue,
+- (void*)&r2t, sizeof(void*));
+- tcp_ctask->r2t = r2t = NULL;
+- }
++ if (ctask->unsol_count) {
++ debug_scsi("sending more uns\n");
++ tcp_ctask->xmstate |= XMSTATE_UNS_INIT;
++ goto send_hdr;
+ }
++ }
++ return 0;
++}
+
+- if (r2t == NULL) {
++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*));
+- r2t = tcp_ctask->r2t;
++ 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;
+ }
+- spin_unlock_bh(&session->lock);
+
+- /* Waiting for more R2Ts to arrive. */
+- if (r2t == NULL) {
+- debug_tcp("no R2Ts yet\n");
+- return 0;
++ if (conn->datadgst_en) {
++ iscsi_data_digest_init(conn->dd_data, tcp_ctask);
++ dtask->digest = 0;
+ }
+
+- debug_scsi("sol dout %p [dsn %d itt 0x%x doff %d dlen %d]\n",
+- r2t, r2t->solicit_datasn - 1, ctask->itt,
+- r2t->data_offset + r2t->sent, r2t->data_count);
++ 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);
++ }
+
+- iscsi_tcp_send_hdr_prep(conn, &r2t->dtask.hdr,
+- sizeof(struct iscsi_hdr));
++ if (tcp_ctask->xmstate & XMSTATE_SOL_DATA) {
++ r2t = tcp_ctask->r2t;
++ dtask = &r2t->dtask;
+
+- rc = iscsi_tcp_send_data_prep(conn, sdb->table.sgl,
+- sdb->table.nents,
+- r2t->data_offset + r2t->sent,
+- r2t->data_count);
++ rc = iscsi_send_data(ctask, &r2t->sendbuf, &r2t->sg,
++ &r2t->sent, &r2t->data_count,
++ &dtask->digestbuf, &dtask->digest);
+ if (rc)
+- goto fail;
+- tcp_ctask->sent += r2t->data_count;
+- r2t->sent += r2t->data_count;
+- goto flush;
++ 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;
+-fail:
+- iscsi_conn_failure(conn, rc);
+- return -EIO;
++}
++
++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 *
+@@ -1502,29 +1770,37 @@ iscsi_tcp_conn_create(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
+
+ 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))
++ 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))
++ 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:
+- iscsi_conn_printk(KERN_ERR, conn,
+- "Could not create connection due to crc32c "
+- "loading error. Make sure the crc32c "
+- "module is built as a module or into the "
+- "kernel\n");
+ kfree(tcp_conn);
+ tcp_conn_alloc_fail:
+ iscsi_conn_teardown(cls_conn);
+@@ -1534,22 +1810,18 @@ tcp_conn_alloc_fail:
+ static void
+ iscsi_tcp_release_conn(struct iscsi_conn *conn)
+ {
+- struct iscsi_session *session = conn->session;
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+- struct socket *sock = tcp_conn->sock;
+
+- if (!sock)
++ if (!tcp_conn->sock)
+ return;
+
+- sock_hold(sock->sk);
++ sock_hold(tcp_conn->sock->sk);
+ iscsi_conn_restore_callbacks(tcp_conn);
+- sock_put(sock->sk);
++ sock_put(tcp_conn->sock->sk);
+
+- spin_lock_bh(&session->lock);
++ sock_release(tcp_conn->sock);
+ tcp_conn->sock = NULL;
+ conn->recv_lock = NULL;
+- spin_unlock_bh(&session->lock);
+- sockfd_put(sock);
+ }
+
+ static void
+@@ -1573,49 +1845,11 @@ 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);
+-}
+-
+-static int iscsi_tcp_get_addr(struct iscsi_conn *conn, struct socket *sock,
+- char *buf, int *port,
+- int (*getname)(struct socket *, struct sockaddr *,
+- int *addrlen))
+-{
+- struct sockaddr_storage *addr;
+- struct sockaddr_in6 *sin6;
+- struct sockaddr_in *sin;
+- int rc = 0, len;
+-
+- addr = kmalloc(sizeof(*addr), GFP_KERNEL);
+- if (!addr)
+- return -ENOMEM;
+-
+- if (getname(sock, (struct sockaddr *) addr, &len)) {
+- rc = -ENODEV;
+- goto free_addr;
+- }
+-
+- switch (addr->ss_family) {
+- case AF_INET:
+- sin = (struct sockaddr_in *)addr;
+- spin_lock_bh(&conn->session->lock);
+- sprintf(buf, NIPQUAD_FMT, NIPQUAD(sin->sin_addr.s_addr));
+- *port = be16_to_cpu(sin->sin_port);
+- spin_unlock_bh(&conn->session->lock);
+- break;
+- case AF_INET6:
+- sin6 = (struct sockaddr_in6 *)addr;
+- spin_lock_bh(&conn->session->lock);
+- sprintf(buf, NIP6_FMT, NIP6(sin6->sin6_addr));
+- *port = be16_to_cpu(sin6->sin6_port);
+- spin_unlock_bh(&conn->session->lock);
+- break;
+- }
+-free_addr:
+- kfree(addr);
+- return rc;
++ tcp_conn->hdr_size = sizeof(struct iscsi_hdr);
+ }
+
+ static int
+@@ -1632,28 +1866,13 @@ iscsi_tcp_conn_bind(struct iscsi_cls_session *cls_session,
+ /* lookup for existing socket */
+ sock = sockfd_lookup((int)transport_eph, &err);
+ if (!sock) {
+- iscsi_conn_printk(KERN_ERR, conn,
+- "sockfd_lookup failed %d\n", err);
++ printk(KERN_ERR "iscsi_tcp: sockfd_lookup failed %d\n", err);
+ return -EEXIST;
+ }
+- /*
+- * copy these values now because if we drop the session
+- * userspace may still want to query the values since we will
+- * be using them for the reconnect
+- */
+- err = iscsi_tcp_get_addr(conn, sock, conn->portal_address,
+- &conn->portal_port, kernel_getpeername);
+- if (err)
+- goto free_socket;
+-
+- err = iscsi_tcp_get_addr(conn, sock, conn->local_address,
+- &conn->local_port, kernel_getsockname);
+- if (err)
+- goto free_socket;
+
+ err = iscsi_conn_bind(cls_session, cls_conn, is_leading);
+ if (err)
+- goto free_socket;
++ return err;
+
+ /* bind iSCSI connection and socket */
+ tcp_conn->sock = sock;
+@@ -1676,27 +1895,26 @@ iscsi_tcp_conn_bind(struct iscsi_cls_session *cls_session,
+ /*
+ * set receive state machine into initial state
+ */
+- iscsi_tcp_hdr_recv_prep(tcp_conn);
+- return 0;
++ tcp_conn->in_progress = IN_PROGRESS_WAIT_HEADER;
+
+-free_socket:
+- sockfd_put(sock);
+- return err;
++ return 0;
+ }
+
+ /* called with host lock */
+ static void
+-iscsi_tcp_mtask_init(struct iscsi_conn *conn, struct iscsi_mgmt_task *mtask)
++iscsi_tcp_mgmt_init(struct iscsi_conn *conn, struct iscsi_mgmt_task *mtask,
++ char *data, uint32_t data_size)
+ {
+- debug_scsi("mtask deq [cid %d itt 0x%x]\n", conn->id, mtask->itt);
++ struct iscsi_tcp_mgmt_task *tcp_mtask = mtask->dd_data;
+
+- /* Prepare PDU, optionally w/ immediate data */
+- iscsi_tcp_send_hdr_prep(conn, mtask->hdr, sizeof(*mtask->hdr));
++ 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 we have immediate data, attach a payload */
+ if (mtask->data_count)
+- iscsi_tcp_send_linear_data_prepare(conn, mtask->data,
+- mtask->data_count);
++ iscsi_buf_init_iov(&tcp_mtask->sendbuf, (char*)mtask->data,
++ mtask->data_count);
+ }
+
+ static int
+@@ -1719,7 +1937,8 @@ iscsi_r2tpool_alloc(struct iscsi_session *session)
+ */
+
+ /* R2T pool */
+- if (iscsi_pool_init(&tcp_ctask->r2tpool, session->max_r2t * 4, NULL,
++ if (iscsi_pool_init(&tcp_ctask->r2tpool, session->max_r2t * 4,
++ (void***)&tcp_ctask->r2ts,
+ sizeof(struct iscsi_r2t_info))) {
+ goto r2t_alloc_fail;
+ }
+@@ -1728,7 +1947,8 @@ iscsi_r2tpool_alloc(struct iscsi_session *session)
+ 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);
++ iscsi_pool_free(&tcp_ctask->r2tpool,
++ (void**)tcp_ctask->r2ts);
+ goto r2t_alloc_fail;
+ }
+ }
+@@ -1741,7 +1961,8 @@ r2t_alloc_fail:
+ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+
+ kfifo_free(tcp_ctask->r2tqueue);
+- iscsi_pool_free(&tcp_ctask->r2tpool);
++ iscsi_pool_free(&tcp_ctask->r2tpool,
++ (void**)tcp_ctask->r2ts);
+ }
+ return -ENOMEM;
+ }
+@@ -1756,7 +1977,8 @@ iscsi_r2tpool_free(struct iscsi_session *session)
+ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+
+ kfifo_free(tcp_ctask->r2tqueue);
+- iscsi_pool_free(&tcp_ctask->r2tpool);
++ iscsi_pool_free(&tcp_ctask->r2tpool,
++ (void**)tcp_ctask->r2ts);
+ }
+ }
+
+@@ -1772,6 +1994,9 @@ iscsi_conn_set_param(struct iscsi_cls_conn *cls_conn, enum iscsi_param param,
+ 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);
+@@ -1780,12 +2005,12 @@ iscsi_conn_set_param(struct iscsi_cls_conn *cls_conn, enum iscsi_param param,
+ break;
+ case ISCSI_PARAM_MAX_R2T:
+ sscanf(buf, "%d", &value);
+- if (value <= 0 || !is_power_of_2(value))
+- return -EINVAL;
+- if (session->max_r2t == 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;
+@@ -1801,18 +2026,41 @@ 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:
+- spin_lock_bh(&conn->session->lock);
+- len = sprintf(buf, "%hu\n", conn->portal_port);
+- spin_unlock_bh(&conn->session->lock);
++ 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:
+- spin_lock_bh(&conn->session->lock);
+- len = sprintf(buf, "%s\n", conn->portal_address);
+- spin_unlock_bh(&conn->session->lock);
++ 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);
+@@ -1821,29 +2069,6 @@ iscsi_tcp_conn_get_param(struct iscsi_cls_conn *cls_conn,
+ return len;
+ }
+
+-static int
+-iscsi_tcp_host_get_param(struct Scsi_Host *shost, enum iscsi_host_param param,
+- char *buf)
+-{
+- struct iscsi_session *session = iscsi_hostdata(shost->hostdata);
+- int len;
+-
+- switch (param) {
+- case ISCSI_HOST_PARAM_IPADDRESS:
+- spin_lock_bh(&session->lock);
+- if (!session->leadconn)
+- len = -ENODEV;
+- else
+- len = sprintf(buf, "%s\n",
+- session->leadconn->local_address);
+- spin_unlock_bh(&session->lock);
+- break;
+- default:
+- return iscsi_host_get_param(shost, param, buf);
+- }
+- return len;
+-}
+-
+ static void
+ iscsi_conn_get_stats(struct iscsi_cls_conn *cls_conn, struct iscsi_stats *stats)
+ {
+@@ -1871,7 +2096,6 @@ iscsi_conn_get_stats(struct iscsi_cls_conn *cls_conn, struct iscsi_stats *stats)
+ static struct iscsi_cls_session *
+ iscsi_tcp_session_create(struct iscsi_transport *iscsit,
+ struct scsi_transport_template *scsit,
+- uint16_t cmds_max, uint16_t qdepth,
+ uint32_t initial_cmdsn, uint32_t *hostno)
+ {
+ struct iscsi_cls_session *cls_session;
+@@ -1879,7 +2103,7 @@ iscsi_tcp_session_create(struct iscsi_transport *iscsit,
+ uint32_t hn;
+ int cmd_i;
+
+- cls_session = iscsi_session_setup(iscsit, scsit, cmds_max, qdepth,
++ cls_session = iscsi_session_setup(iscsit, scsit,
+ sizeof(struct iscsi_tcp_cmd_task),
+ sizeof(struct iscsi_tcp_mgmt_task),
+ initial_cmdsn, &hn);
+@@ -1892,15 +2116,14 @@ iscsi_tcp_session_create(struct iscsi_transport *iscsit,
+ struct iscsi_cmd_task *ctask = session->cmds[cmd_i];
+ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+
+- ctask->hdr = &tcp_ctask->hdr.cmd_hdr;
+- ctask->hdr_max = sizeof(tcp_ctask->hdr) - ISCSI_DIGEST_SIZE;
++ 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 = (struct iscsi_hdr *) &tcp_mtask->hdr;
++ mtask->hdr = &tcp_mtask->hdr;
+ }
+
+ if (iscsi_r2tpool_alloc(class_to_transport_session(cls_session)))
+@@ -1919,27 +2142,17 @@ static void iscsi_tcp_session_destroy(struct iscsi_cls_session *cls_session)
+ iscsi_session_teardown(cls_session);
+ }
+
+-static int iscsi_tcp_slave_configure(struct scsi_device *sdev)
+-{
+- blk_queue_bounce_limit(sdev->request_queue, BLK_BOUNCE_ANY);
+- blk_queue_dma_alignment(sdev->request_queue, 0);
+- return 0;
+-}
+-
+ static struct scsi_host_template iscsi_sht = {
+- .module = THIS_MODULE,
+ .name = "iSCSI Initiator over TCP/IP",
+ .queuecommand = iscsi_queuecommand,
+ .change_queue_depth = iscsi_change_queue_depth,
+- .can_queue = ISCSI_DEF_XMIT_CMDS_MAX - 1,
+- .sg_tablesize = 4096,
++ .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_device_reset_handler= iscsi_eh_device_reset,
+ .eh_host_reset_handler = iscsi_eh_host_reset,
+ .use_clustering = DISABLE_CLUSTERING,
+- .slave_configure = iscsi_tcp_slave_configure,
+ .proc_name = "iscsi_tcp",
+ .this_id = -1,
+ };
+@@ -1966,19 +2179,12 @@ static struct iscsi_transport iscsi_tcp_transport = {
+ ISCSI_EXP_STATSN |
+ ISCSI_PERSISTENT_PORT |
+ ISCSI_PERSISTENT_ADDRESS |
+- ISCSI_TARGET_NAME | ISCSI_TPGT |
+- ISCSI_USERNAME | ISCSI_PASSWORD |
+- ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN |
+- ISCSI_FAST_ABORT | ISCSI_ABORT_TMO |
+- ISCSI_LU_RESET_TMO |
+- ISCSI_PING_TMO | ISCSI_RECV_TMO,
+- .host_param_mask = ISCSI_HOST_HWADDRESS | ISCSI_HOST_IPADDRESS |
+- ISCSI_HOST_INITIATOR_NAME |
+- ISCSI_HOST_NETDEV_NAME,
++ ISCSI_TARGET_NAME |
++ ISCSI_TPGT,
+ .host_template = &iscsi_sht,
+ .conndata_size = sizeof(struct iscsi_conn),
+ .max_conn = 1,
+- .max_cmd_len = 16,
++ .max_cmd_len = ISCSI_TCP_MAX_CMD_LEN,
+ /* session management */
+ .create_session = iscsi_tcp_session_create,
+ .destroy_session = iscsi_tcp_session_destroy,
+@@ -1991,14 +2197,11 @@ static struct iscsi_transport iscsi_tcp_transport = {
+ .get_session_param = iscsi_session_get_param,
+ .start_conn = iscsi_conn_start,
+ .stop_conn = iscsi_tcp_conn_stop,
+- /* iscsi host params */
+- .get_host_param = iscsi_tcp_host_get_param,
+- .set_host_param = iscsi_host_set_param,
+ /* IO */
+ .send_pdu = iscsi_conn_send_pdu,
+ .get_stats = iscsi_conn_get_stats,
+- .init_cmd_task = iscsi_tcp_ctask_init,
+- .init_mgmt_task = iscsi_tcp_mtask_init,
++ .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,
+diff --git a/drivers/scsi/iscsi_tcp.h b/drivers/scsi/iscsi_tcp.h
+index ed0b991..3273683 100644
+--- a/drivers/scsi/iscsi_tcp.h
++++ b/drivers/scsi/iscsi_tcp.h
+@@ -24,61 +24,68 @@
+
+ #include <scsi/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;
+-struct iscsi_tcp_conn;
+-struct iscsi_segment;
+-
+-typedef int iscsi_segment_done_fn_t(struct iscsi_tcp_conn *,
+- struct iscsi_segment *);
+-
+-struct iscsi_segment {
+- unsigned char *data;
+- unsigned int size;
+- unsigned int copied;
+- unsigned int total_size;
+- unsigned int total_copied;
+-
+- struct hash_desc *hash;
+- unsigned char recv_digest[ISCSI_DIGEST_SIZE];
+- unsigned char digest[ISCSI_DIGEST_SIZE];
+- unsigned int digest_len;
+-
+- struct scatterlist *sg;
+- void *sg_mapped;
+- unsigned int sg_offset;
+-
+- iscsi_segment_done_fn_t *done;
+-};
+
+ /* Socket connection recieve helper */
+ struct iscsi_tcp_recv {
+ struct iscsi_hdr *hdr;
+- struct iscsi_segment segment;
+-
+- /* Allocate buffer for BHS + AHS */
+- uint32_t hdr_buf[64];
++ 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;
+-};
+-
+-/* Socket connection send helper */
+-struct iscsi_tcp_send {
+- struct iscsi_hdr *hdr;
+- struct iscsi_segment segment;
+- struct iscsi_segment data_segment;
++ 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 */
+- struct iscsi_tcp_send out; /* TCP send context */
++ int in_progress; /* connection state machine */
+
+ /* old values for socket callbacks */
+ void (*old_data_ready)(struct sock *, int);
+@@ -93,19 +100,29 @@ struct iscsi_tcp_conn {
+ uint32_t sendpage_failures_cnt;
+ uint32_t discontiguous_hdr_cnt;
+
+- int error;
+-
+ 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[ISCSI_DIGEST_SIZE];/* Header-Digest */
++ 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[ISCSI_DIGEST_SIZE]; /* Header-Digest */
++ 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 {
+@@ -113,26 +130,38 @@ struct iscsi_r2t_info {
+ __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; /* Data-Out header buf */
++ struct iscsi_data_task dtask; /* which data task */
+ };
+
+ struct iscsi_tcp_cmd_task {
+- struct iscsi_hdr_buff {
+- struct iscsi_cmd cmd_hdr;
+- char hdrextbuf[ISCSI_MAX_AHS_SIZE +
+- ISCSI_DIGEST_SIZE];
+- } hdr;
+-
++ 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;
+- uint32_t exp_datasn; /* expected target's R2TSN/DataSN */
++ 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_pool r2tpool;
++ struct iscsi_r2t_info *r2t; /* in progress R2T */
++ struct iscsi_queue r2tpool;
+ struct kfifo *r2tqueue;
+- struct iscsi_data_task unsol_dtask; /* Data-Out header buf */
++ 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 */
+diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
+index b43bf1d..3f5b9b4 100644
+--- a/drivers/scsi/libiscsi.c
++++ b/drivers/scsi/libiscsi.c
+@@ -22,9 +22,9 @@
+ * 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 <linux/log2.h>
+ #include <asm/unaligned.h>
+ #include <net/tcp.h>
+ #include <scsi/scsi_cmnd.h>
+@@ -46,53 +46,27 @@ class_to_transport_session(struct iscsi_cls_session *cls_session)
+ }
+ EXPORT_SYMBOL_GPL(class_to_transport_session);
+
+-/* Serial Number Arithmetic, 32 bits, less than, RFC1982 */
+-#define SNA32_CHECK 2147483648UL
++#define INVALID_SN_DELTA 0xffff
+
+-static int iscsi_sna_lt(u32 n1, u32 n2)
+-{
+- return n1 != n2 && ((n1 < n2 && (n2 - n1 < SNA32_CHECK)) ||
+- (n1 > n2 && (n2 - n1 < SNA32_CHECK)));
+-}
+-
+-/* Serial Number Arithmetic, 32 bits, less than, RFC1982 */
+-static int iscsi_sna_lte(u32 n1, u32 n2)
+-{
+- return n1 == n2 || ((n1 < n2 && (n2 - n1 < SNA32_CHECK)) ||
+- (n1 > n2 && (n2 - n1 < SNA32_CHECK)));
+-}
+-
+-void
+-iscsi_update_cmdsn(struct iscsi_session *session, struct iscsi_nopin *hdr)
++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);
+
+- /*
+- * standard specifies this check for when to update expected and
+- * max sequence numbers
+- */
+- if (iscsi_sna_lt(max_cmdsn, exp_cmdsn - 1))
+- return;
+-
+- if (exp_cmdsn != session->exp_cmdsn &&
+- !iscsi_sna_lt(exp_cmdsn, session->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;
+
+- if (max_cmdsn != session->max_cmdsn &&
+- !iscsi_sna_lt(max_cmdsn, session->max_cmdsn)) {
+- session->max_cmdsn = max_cmdsn;
+- /*
+- * if the window closed with IO queued, then kick the
+- * xmit thread
+- */
+- if (!list_empty(&session->leadconn->xmitqueue) ||
+- !list_empty(&session->leadconn->mgmtqueue))
+- scsi_queue_work(session->host,
+- &session->leadconn->xmitwork);
+- }
++ return 0;
+ }
+-EXPORT_SYMBOL_GPL(iscsi_update_cmdsn);
++EXPORT_SYMBOL_GPL(iscsi_check_assign_cmdsn);
+
+ void iscsi_prep_unsolicit_data_pdu(struct iscsi_cmd_task *ctask,
+ struct iscsi_data *hdr)
+@@ -123,84 +97,6 @@ void iscsi_prep_unsolicit_data_pdu(struct iscsi_cmd_task *ctask,
+ }
+ EXPORT_SYMBOL_GPL(iscsi_prep_unsolicit_data_pdu);
+
+-static int iscsi_add_hdr(struct iscsi_cmd_task *ctask, unsigned len)
+-{
+- unsigned exp_len = ctask->hdr_len + len;
+-
+- if (exp_len > ctask->hdr_max) {
+- WARN_ON(1);
+- return -EINVAL;
+- }
+-
+- WARN_ON(len & (ISCSI_PAD_LEN - 1)); /* caller must pad the AHS */
+- ctask->hdr_len = exp_len;
+- return 0;
+-}
+-
+-/*
+- * make an extended cdb AHS
+- */
+-static int iscsi_prep_ecdb_ahs(struct iscsi_cmd_task *ctask)
+-{
+- struct scsi_cmnd *cmd = ctask->sc;
+- unsigned rlen, pad_len;
+- unsigned short ahslength;
+- struct iscsi_ecdb_ahdr *ecdb_ahdr;
+- int rc;
+-
+- ecdb_ahdr = iscsi_next_hdr(ctask);
+- rlen = cmd->cmd_len - ISCSI_CDB_SIZE;
+-
+- BUG_ON(rlen > sizeof(ecdb_ahdr->ecdb));
+- ahslength = rlen + sizeof(ecdb_ahdr->reserved);
+-
+- pad_len = iscsi_padding(rlen);
+-
+- rc = iscsi_add_hdr(ctask, sizeof(ecdb_ahdr->ahslength) +
+- sizeof(ecdb_ahdr->ahstype) + ahslength + pad_len);
+- if (rc)
+- return rc;
+-
+- if (pad_len)
+- memset(&ecdb_ahdr->ecdb[rlen], 0, pad_len);
+-
+- ecdb_ahdr->ahslength = cpu_to_be16(ahslength);
+- ecdb_ahdr->ahstype = ISCSI_AHSTYPE_CDB;
+- ecdb_ahdr->reserved = 0;
+- memcpy(ecdb_ahdr->ecdb, cmd->cmnd + ISCSI_CDB_SIZE, rlen);
+-
+- debug_scsi("iscsi_prep_ecdb_ahs: varlen_cdb_len %d "
+- "rlen %d pad_len %d ahs_length %d iscsi_headers_size %u\n",
+- cmd->cmd_len, rlen, pad_len, ahslength, ctask->hdr_len);
+-
+- return 0;
+-}
+-
+-static int iscsi_prep_bidi_ahs(struct iscsi_cmd_task *ctask)
+-{
+- struct scsi_cmnd *sc = ctask->sc;
+- struct iscsi_rlength_ahdr *rlen_ahdr;
+- int rc;
+-
+- rlen_ahdr = iscsi_next_hdr(ctask);
+- rc = iscsi_add_hdr(ctask, sizeof(*rlen_ahdr));
+- if (rc)
+- return rc;
+-
+- rlen_ahdr->ahslength =
+- cpu_to_be16(sizeof(rlen_ahdr->read_length) +
+- sizeof(rlen_ahdr->reserved));
+- rlen_ahdr->ahstype = ISCSI_AHSTYPE_RLENGTH;
+- rlen_ahdr->reserved = 0;
+- rlen_ahdr->read_length = cpu_to_be32(scsi_in(sc)->length);
+-
+- debug_scsi("bidi-in rlen_ahdr->read_length(%d) "
+- "rlen_ahdr->ahslength(%d)\n",
+- be32_to_cpu(rlen_ahdr->read_length),
+- be16_to_cpu(rlen_ahdr->ahslength));
+- return 0;
+-}
+-
+ /**
+ * iscsi_prep_scsi_cmd_pdu - prep iscsi scsi cmd pdu
+ * @ctask: iscsi cmd task
+@@ -208,47 +104,26 @@ static int iscsi_prep_bidi_ahs(struct iscsi_cmd_task *ctask)
+ * 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 int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)
++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;
+- unsigned hdrlength, cmd_len;
+- int rc;
+-
+- ctask->hdr_len = 0;
+- rc = iscsi_add_hdr(ctask, sizeof(*hdr));
+- if (rc)
+- return rc;
+- 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, session->age);
+- hdr->cmdsn = cpu_to_be32(session->cmdsn);
+- session->cmdsn++;
+- hdr->exp_statsn = cpu_to_be32(conn->exp_statsn);
+- cmd_len = sc->cmd_len;
+- if (cmd_len < ISCSI_CDB_SIZE)
+- memset(&hdr->cdb[cmd_len], 0, ISCSI_CDB_SIZE - cmd_len);
+- else if (cmd_len > ISCSI_CDB_SIZE) {
+- rc = iscsi_prep_ecdb_ahs(ctask);
+- if (rc)
+- return rc;
+- cmd_len = ISCSI_CDB_SIZE;
+- }
+- memcpy(hdr->cdb, sc->cmnd, cmd_len);
+
+- ctask->imm_count = 0;
+- if (scsi_bidi_cmnd(sc)) {
+- hdr->flags |= ISCSI_FLAG_CMD_READ;
+- rc = iscsi_prep_bidi_ahs(ctask);
+- if (rc)
+- return rc;
+- }
++ 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) {
+- unsigned out_len = scsi_out(sc)->length;
+- hdr->data_length = cpu_to_be32(out_len);
+ hdr->flags |= ISCSI_FLAG_CMD_WRITE;
+ /*
+ * Write counters:
+@@ -264,61 +139,43 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)
+ *
+ * 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 (out_len >= session->first_burst)
++ if (ctask->total_length >= session->first_burst)
+ ctask->imm_count = min(session->first_burst,
+ conn->max_xmit_dlength);
+ else
+- ctask->imm_count = min(out_len,
++ ctask->imm_count = min(ctask->total_length,
+ conn->max_xmit_dlength);
+- hton24(hdr->dlength, ctask->imm_count);
++ hton24(ctask->hdr->dlength, ctask->imm_count);
+ } else
+- zero_data(hdr->dlength);
++ zero_data(ctask->hdr->dlength);
+
+ if (!session->initial_r2t_en) {
+- ctask->unsol_count = min(session->first_burst, out_len)
+- - ctask->imm_count;
++ 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 */
+- hdr->flags |= ISCSI_FLAG_CMD_FINAL;
++ ctask->hdr->flags |= ISCSI_FLAG_CMD_FINAL;
+ } else {
++ ctask->datasn = 0;
+ hdr->flags |= ISCSI_FLAG_CMD_FINAL;
+ zero_data(hdr->dlength);
+- hdr->data_length = cpu_to_be32(scsi_in(sc)->length);
+
+ if (sc->sc_data_direction == DMA_FROM_DEVICE)
+ hdr->flags |= ISCSI_FLAG_CMD_READ;
+ }
+
+- /* calculate size of additional header segments (AHSs) */
+- hdrlength = ctask->hdr_len - sizeof(*hdr);
+-
+- WARN_ON(hdrlength & (ISCSI_PAD_LEN-1));
+- hdrlength /= ISCSI_PAD_LEN;
+-
+- WARN_ON(hdrlength >= 256);
+- hdr->hlength = hdrlength & 0xFF;
+-
+- if (conn->session->tt->init_cmd_task(conn->ctask))
+- return EIO;
+-
+ conn->scsicmd_pdus_cnt++;
+- debug_scsi("iscsi prep [%s cid %d sc %p cdb 0x%x itt 0x%x "
+- "len %d bidi_len %d cmdsn %d win %d]\n",
+- scsi_bidi_cmnd(sc) ? "bidirectional" :
+- sc->sc_data_direction == DMA_TO_DEVICE ? "write" : "read",
+- conn->id, sc, sc->cmnd[0], ctask->itt,
+- scsi_bufflen(sc), scsi_bidi_cmnd(sc) ? scsi_in(sc)->length : 0,
+- session->cmdsn, session->max_cmdsn - session->exp_cmdsn + 1);
+- return 0;
+ }
++EXPORT_SYMBOL_GPL(iscsi_prep_scsi_cmd_pdu);
+
+ /**
+ * iscsi_complete_command - return command back to scsi-ml
+@@ -330,16 +187,13 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)
+ */
+ static void iscsi_complete_command(struct iscsi_cmd_task *ctask)
+ {
+- struct iscsi_conn *conn = ctask->conn;
+- struct iscsi_session *session = conn->session;
++ 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;
+- if (conn->ctask == ctask)
+- conn->ctask = NULL;
+ list_del_init(&ctask->running);
+ __kfifo_put(session->cmdpool.queue, (void*)&ctask, sizeof(void*));
+ sc->scsi_done(sc);
+@@ -350,124 +204,27 @@ 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);
+ }
+
+-/*
+- * session lock must be held
+- */
+-static void fail_command(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
+- int err)
++static void iscsi_put_ctask(struct iscsi_cmd_task *ctask)
+ {
+- struct scsi_cmnd *sc;
+-
+- sc = ctask->sc;
+- if (!sc)
+- return;
+-
+- if (ctask->state == ISCSI_TASK_PENDING)
+- /*
+- * cmd never made it to the xmit thread, so we should not count
+- * the cmd in the sequencing
+- */
+- conn->session->queued_cmdsn--;
+- else
+- conn->session->tt->cleanup_cmd_task(conn, ctask);
+-
+- sc->result = err;
+- if (!scsi_bidi_cmnd(sc))
+- scsi_set_resid(sc, scsi_bufflen(sc));
+- else {
+- scsi_out(sc)->resid = scsi_out(sc)->length;
+- scsi_in(sc)->resid = scsi_in(sc)->length;
+- }
+- if (conn->ctask == ctask)
+- conn->ctask = NULL;
+- /* release ref from queuecommand */
++ spin_lock_bh(&ctask->conn->session->lock);
+ __iscsi_put_ctask(ctask);
++ spin_unlock_bh(&ctask->conn->session->lock);
+ }
+
+ /**
+- * iscsi_free_mgmt_task - return mgmt task back to pool
+- * @conn: iscsi connection
+- * @mtask: mtask
+- *
+- * Must be called with session lock.
+- */
+-void iscsi_free_mgmt_task(struct iscsi_conn *conn,
+- struct iscsi_mgmt_task *mtask)
+-{
+- list_del_init(&mtask->running);
+- if (conn->login_mtask == mtask)
+- return;
+-
+- if (conn->ping_mtask == mtask)
+- conn->ping_mtask = NULL;
+- __kfifo_put(conn->session->mgmtpool.queue,
+- (void*)&mtask, sizeof(void*));
+-}
+-EXPORT_SYMBOL_GPL(iscsi_free_mgmt_task);
+-
+-static struct iscsi_mgmt_task *
+-__iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+- char *data, uint32_t data_size)
+-{
+- struct iscsi_session *session = conn->session;
+- struct iscsi_mgmt_task *mtask;
+-
+- if (session->state == ISCSI_STATE_TERMINATE)
+- return NULL;
+-
+- 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);
+-
+- if (!__kfifo_get(session->mgmtpool.queue,
+- (void*)&mtask, sizeof(void*)))
+- return NULL;
+- }
+-
+- if (data_size) {
+- memcpy(mtask->data, data, data_size);
+- mtask->data_count = data_size;
+- } else
+- mtask->data_count = 0;
+-
+- memcpy(mtask->hdr, hdr, sizeof(struct iscsi_hdr));
+- INIT_LIST_HEAD(&mtask->running);
+- list_add_tail(&mtask->running, &conn->mgmtqueue);
+- return mtask;
+-}
+-
+-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;
+- struct iscsi_session *session = conn->session;
+- int err = 0;
+-
+- spin_lock_bh(&session->lock);
+- if (!__iscsi_conn_send_pdu(conn, hdr, data, data_size))
+- err = -EPERM;
+- spin_unlock_bh(&session->lock);
+- scsi_queue_work(session->host, &conn->xmitwork);
+- return err;
+-}
+-EXPORT_SYMBOL_GPL(iscsi_conn_send_pdu);
+-
+-/**
+ * iscsi_cmd_rsp - SCSI Command Response processing
+ * @conn: iscsi connection
+ * @hdr: iscsi header
+@@ -478,15 +235,21 @@ EXPORT_SYMBOL_GPL(iscsi_conn_send_pdu);
+ * iscsi_cmd_rsp sets up the scsi_cmnd fields based on the PDU and
+ * then completes the command and task.
+ **/
+-static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+- struct iscsi_cmd_task *ctask, char *data,
+- int datalen)
++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;
+
+- iscsi_update_cmdsn(session, (struct iscsi_nopin*)rhdr);
++ 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;
+@@ -501,9 +264,8 @@ static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+
+ if (datalen < 2) {
+ invalid_datalen:
+- iscsi_conn_printk(KERN_ERR, conn,
+- "Got CHECK_CONDITION but invalid data "
+- "buffer size of %d\n", 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;
+ }
+@@ -518,36 +280,28 @@ invalid_datalen:
+ min_t(uint16_t, senselen, SCSI_SENSE_BUFFERSIZE));
+ }
+
+- if (rhdr->flags & (ISCSI_FLAG_CMD_BIDI_UNDERFLOW |
+- ISCSI_FLAG_CMD_BIDI_OVERFLOW)) {
+- int res_count = be32_to_cpu(rhdr->bi_residual_count);
+-
+- if (scsi_bidi_cmnd(sc) && res_count > 0 &&
+- (rhdr->flags & ISCSI_FLAG_CMD_BIDI_OVERFLOW ||
+- res_count <= scsi_in(sc)->length))
+- scsi_in(sc)->resid = res_count;
+- else
+- sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status;
+- }
++ if (sc->sc_data_direction == DMA_TO_DEVICE)
++ goto out;
+
+- if (rhdr->flags & (ISCSI_FLAG_CMD_UNDERFLOW |
+- ISCSI_FLAG_CMD_OVERFLOW)) {
++ if (rhdr->flags & ISCSI_FLAG_CMD_UNDERFLOW) {
+ int res_count = be32_to_cpu(rhdr->residual_count);
+
+- if (res_count > 0 &&
+- (rhdr->flags & ISCSI_FLAG_CMD_OVERFLOW ||
+- res_count <= scsi_bufflen(sc)))
+- /* write side for bidi or uni-io set_resid */
+- scsi_set_resid(sc, res_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)
+@@ -557,51 +311,18 @@ static void iscsi_tmf_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
+ conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1;
+ conn->tmfrsp_pdus_cnt++;
+
+- if (conn->tmf_state != TMF_QUEUED)
++ if (conn->tmabort_state != TMABORT_INITIAL)
+ return;
+
+ if (tmf->response == ISCSI_TMF_RSP_COMPLETE)
+- conn->tmf_state = TMF_SUCCESS;
++ conn->tmabort_state = TMABORT_SUCCESS;
+ else if (tmf->response == ISCSI_TMF_RSP_NO_TASK)
+- conn->tmf_state = TMF_NOT_FOUND;
++ conn->tmabort_state = TMABORT_NOT_FOUND;
+ else
+- conn->tmf_state = TMF_FAILED;
++ conn->tmabort_state = TMABORT_FAILED;
+ wake_up(&conn->ehwait);
+ }
+
+-static void iscsi_send_nopout(struct iscsi_conn *conn, struct iscsi_nopin *rhdr)
+-{
+- struct iscsi_nopout hdr;
+- struct iscsi_mgmt_task *mtask;
+-
+- if (!rhdr && conn->ping_mtask)
+- return;
+-
+- memset(&hdr, 0, sizeof(struct iscsi_nopout));
+- hdr.opcode = ISCSI_OP_NOOP_OUT | ISCSI_OP_IMMEDIATE;
+- hdr.flags = ISCSI_FLAG_CMD_FINAL;
+-
+- if (rhdr) {
+- memcpy(hdr.lun, rhdr->lun, 8);
+- hdr.ttt = rhdr->ttt;
+- hdr.itt = RESERVED_ITT;
+- } else
+- hdr.ttt = RESERVED_ITT;
+-
+- mtask = __iscsi_conn_send_pdu(conn, (struct iscsi_hdr *)&hdr, NULL, 0);
+- if (!mtask) {
+- iscsi_conn_printk(KERN_ERR, conn, "Could not send nopout\n");
+- return;
+- }
+-
+- /* only track our nops */
+- if (!rhdr) {
+- conn->ping_mtask = mtask;
+- conn->last_ping = jiffies;
+- }
+- scsi_queue_work(conn->session->host, &conn->xmitwork);
+-}
+-
+ static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ char *data, int datalen)
+ {
+@@ -618,10 +339,9 @@ static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ if (ntoh24(reject->dlength) >= sizeof(struct iscsi_hdr)) {
+ memcpy(&rejected_pdu, data, sizeof(struct iscsi_hdr));
+ itt = get_itt(rejected_pdu.itt);
+- iscsi_conn_printk(KERN_ERR, conn,
+- "itt 0x%x had pdu (op 0x%x) rejected "
+- "due to DataDigest error.\n", itt,
+- rejected_pdu.opcode);
++ printk(KERN_ERR "itt 0x%x had pdu (op 0x%x) rejected "
++ "due to DataDigest error.\n", itt,
++ rejected_pdu.opcode);
+ }
+ }
+ return 0;
+@@ -638,8 +358,8 @@ static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ * queuecommand or send generic. session lock must be held and verify
+ * itt must have been called.
+ */
+-static int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+- char *data, int datalen)
++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;
+@@ -647,7 +367,6 @@ static int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ struct iscsi_mgmt_task *mtask;
+ uint32_t itt;
+
+- conn->last_recv = jiffies;
+ if (hdr->itt != RESERVED_ITT)
+ itt = get_itt(hdr->itt);
+ else
+@@ -662,8 +381,8 @@ static int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ switch(opcode) {
+ case ISCSI_OP_SCSI_CMD_RSP:
+ BUG_ON((void*)ctask != ctask->sc->SCp.ptr);
+- iscsi_scsi_cmd_rsp(conn, hdr, ctask, data,
+- datalen);
++ 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);
+@@ -686,7 +405,11 @@ static int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ debug_scsi("immrsp [op 0x%x cid %d itt 0x%x len %d]\n",
+ opcode, conn->id, mtask->itt, datalen);
+
+- iscsi_update_cmdsn(session, (struct iscsi_nopin*)hdr);
++ rc = iscsi_check_assign_cmdsn(session,
++ (struct iscsi_nopin*)hdr);
++ if (rc)
++ goto done;
++
+ switch(opcode) {
+ case ISCSI_OP_LOGOUT_RSP:
+ if (datalen) {
+@@ -703,7 +426,10 @@ static int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ */
+ if (iscsi_recv_pdu(conn->cls_conn, hdr, data, datalen))
+ rc = ISCSI_ERR_CONN_FAILED;
+- iscsi_free_mgmt_task(conn, mtask);
++ 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) {
+@@ -712,35 +438,30 @@ static int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ }
+
+ iscsi_tmf_rsp(conn, hdr);
+- iscsi_free_mgmt_task(conn, mtask);
+ break;
+ case ISCSI_OP_NOOP_IN:
+- if (hdr->ttt != cpu_to_be32(ISCSI_RESERVED_TAG) ||
+- datalen) {
++ 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 (conn->ping_mtask != mtask) {
+- /*
+- * If this is not in response to one of our
+- * nops then it must be from userspace.
+- */
+- if (iscsi_recv_pdu(conn->cls_conn, hdr, data,
+- datalen))
+- rc = ISCSI_ERR_CONN_FAILED;
+- } else
+- mod_timer(&conn->transport_timer,
+- jiffies + conn->recv_timeout);
+- iscsi_free_mgmt_task(conn, mtask);
++ 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) {
+- iscsi_update_cmdsn(session, (struct iscsi_nopin*)hdr);
++ rc = iscsi_check_assign_cmdsn(session,
++ (struct iscsi_nopin*)hdr);
++ if (rc)
++ goto done;
+
+ switch(opcode) {
+ case ISCSI_OP_NOOP_IN:
+@@ -752,7 +473,8 @@ static int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ if (hdr->ttt == cpu_to_be32(ISCSI_RESERVED_TAG))
+ break;
+
+- iscsi_send_nopout(conn, (struct iscsi_nopin*)hdr);
++ 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);
+@@ -769,8 +491,10 @@ static int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ } 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)
+@@ -795,13 +519,18 @@ int iscsi_verify_itt(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ if (hdr->itt != RESERVED_ITT) {
+ if (((__force u32)hdr->itt & ISCSI_AGE_MASK) !=
+ (session->age << ISCSI_AGE_SHIFT)) {
+- iscsi_conn_printk(KERN_ERR, conn,
+- "received itt %x expected session "
+- "age (%x)\n", (__force u32)hdr->itt,
+- session->age & ISCSI_AGE_MASK);
++ 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;
+@@ -810,17 +539,16 @@ int iscsi_verify_itt(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ ctask = session->cmds[itt];
+
+ if (!ctask->sc) {
+- iscsi_conn_printk(KERN_INFO, conn, "dropping ctask "
+- "with itt 0x%x\n", ctask->itt);
++ 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) {
+- iscsi_conn_printk(KERN_ERR, conn,
+- "iscsi: ctask's session age %d, "
+- "expected %d\n", 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;
+ }
+ }
+@@ -850,110 +578,30 @@ void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err)
+ }
+ EXPORT_SYMBOL_GPL(iscsi_conn_failure);
+
+-static void iscsi_prep_mtask(struct iscsi_conn *conn,
+- struct iscsi_mgmt_task *mtask)
+-{
+- struct iscsi_session *session = conn->session;
+- struct iscsi_hdr *hdr = mtask->hdr;
+- struct iscsi_nopout *nop = (struct iscsi_nopout *)hdr;
+-
+- if (hdr->opcode != (ISCSI_OP_LOGIN | ISCSI_OP_IMMEDIATE) &&
+- hdr->opcode != (ISCSI_OP_TEXT | ISCSI_OP_IMMEDIATE))
+- nop->exp_statsn = cpu_to_be32(conn->exp_statsn);
+- /*
+- * pre-format CmdSN for outgoing PDU.
+- */
+- nop->cmdsn = cpu_to_be32(session->cmdsn);
+- if (hdr->itt != RESERVED_ITT) {
+- hdr->itt = build_itt(mtask->itt, session->age);
+- /*
+- * TODO: We always use immediate, so we never hit this.
+- * If we start to send tmfs or nops as non-immediate then
+- * we should start checking the cmdsn numbers for mgmt tasks.
+- */
+- if (conn->c_stage == ISCSI_CONN_STARTED &&
+- !(hdr->opcode & ISCSI_OP_IMMEDIATE)) {
+- session->queued_cmdsn++;
+- session->cmdsn++;
+- }
+- }
+-
+- if (session->tt->init_mgmt_task)
+- session->tt->init_mgmt_task(conn, mtask);
+-
+- debug_scsi("mgmtpdu [op 0x%x hdr->itt 0x%x datalen %d]\n",
+- hdr->opcode & ISCSI_OPCODE_MASK, hdr->itt,
+- mtask->data_count);
+-}
+-
+ static int iscsi_xmit_mtask(struct iscsi_conn *conn)
+ {
+ struct iscsi_hdr *hdr = conn->mtask->hdr;
+- int rc;
+-
+- if ((hdr->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_LOGOUT)
+- conn->session->state = ISCSI_STATE_LOGGING_OUT;
+- spin_unlock_bh(&conn->session->lock);
++ 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);
+- spin_lock_bh(&conn->session->lock);
+ if (rc)
+ return rc;
+
+ /* done with this in-progress mtask */
+ conn->mtask = NULL;
+- return 0;
+-}
+-
+-static int iscsi_check_cmdsn_window_closed(struct iscsi_conn *conn)
+-{
+- struct iscsi_session *session = conn->session;
+
+- /*
+- * Check for iSCSI window and take care of CmdSN wrap-around
+- */
+- if (!iscsi_sna_lte(session->queued_cmdsn, session->max_cmdsn)) {
+- debug_scsi("iSCSI CmdSN closed. ExpCmdSn %u MaxCmdSN %u "
+- "CmdSN %u/%u\n", session->exp_cmdsn,
+- session->max_cmdsn, session->cmdsn,
+- session->queued_cmdsn);
+- return -ENOSPC;
++ if (was_logout) {
++ set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
++ return -ENODATA;
+ }
+ return 0;
+ }
+
+-static int iscsi_xmit_ctask(struct iscsi_conn *conn)
+-{
+- struct iscsi_cmd_task *ctask = conn->ctask;
+- int rc;
+-
+- __iscsi_get_ctask(ctask);
+- spin_unlock_bh(&conn->session->lock);
+- rc = conn->session->tt->xmit_cmd_task(conn, ctask);
+- spin_lock_bh(&conn->session->lock);
+- __iscsi_put_ctask(ctask);
+- if (!rc)
+- /* done with this ctask */
+- conn->ctask = NULL;
+- return rc;
+-}
+-
+-/**
+- * iscsi_requeue_ctask - requeue ctask to run from session workqueue
+- * @ctask: ctask to requeue
+- *
+- * LLDs that need to run a ctask from the session workqueue should call
+- * this. The session lock must be held.
+- */
+-void iscsi_requeue_ctask(struct iscsi_cmd_task *ctask)
+-{
+- struct iscsi_conn *conn = ctask->conn;
+-
+- list_move_tail(&ctask->running, &conn->requeue);
+- scsi_queue_work(conn->session->host, &conn->xmitwork);
+-}
+-EXPORT_SYMBOL_GPL(iscsi_requeue_ctask);
+-
+ /**
+ * iscsi_data_xmit - xmit any command into the scheduled connection
+ * @conn: iscsi connection
+@@ -965,106 +613,106 @@ EXPORT_SYMBOL_GPL(iscsi_requeue_ctask);
+ **/
+ static int iscsi_data_xmit(struct iscsi_conn *conn)
+ {
++ struct iscsi_transport *tt;
+ int rc = 0;
+
+- spin_lock_bh(&conn->session->lock);
+ if (unlikely(conn->suspend_tx)) {
+ debug_scsi("conn %d Tx suspended!\n", conn->id);
+- spin_unlock_bh(&conn->session->lock);
+ 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) {
+- rc = iscsi_xmit_ctask(conn);
++ 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 mgmt pdus like nops before commands since we should
+- * only have one nop-out as a ping from us and targets should not
+- * overflow us with nop-ins
+- */
+-check_mgmt:
+- while (!list_empty(&conn->mgmtqueue)) {
+- conn->mtask = list_entry(conn->mgmtqueue.next,
+- struct iscsi_mgmt_task, running);
+- if (conn->session->state == ISCSI_STATE_LOGGING_OUT) {
+- iscsi_free_mgmt_task(conn, conn->mtask);
+- conn->mtask = NULL;
+- continue;
+- }
+-
+- iscsi_prep_mtask(conn, conn->mtask);
+- list_move_tail(conn->mgmtqueue.next, &conn->mgmt_run_list);
+- 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 pending command queue */
++ /* process command queue */
++ spin_lock_bh(&conn->session->lock);
+ while (!list_empty(&conn->xmitqueue)) {
+- if (conn->tmf_state == TMF_QUEUED)
+- break;
+-
++ /*
++ * 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);
+- if (conn->session->state == ISCSI_STATE_LOGGING_OUT) {
+- fail_command(conn, conn->ctask, DID_IMM_RETRY << 16);
+- continue;
+- }
+- if (iscsi_prep_scsi_cmd_pdu(conn->ctask)) {
+- fail_command(conn, conn->ctask, DID_ABORT << 16);
+- continue;
+- }
+-
+ conn->ctask->state = ISCSI_TASK_RUNNING;
+ list_move_tail(conn->xmitqueue.next, &conn->run_list);
+- rc = iscsi_xmit_ctask(conn);
+- if (rc)
+- goto again;
+- /*
+- * we could continuously get new ctask requests so
+- * we need to check the mgmt queue for nops that need to
+- * be sent to aviod starvation
+- */
+- if (!list_empty(&conn->mgmtqueue))
+- goto check_mgmt;
+- }
++ __iscsi_get_ctask(conn->ctask);
++ spin_unlock_bh(&conn->session->lock);
+
+- while (!list_empty(&conn->requeue)) {
+- if (conn->session->fast_abort && conn->tmf_state != TMF_INITIAL)
+- break;
++ rc = tt->xmit_cmd_task(conn, conn->ctask);
+
+- /*
+- * we always do fastlogout - conn stop code will clean up.
+- */
+- if (conn->session->state == ISCSI_STATE_LOGGING_OUT)
+- break;
+-
+- conn->ctask = list_entry(conn->requeue.next,
+- struct iscsi_cmd_task, running);
+- conn->ctask->state = ISCSI_TASK_RUNNING;
+- list_move_tail(conn->requeue.next, &conn->run_list);
+- rc = iscsi_xmit_ctask(conn);
+- if (rc)
++ spin_lock_bh(&conn->session->lock);
++ __iscsi_put_ctask(conn->ctask);
++ if (rc) {
++ spin_unlock_bh(&conn->session->lock);
+ goto again;
+- if (!list_empty(&conn->mgmtqueue))
+- goto check_mgmt;
++ }
+ }
+ 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))
+- rc = -ENODATA;
+- spin_unlock_bh(&conn->session->lock);
++ return -ENODATA;
++
+ return rc;
+ }
+
+@@ -1076,9 +724,11 @@ static void iscsi_xmitworker(struct work_struct *work)
+ /*
+ * 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 {
+@@ -1090,8 +740,6 @@ enum {
+ FAILURE_SESSION_TERMINATE,
+ FAILURE_SESSION_IN_RECOVERY,
+ FAILURE_SESSION_RECOVERY_TIMEOUT,
+- FAILURE_SESSION_LOGGING_OUT,
+- FAILURE_SESSION_NOT_READY,
+ };
+
+ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
+@@ -1107,16 +755,9 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
+ sc->SCp.ptr = NULL;
+
+ host = sc->device->host;
+- spin_unlock(host->host_lock);
+-
+ session = iscsi_hostdata(host->hostdata);
+- spin_lock(&session->lock);
+
+- reason = iscsi_session_chkready(session_to_cls(session));
+- if (reason) {
+- sc->result = reason;
+- goto fault;
+- }
++ spin_lock(&session->lock);
+
+ /*
+ * ISCSI_STATE_FAILED is a temp. state. The recovery
+@@ -1131,82 +772,77 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
+ * be entering our queuecommand while a block is starting
+ * up because the block code is not locked)
+ */
+- switch (session->state) {
+- case ISCSI_STATE_IN_RECOVERY:
++ if (session->state == ISCSI_STATE_IN_RECOVERY) {
+ reason = FAILURE_SESSION_IN_RECOVERY;
+- sc->result = DID_IMM_RETRY << 16;
+- break;
+- case ISCSI_STATE_LOGGING_OUT:
+- reason = FAILURE_SESSION_LOGGING_OUT;
+- sc->result = DID_IMM_RETRY << 16;
+- break;
+- case ISCSI_STATE_RECOVERY_FAILED:
++ goto reject;
++ }
++
++ if (session->state == ISCSI_STATE_RECOVERY_FAILED)
+ reason = FAILURE_SESSION_RECOVERY_TIMEOUT;
+- sc->result = DID_NO_CONNECT << 16;
+- break;
+- case ISCSI_STATE_TERMINATE:
++ else if (session->state == ISCSI_STATE_TERMINATE)
+ reason = FAILURE_SESSION_TERMINATE;
+- sc->result = DID_NO_CONNECT << 16;
+- break;
+- default:
++ else
+ reason = FAILURE_SESSION_FREED;
+- sc->result = DID_NO_CONNECT << 16;
+- }
+ 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;
+- sc->result = DID_NO_CONNECT << 16;
+ goto fault;
+ }
+
+- if (iscsi_check_cmdsn_window_closed(conn)) {
+- reason = FAILURE_WINDOW_CLOSED;
+- goto reject;
+- }
+-
+ if (!__kfifo_get(session->cmdpool.queue, (void*)&ctask,
+ sizeof(void*))) {
+ reason = FAILURE_OOM;
+ goto reject;
+ }
+- session->queued_cmdsn++;
+-
+ 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);
+- spin_lock(host->host_lock);
+ return 0;
+
+ reject:
+ spin_unlock(&session->lock);
+ debug_scsi("cmd 0x%x rejected (%d)\n", sc->cmnd[0], reason);
+- spin_lock(host->host_lock);
+ return SCSI_MLQUEUE_HOST_BUSY;
+
+ fault:
+ spin_unlock(&session->lock);
+- debug_scsi("iscsi: cmd 0x%x is not queued (%d)\n", sc->cmnd[0], reason);
+- if (!scsi_bidi_cmnd(sc))
+- scsi_set_resid(sc, scsi_bufflen(sc));
+- else {
+- scsi_out(sc)->resid = scsi_out(sc)->length;
+- scsi_in(sc)->resid = scsi_in(sc)->length;
+- }
++ 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);
+- spin_lock(host->host_lock);
+ return 0;
+ }
+ EXPORT_SYMBOL_GPL(iscsi_queuecommand);
+@@ -1220,15 +856,106 @@ int iscsi_change_queue_depth(struct scsi_device *sdev, int 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 (session->leadconn)
+- wake_up(&session->leadconn->ehwait);
++ if (conn)
++ wake_up(&conn->ehwait);
+ }
+ spin_unlock_bh(&session->lock);
+ }
+@@ -1239,25 +966,30 @@ 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;
+
+- mutex_lock(&session->eh_mutex);
+ 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);
+- mutex_unlock(&session->eh_mutex);
+ 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);
+- mutex_unlock(&session->eh_mutex);
++
+ /*
+ * we drop the lock here but the leadconn cannot be destoyed while
+ * we are in the scsi eh
+ */
+- iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
++ 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,
+@@ -1267,496 +999,324 @@ failed:
+ if (signal_pending(current))
+ flush_signals(current);
+
+- mutex_lock(&session->eh_mutex);
+ spin_lock_bh(&session->lock);
+ if (session->state == ISCSI_STATE_LOGGED_IN)
+- iscsi_session_printk(KERN_INFO, session,
+- "host reset succeeded\n");
++ printk(KERN_INFO "iscsi: host reset succeeded\n");
+ else
+ goto failed;
+ spin_unlock_bh(&session->lock);
+- mutex_unlock(&session->eh_mutex);
++
+ return SUCCESS;
+ }
+ EXPORT_SYMBOL_GPL(iscsi_eh_host_reset);
+
+-static void iscsi_tmf_timedout(unsigned long data)
++static void iscsi_tmabort_timedout(unsigned long data)
+ {
+- struct iscsi_conn *conn = (struct iscsi_conn *)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->tmf_state == TMF_QUEUED) {
+- conn->tmf_state = TMF_TIMEDOUT;
+- debug_scsi("tmf timedout\n");
++ 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);
+ }
+
+-static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn,
+- struct iscsi_tm *hdr, int age,
+- int timeout)
++/* 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_mgmt_task *mtask;
++ struct iscsi_tm *hdr = &conn->tmhdr;
++ int rc;
+
+- mtask = __iscsi_conn_send_pdu(conn, (struct iscsi_hdr *)hdr,
+- NULL, 0);
+- if (!mtask) {
+- spin_unlock_bh(&session->lock);
++ /*
++ * 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);
+- spin_lock_bh(&session->lock);
+- debug_scsi("tmf exec failure\n");
+- return -EPERM;
++ debug_scsi("abort sent failure [itt 0x%x] %d\n", ctask->itt,
++ rc);
++ return rc;
+ }
+- conn->tmfcmd_pdus_cnt++;
+- conn->tmf_timer.expires = timeout * HZ + jiffies;
+- conn->tmf_timer.function = iscsi_tmf_timedout;
+- conn->tmf_timer.data = (unsigned long)conn;
+- add_timer(&conn->tmf_timer);
+- debug_scsi("tmf set timeout\n");
+
++ 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(&session->eh_mutex);
+- scsi_queue_work(session->host, &conn->xmitwork);
++ mutex_unlock(&conn->xmitmutex);
+
+ /*
+ * block eh thread until:
+ *
+- * 1) tmf response
+- * 2) tmf timeout
++ * 1) abort response
++ * 2) abort timeout
+ * 3) session is terminated or restarted or userspace has
+ * given up on recovery
+ */
+- wait_event_interruptible(conn->ehwait, age != session->age ||
++ wait_event_interruptible(conn->ehwait,
++ sc->SCp.phase != session->age ||
+ session->state != ISCSI_STATE_LOGGED_IN ||
+- conn->tmf_state != TMF_QUEUED);
++ conn->tmabort_state != TMABORT_INITIAL);
+ if (signal_pending(current))
+ flush_signals(current);
+- del_timer_sync(&conn->tmf_timer);
++ del_timer_sync(&conn->tmabort_timer);
+
+- mutex_lock(&session->eh_mutex);
+- spin_lock_bh(&session->lock);
+- /* if the session drops it will clean up the mtask */
+- if (age != session->age ||
+- session->state != ISCSI_STATE_LOGGED_IN)
+- return -ENOTCONN;
++ mutex_lock(&conn->xmitmutex);
+ return 0;
+ }
+
+ /*
+- * Fail commands. session lock held and recv side suspended and xmit
+- * thread flushed
++ * xmit mutex and session lock must be held
+ */
+-static void fail_all_commands(struct iscsi_conn *conn, unsigned lun,
+- int error)
++static struct iscsi_mgmt_task *
++iscsi_remove_mgmt_task(struct kfifo *fifo, uint32_t itt)
+ {
+- struct iscsi_cmd_task *ctask, *tmp;
++ int i, nr_tasks = __kfifo_len(fifo) / sizeof(void*);
++ struct iscsi_mgmt_task *task;
+
+- if (conn->ctask && (conn->ctask->sc->device->lun == lun || lun == -1))
+- conn->ctask = NULL;
++ debug_scsi("searching %d tasks\n", nr_tasks);
+
+- /* flush pending */
+- list_for_each_entry_safe(ctask, tmp, &conn->xmitqueue, running) {
+- if (lun == ctask->sc->device->lun || lun == -1) {
+- debug_scsi("failing pending sc %p itt 0x%x\n",
+- ctask->sc, ctask->itt);
+- fail_command(conn, ctask, error << 16);
+- }
+- }
++ for (i = 0; i < nr_tasks; i++) {
++ __kfifo_get(fifo, (void*)&task, sizeof(void*));
++ debug_scsi("check task %u\n", task->itt);
+
+- list_for_each_entry_safe(ctask, tmp, &conn->requeue, running) {
+- if (lun == ctask->sc->device->lun || lun == -1) {
+- debug_scsi("failing requeued sc %p itt 0x%x\n",
+- ctask->sc, ctask->itt);
+- fail_command(conn, ctask, error << 16);
++ if (task->itt == itt) {
++ debug_scsi("matched task\n");
++ return task;
+ }
+- }
+
+- /* fail all other running */
+- list_for_each_entry_safe(ctask, tmp, &conn->run_list, running) {
+- if (lun == ctask->sc->device->lun || lun == -1) {
+- debug_scsi("failing in progress sc %p itt 0x%x\n",
+- ctask->sc, ctask->itt);
+- fail_command(conn, ctask, DID_BUS_BUSY << 16);
+- }
++ __kfifo_put(fifo, (void*)&task, sizeof(void*));
+ }
++ return NULL;
+ }
+
+-static void iscsi_suspend_tx(struct iscsi_conn *conn)
+-{
+- set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
+- scsi_flush_work(conn->session->host);
+-}
+-
+-static void iscsi_start_tx(struct iscsi_conn *conn)
+-{
+- clear_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
+- scsi_queue_work(conn->session->host, &conn->xmitwork);
+-}
+-
+-static enum scsi_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *scmd)
++static int iscsi_ctask_mtask_cleanup(struct iscsi_cmd_task *ctask)
+ {
+- struct iscsi_cls_session *cls_session;
+- struct iscsi_session *session;
+- struct iscsi_conn *conn;
+- enum scsi_eh_timer_return rc = EH_NOT_HANDLED;
+-
+- cls_session = starget_to_session(scsi_target(scmd->device));
+- session = class_to_transport_session(cls_session);
+-
+- debug_scsi("scsi cmd %p timedout\n", scmd);
+-
+- spin_lock(&session->lock);
+- if (session->state != ISCSI_STATE_LOGGED_IN) {
+- /*
+- * We are probably in the middle of iscsi recovery so let
+- * that complete and handle the error.
+- */
+- rc = EH_RESET_TIMER;
+- goto done;
+- }
++ struct iscsi_conn *conn = ctask->conn;
++ struct iscsi_session *session = conn->session;
+
+- conn = session->leadconn;
+- if (!conn) {
+- /* In the middle of shuting down */
+- rc = EH_RESET_TIMER;
+- goto done;
+- }
++ if (!ctask->mtask)
++ return -EINVAL;
+
+- if (!conn->recv_timeout && !conn->ping_timeout)
+- goto done;
+- /*
+- * if the ping timedout then we are in the middle of cleaning up
+- * and can let the iscsi eh handle it
+- */
+- if (time_before_eq(conn->last_recv + (conn->recv_timeout * HZ) +
+- (conn->ping_timeout * HZ), jiffies))
+- rc = EH_RESET_TIMER;
+- /*
+- * if we are about to check the transport then give the command
+- * more time
+- */
+- if (time_before_eq(conn->last_recv + (conn->recv_timeout * HZ),
+- jiffies))
+- rc = EH_RESET_TIMER;
+- /* if in the middle of checking the transport then give us more time */
+- if (conn->ping_mtask)
+- rc = EH_RESET_TIMER;
+-done:
+- spin_unlock(&session->lock);
+- debug_scsi("return %s\n", rc == EH_RESET_TIMER ? "timer reset" : "nh");
+- return rc;
++ 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;
+ }
+
+-static void iscsi_check_transport_timeouts(unsigned long data)
++/*
++ * session lock and xmitmutex must be held
++ */
++static void fail_command(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
++ int err)
+ {
+- struct iscsi_conn *conn = (struct iscsi_conn *)data;
+- struct iscsi_session *session = conn->session;
+- unsigned long recv_timeout, next_timeout = 0, last_recv;
++ struct scsi_cmnd *sc;
+
+- spin_lock(&session->lock);
+- if (session->state != ISCSI_STATE_LOGGED_IN)
+- goto done;
+-
+- recv_timeout = conn->recv_timeout;
+- if (!recv_timeout)
+- goto done;
+-
+- recv_timeout *= HZ;
+- last_recv = conn->last_recv;
+- if (conn->ping_mtask &&
+- time_before_eq(conn->last_ping + (conn->ping_timeout * HZ),
+- jiffies)) {
+- iscsi_conn_printk(KERN_ERR, conn, "ping timeout of %d secs "
+- "expired, last rx %lu, last ping %lu, "
+- "now %lu\n", conn->ping_timeout, last_recv,
+- conn->last_ping, jiffies);
+- spin_unlock(&session->lock);
+- iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
++ sc = ctask->sc;
++ if (!sc)
+ return;
+- }
+-
+- if (time_before_eq(last_recv + recv_timeout, jiffies)) {
+- /* send a ping to try to provoke some traffic */
+- debug_scsi("Sending nopout as ping on conn %p\n", conn);
+- iscsi_send_nopout(conn, NULL);
+- next_timeout = conn->last_ping + (conn->ping_timeout * HZ);
+- } else
+- next_timeout = last_recv + recv_timeout;
+
+- debug_scsi("Setting next tmo %lu\n", next_timeout);
+- mod_timer(&conn->transport_timer, next_timeout);
+-done:
+- spin_unlock(&session->lock);
+-}
++ conn->session->tt->cleanup_cmd_task(conn, ctask);
++ iscsi_ctask_mtask_cleanup(ctask);
+
+-static void iscsi_prep_abort_task_pdu(struct iscsi_cmd_task *ctask,
+- struct iscsi_tm *hdr)
+-{
+- memset(hdr, 0, sizeof(*hdr));
+- hdr->opcode = ISCSI_OP_SCSI_TMFUNC | ISCSI_OP_IMMEDIATE;
+- hdr->flags = ISCSI_TM_FUNC_ABORT_TASK & ISCSI_FLAG_TM_FUNC_MASK;
+- 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;
++ 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 Scsi_Host *host = sc->device->host;
+- struct iscsi_session *session = iscsi_hostdata(host->hostdata);
+- struct iscsi_conn *conn;
+ struct iscsi_cmd_task *ctask;
+- struct iscsi_tm *hdr;
+- int rc, age;
++ struct iscsi_conn *conn;
++ struct iscsi_session *session;
++ int rc;
+
+- mutex_lock(&session->eh_mutex);
+- spin_lock_bh(&session->lock);
+ /*
+ * 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");
+- spin_unlock_bh(&session->lock);
+- mutex_unlock(&session->eh_mutex);
+ 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->leadconn || session->state != ISCSI_STATE_LOGGED_IN ||
+- sc->SCp.phase != session->age) {
+- spin_unlock_bh(&session->lock);
+- mutex_unlock(&session->eh_mutex);
+- return FAILED;
+- }
+-
+- conn = session->leadconn;
+- conn->eh_abort_cnt++;
+- age = session->age;
+-
+- ctask = (struct iscsi_cmd_task *)sc->SCp.ptr;
+- debug_scsi("aborting [sc %p itt 0x%x]\n", sc, ctask->itt);
++ 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;
++ goto success_rel_mutex;
+ }
+
+- if (ctask->state == ISCSI_TASK_PENDING) {
+- fail_command(conn, ctask, DID_ABORT << 16);
+- goto success;
++ /* 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;
+ }
+
+- /* only have one tmf outstanding at a time */
+- if (conn->tmf_state != TMF_INITIAL)
+- goto failed;
+- conn->tmf_state = TMF_QUEUED;
++ if (ctask->state == ISCSI_TASK_PENDING)
++ goto success_cleanup;
+
+- hdr = &conn->tmhdr;
+- iscsi_prep_abort_task_pdu(ctask, hdr);
++ conn->tmabort_state = TMABORT_INITIAL;
+
+- if (iscsi_exec_task_mgmt_fn(conn, hdr, age, session->abort_timeout)) {
+- rc = FAILED;
++ 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->tmf_state) {
+- case TMF_SUCCESS:
+- spin_unlock_bh(&session->lock);
+- iscsi_suspend_tx(conn);
+- /*
+- * clean up task if aborted. grab the recv lock as a writer
+- */
+- write_lock_bh(conn->recv_lock);
+- spin_lock(&session->lock);
+- fail_command(conn, ctask, DID_ABORT << 16);
+- conn->tmf_state = TMF_INITIAL;
+- spin_unlock(&session->lock);
+- write_unlock_bh(conn->recv_lock);
+- iscsi_start_tx(conn);
+- goto success_unlocked;
+- case TMF_TIMEDOUT:
+- spin_unlock_bh(&session->lock);
+- iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
+- goto failed_unlocked;
+- case TMF_NOT_FOUND:
+- if (!sc->SCp.ptr) {
+- conn->tmf_state = TMF_INITIAL;
++ 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;
++ goto success_rel_mutex;
+ }
+ /* fall through */
+ default:
+- conn->tmf_state = TMF_INITIAL;
++ /* timedout or failed */
++ spin_unlock_bh(&session->lock);
++ iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
++ spin_lock_bh(&session->lock);
+ goto failed;
+ }
+
+-success:
+- spin_unlock_bh(&session->lock);
+-success_unlocked:
++success_cleanup:
+ debug_scsi("abort success [sc %lx itt 0x%x]\n", (long)sc, ctask->itt);
+- mutex_unlock(&session->eh_mutex);
+- return SUCCESS;
+-
+-failed:
+ spin_unlock_bh(&session->lock);
+-failed_unlocked:
+- debug_scsi("abort failed [sc %p itt 0x%x]\n", sc,
+- ctask ? ctask->itt : 0);
+- mutex_unlock(&session->eh_mutex);
+- return FAILED;
+-}
+-EXPORT_SYMBOL_GPL(iscsi_eh_abort);
+
+-static void iscsi_prep_lun_reset_pdu(struct scsi_cmnd *sc, struct iscsi_tm *hdr)
+-{
+- memset(hdr, 0, sizeof(*hdr));
+- hdr->opcode = ISCSI_OP_SCSI_TMFUNC | ISCSI_OP_IMMEDIATE;
+- hdr->flags = ISCSI_TM_FUNC_LOGICAL_UNIT_RESET & ISCSI_FLAG_TM_FUNC_MASK;
+- hdr->flags |= ISCSI_FLAG_CMD_FINAL;
+- int_to_scsilun(sc->device->lun, (struct scsi_lun *)hdr->lun);
+- hdr->rtt = RESERVED_ITT;
+-}
+-
+-int iscsi_eh_device_reset(struct scsi_cmnd *sc)
+-{
+- struct Scsi_Host *host = sc->device->host;
+- struct iscsi_session *session = iscsi_hostdata(host->hostdata);
+- struct iscsi_conn *conn;
+- struct iscsi_tm *hdr;
+- int rc = FAILED;
+-
+- debug_scsi("LU Reset [sc %p lun %u]\n", sc, sc->device->lun);
+-
+- mutex_lock(&session->eh_mutex);
+- spin_lock_bh(&session->lock);
+ /*
+- * Just check if we are not logged in. We cannot check for
+- * the phase because the reset could come from a ioctl.
++ * clean up task if aborted. we have the xmitmutex so grab
++ * the recv lock as a writer
+ */
+- if (!session->leadconn || session->state != ISCSI_STATE_LOGGED_IN)
+- goto unlock;
+- conn = session->leadconn;
+-
+- /* only have one tmf outstanding at a time */
+- if (conn->tmf_state != TMF_INITIAL)
+- goto unlock;
+- conn->tmf_state = TMF_QUEUED;
+-
+- hdr = &conn->tmhdr;
+- iscsi_prep_lun_reset_pdu(sc, hdr);
+-
+- if (iscsi_exec_task_mgmt_fn(conn, hdr, session->age,
+- session->lu_reset_timeout)) {
+- rc = FAILED;
+- goto unlock;
+- }
+-
+- switch (conn->tmf_state) {
+- case TMF_SUCCESS:
+- break;
+- case TMF_TIMEDOUT:
+- spin_unlock_bh(&session->lock);
+- iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
+- goto done;
+- default:
+- conn->tmf_state = TMF_INITIAL;
+- goto unlock;
+- }
+-
+- rc = SUCCESS;
+- spin_unlock_bh(&session->lock);
+-
+- iscsi_suspend_tx(conn);
+- /* need to grab the recv lock then session lock */
+ write_lock_bh(conn->recv_lock);
+ spin_lock(&session->lock);
+- fail_all_commands(conn, sc->device->lun, DID_ERROR);
+- conn->tmf_state = TMF_INITIAL;
++ fail_command(conn, ctask, DID_ABORT << 16);
+ spin_unlock(&session->lock);
+ write_unlock_bh(conn->recv_lock);
+
+- iscsi_start_tx(conn);
+- goto done;
++success_rel_mutex:
++ mutex_unlock(&conn->xmitmutex);
++ return SUCCESS;
+
+-unlock:
++failed:
+ spin_unlock_bh(&session->lock);
+-done:
+- debug_scsi("iscsi_eh_device_reset %s\n",
+- rc == SUCCESS ? "SUCCESS" : "FAILED");
+- mutex_unlock(&session->eh_mutex);
+- return rc;
++ 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_device_reset);
++EXPORT_SYMBOL_GPL(iscsi_eh_abort);
+
+-/*
+- * Pre-allocate a pool of @max items of @item_size. By default, the pool
+- * should be accessed via kfifo_{get,put} on q->queue.
+- * Optionally, the caller can obtain the array of object pointers
+- * by passing in a non-NULL @items pointer
+- */
+ int
+-iscsi_pool_init(struct iscsi_pool *q, int max, void ***items, int item_size)
++iscsi_pool_init(struct iscsi_queue *q, int max, void ***items, int item_size)
+ {
+- int i, num_arrays = 1;
++ int i;
+
+- memset(q, 0, sizeof(*q));
++ *items = kmalloc(max * sizeof(void*), GFP_KERNEL);
++ if (*items == NULL)
++ return -ENOMEM;
+
+ q->max = max;
+-
+- /* If the user passed an items pointer, he wants a copy of
+- * the array. */
+- if (items)
+- num_arrays++;
+- q->pool = kzalloc(num_arrays * max * sizeof(void*), GFP_KERNEL);
+- if (q->pool == NULL)
+- goto enomem;
++ 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))
+- goto enomem;
++ if (q->queue == ERR_PTR(-ENOMEM)) {
++ kfree(q->pool);
++ kfree(*items);
++ return -ENOMEM;
++ }
+
+ for (i = 0; i < max; i++) {
+- q->pool[i] = kzalloc(item_size, GFP_KERNEL);
++ q->pool[i] = kmalloc(item_size, GFP_KERNEL);
+ if (q->pool[i] == NULL) {
+- q->max = i;
+- goto enomem;
++ 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*));
+ }
+-
+- if (items) {
+- *items = q->pool + max;
+- memcpy(*items, q->pool, max * sizeof(void *));
+- }
+-
+ return 0;
+-
+-enomem:
+- iscsi_pool_free(q);
+- return -ENOMEM;
+ }
+ EXPORT_SYMBOL_GPL(iscsi_pool_init);
+
+-void iscsi_pool_free(struct iscsi_pool *q)
++void iscsi_pool_free(struct iscsi_queue *q, void **items)
+ {
+ int i;
+
+ for (i = 0; i < q->max; i++)
+- kfree(q->pool[i]);
+- if (q->pool)
+- kfree(q->pool);
++ kfree(items[i]);
++ kfree(q->pool);
++ kfree(items);
+ }
+ EXPORT_SYMBOL_GPL(iscsi_pool_free);
+
+@@ -1779,10 +1339,6 @@ EXPORT_SYMBOL_GPL(iscsi_pool_free);
+ * iscsi_session_setup - create iscsi cls session and host and session
+ * @scsit: scsi transport template
+ * @iscsit: iscsi transport template
+- * @cmds_max: scsi host can queue
+- * @qdepth: scsi host cmds per lun
+- * @cmd_task_size: LLD ctask private data size
+- * @mgmt_task_size: LLD mtask private data size
+ * @initial_cmdsn: initial CmdSN
+ * @hostno: host no allocated
+ *
+@@ -1792,7 +1348,6 @@ EXPORT_SYMBOL_GPL(iscsi_pool_free);
+ struct iscsi_cls_session *
+ iscsi_session_setup(struct iscsi_transport *iscsit,
+ struct scsi_transport_template *scsit,
+- uint16_t cmds_max, uint16_t qdepth,
+ int cmd_task_size, int mgmt_task_size,
+ uint32_t initial_cmdsn, uint32_t *hostno)
+ {
+@@ -1801,56 +1356,30 @@ iscsi_session_setup(struct iscsi_transport *iscsit,
+ struct iscsi_cls_session *cls_session;
+ int cmd_i;
+
+- if (qdepth > ISCSI_MAX_CMD_PER_LUN || qdepth < 1) {
+- if (qdepth != 0)
+- printk(KERN_ERR "iscsi: invalid queue depth of %d. "
+- "Queue depth must be between 1 and %d.\n",
+- qdepth, ISCSI_MAX_CMD_PER_LUN);
+- qdepth = ISCSI_DEF_CMD_PER_LUN;
+- }
+-
+- if (!is_power_of_2(cmds_max) || cmds_max >= ISCSI_MGMT_ITT_OFFSET ||
+- cmds_max < 2) {
+- if (cmds_max != 0)
+- printk(KERN_ERR "iscsi: invalid can_queue of %d. "
+- "can_queue must be a power of 2 and between "
+- "2 and %d - setting to %d.\n", cmds_max,
+- ISCSI_MGMT_ITT_OFFSET, ISCSI_DEF_XMIT_CMDS_MAX);
+- cmds_max = ISCSI_DEF_XMIT_CMDS_MAX;
+- }
+-
+ shost = scsi_host_alloc(iscsit->host_template,
+ hostdata_privsize(sizeof(*session)));
+ if (!shost)
+ return NULL;
+
+- /* the iscsi layer takes one task for reserve */
+- shost->can_queue = cmds_max - 1;
+- shost->cmd_per_lun = qdepth;
+ 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;
+- shost->transportt->eh_timed_out = iscsi_eh_cmd_timed_out;
+ *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->fast_abort = 1;
+- session->lu_reset_timeout = 15;
+- session->abort_timeout = 10;
+ session->mgmtpool_max = ISCSI_MGMT_CMDS_MAX;
+- session->cmds_max = cmds_max;
+- session->queued_cmdsn = session->cmdsn = initial_cmdsn;
++ 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;
+- mutex_init(&session->eh_mutex);
+
+ /* initialize SCSI PDU commands pool */
+ if (iscsi_pool_init(&session->cmdpool, session->cmds_max,
+@@ -1905,9 +1434,9 @@ module_put:
+ cls_session_fail:
+ scsi_remove_host(shost);
+ add_host_fail:
+- iscsi_pool_free(&session->mgmtpool);
++ iscsi_pool_free(&session->mgmtpool, (void**)session->mgmt_cmds);
+ mgmtpool_alloc_fail:
+- iscsi_pool_free(&session->cmdpool);
++ iscsi_pool_free(&session->cmdpool, (void**)session->cmds);
+ cmdpool_alloc_fail:
+ scsi_host_put(shost);
+ return NULL;
+@@ -1927,22 +1456,14 @@ void iscsi_session_teardown(struct iscsi_cls_session *cls_session)
+ struct iscsi_session *session = iscsi_hostdata(shost->hostdata);
+ struct module *owner = cls_session->transport->owner;
+
+- iscsi_remove_session(cls_session);
+ scsi_remove_host(shost);
+
+- iscsi_pool_free(&session->mgmtpool);
+- iscsi_pool_free(&session->cmdpool);
++ iscsi_pool_free(&session->mgmtpool, (void**)session->mgmt_cmds);
++ iscsi_pool_free(&session->cmdpool, (void**)session->cmds);
+
+- kfree(session->password);
+- kfree(session->password_in);
+- kfree(session->username);
+- kfree(session->username_in);
+ kfree(session->targetname);
+- kfree(session->netdev);
+- kfree(session->hwaddress);
+- kfree(session->initiatorname);
+
+- iscsi_free_session(cls_session);
++ iscsi_destroy_session(cls_session);
+ scsi_host_put(shost);
+ module_put(owner);
+ }
+@@ -1972,17 +1493,22 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
+ conn->c_stage = ISCSI_CONN_INITIAL_STAGE;
+ conn->id = conn_idx;
+ conn->exp_statsn = 0;
+- conn->tmf_state = TMF_INITIAL;
+-
+- init_timer(&conn->transport_timer);
+- conn->transport_timer.data = (unsigned long)conn;
+- conn->transport_timer.function = iscsi_check_transport_timeouts;
+-
++ conn->tmabort_state = TMABORT_INITIAL;
+ INIT_LIST_HEAD(&conn->run_list);
+ INIT_LIST_HEAD(&conn->mgmt_run_list);
+- INIT_LIST_HEAD(&conn->mgmtqueue);
+ INIT_LIST_HEAD(&conn->xmitqueue);
+- INIT_LIST_HEAD(&conn->requeue);
++
++ /* 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 */
+@@ -2000,7 +1526,8 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
+ goto login_mtask_data_alloc_fail;
+ conn->login_mtask->data = conn->data = data;
+
+- init_timer(&conn->tmf_timer);
++ init_timer(&conn->tmabort_timer);
++ mutex_init(&conn->xmitmutex);
+ init_waitqueue_head(&conn->ehwait);
+
+ return cls_conn;
+@@ -2009,6 +1536,10 @@ 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;
+ }
+@@ -2027,7 +1558,8 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn)
+ struct iscsi_session *session = conn->session;
+ unsigned long flags;
+
+- del_timer_sync(&conn->transport_timer);
++ set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
++ mutex_lock(&conn->xmitmutex);
+
+ spin_lock_bh(&session->lock);
+ conn->c_stage = ISCSI_CONN_CLEANUP_WAIT;
+@@ -2040,6 +1572,8 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn)
+ }
+ spin_unlock_bh(&session->lock);
+
++ mutex_unlock(&conn->xmitmutex);
++
+ /*
+ * Block until all in-progress commands for this connection
+ * time out or fail.
+@@ -2052,10 +1586,9 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn)
+ }
+ spin_unlock_irqrestore(session->host->host_lock, flags);
+ msleep_interruptible(500);
+- iscsi_conn_printk(KERN_INFO, conn, "iscsi conn_destroy(): "
+- "host_busy %d host_failed %d\n",
+- session->host->host_busy,
+- session->host->host_failed);
++ 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
+ */
+@@ -2063,17 +1596,23 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn)
+ }
+
+ /* flush queued up work because we free the connection below */
+- iscsi_suspend_tx(conn);
++ 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)
++ 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);
+@@ -2084,41 +1623,21 @@ int iscsi_conn_start(struct iscsi_cls_conn *cls_conn)
+ struct iscsi_session *session = conn->session;
+
+ if (!session) {
+- iscsi_conn_printk(KERN_ERR, conn,
+- "can't start unbound connection\n");
++ 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) {
+- iscsi_conn_printk(KERN_INFO, conn, "invalid burst lengths: "
+- "first_burst %d max_burst %d\n",
+- 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;
+ }
+
+- if (conn->ping_timeout && !conn->recv_timeout) {
+- iscsi_conn_printk(KERN_ERR, conn, "invalid recv timeout of "
+- "zero. Using 5 seconds\n.");
+- conn->recv_timeout = 5;
+- }
+-
+- if (conn->recv_timeout && !conn->ping_timeout) {
+- iscsi_conn_printk(KERN_ERR, conn, "invalid ping timeout of "
+- "zero. Using 5 seconds.\n");
+- conn->ping_timeout = 5;
+- }
+-
+ spin_lock_bh(&session->lock);
+ conn->c_stage = ISCSI_CONN_STARTED;
+ session->state = ISCSI_STATE_LOGGED_IN;
+- session->queued_cmdsn = session->cmdsn;
+-
+- conn->last_recv = jiffies;
+- conn->last_ping = jiffies;
+- if (conn->recv_timeout && conn->ping_timeout)
+- mod_timer(&conn->transport_timer,
+- jiffies + (conn->recv_timeout * HZ));
+
+ switch(conn->stop_stage) {
+ case STOP_CONN_RECOVER:
+@@ -2127,11 +1646,13 @@ int iscsi_conn_start(struct iscsi_cls_conn *cls_conn)
+ * commands after successful recovery
+ */
+ conn->stop_stage = 0;
+- conn->tmf_state = TMF_INITIAL;
++ conn->tmabort_state = TMABORT_INITIAL;
+ session->age++;
+- if (session->age == 16)
+- session->age = 0;
+- break;
++ 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;
+@@ -2140,8 +1661,6 @@ int iscsi_conn_start(struct iscsi_cls_conn *cls_conn)
+ }
+ spin_unlock_bh(&session->lock);
+
+- iscsi_unblock_session(session_to_cls(session));
+- wake_up(&conn->ehwait);
+ return 0;
+ }
+ EXPORT_SYMBOL_GPL(iscsi_conn_start);
+@@ -2152,43 +1671,59 @@ flush_control_queues(struct iscsi_session *session, struct iscsi_conn *conn)
+ struct iscsi_mgmt_task *mtask, *tmp;
+
+ /* handle pending */
+- list_for_each_entry_safe(mtask, tmp, &conn->mgmtqueue, running) {
++ 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);
+- iscsi_free_mgmt_task(conn, mtask);
++ __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);
+- iscsi_free_mgmt_task(conn, mtask);
++ 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;
+
+- del_timer_sync(&conn->transport_timer);
+-
+- mutex_lock(&session->eh_mutex);
+ spin_lock_bh(&session->lock);
+ if (conn->stop_stage == STOP_CONN_TERM) {
+ spin_unlock_bh(&session->lock);
+- mutex_unlock(&session->eh_mutex);
+- return;
+- }
+-
+- /*
+- * The LLD either freed/unset the lock on us, or userspace called
+- * stop but did not create a proper connection (connection was never
+- * bound or it was unbound then stop was called).
+- */
+- if (!conn->recv_lock) {
+- spin_unlock_bh(&session->lock);
+- mutex_unlock(&session->eh_mutex);
+ return;
+ }
+
+@@ -2205,14 +1740,14 @@ static void iscsi_start_session_recovery(struct iscsi_session *session,
+ 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);
+
+- iscsi_suspend_tx(conn);
+-
+ 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
+@@ -2233,11 +1768,11 @@ static void iscsi_start_session_recovery(struct iscsi_session *session,
+ * flush queues.
+ */
+ spin_lock_bh(&session->lock);
+- fail_all_commands(conn, -1,
+- STOP_CONN_RECOVER ? DID_BUS_BUSY : DID_ERROR);
++ fail_all_commands(conn);
+ flush_control_queues(session, conn);
+ spin_unlock_bh(&session->lock);
+- mutex_unlock(&session->eh_mutex);
++
++ mutex_unlock(&conn->xmitmutex);
+ }
+
+ void iscsi_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
+@@ -2251,8 +1786,7 @@ void iscsi_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
+ iscsi_start_session_recovery(session, conn, flag);
+ break;
+ default:
+- iscsi_conn_printk(KERN_ERR, conn,
+- "invalid stop flag %d\n", flag);
++ printk(KERN_ERR "iscsi: invalid stop flag %d\n", flag);
+ }
+ }
+ EXPORT_SYMBOL_GPL(iscsi_conn_stop);
+@@ -2286,21 +1820,6 @@ int iscsi_set_param(struct iscsi_cls_conn *cls_conn,
+ uint32_t value;
+
+ switch(param) {
+- case ISCSI_PARAM_FAST_ABORT:
+- sscanf(buf, "%d", &session->fast_abort);
+- break;
+- case ISCSI_PARAM_ABORT_TMO:
+- sscanf(buf, "%d", &session->abort_timeout);
+- break;
+- case ISCSI_PARAM_LU_RESET_TMO:
+- sscanf(buf, "%d", &session->lu_reset_timeout);
+- break;
+- case ISCSI_PARAM_PING_TMO:
+- sscanf(buf, "%d", &conn->ping_timeout);
+- break;
+- case ISCSI_PARAM_RECV_TMO:
+- sscanf(buf, "%d", &conn->recv_timeout);
+- break;
+ case ISCSI_PARAM_MAX_RECV_DLENGTH:
+ sscanf(buf, "%d", &conn->max_recv_dlength);
+ break;
+@@ -2348,30 +1867,6 @@ int iscsi_set_param(struct iscsi_cls_conn *cls_conn,
+ case ISCSI_PARAM_EXP_STATSN:
+ sscanf(buf, "%u", &conn->exp_statsn);
+ break;
+- case ISCSI_PARAM_USERNAME:
+- kfree(session->username);
+- session->username = kstrdup(buf, GFP_KERNEL);
+- if (!session->username)
+- return -ENOMEM;
+- break;
+- case ISCSI_PARAM_USERNAME_IN:
+- kfree(session->username_in);
+- session->username_in = kstrdup(buf, GFP_KERNEL);
+- if (!session->username_in)
+- return -ENOMEM;
+- break;
+- case ISCSI_PARAM_PASSWORD:
+- kfree(session->password);
+- session->password = kstrdup(buf, GFP_KERNEL);
+- if (!session->password)
+- return -ENOMEM;
+- break;
+- case ISCSI_PARAM_PASSWORD_IN:
+- kfree(session->password_in);
+- session->password_in = kstrdup(buf, GFP_KERNEL);
+- if (!session->password_in)
+- return -ENOMEM;
+- break;
+ case ISCSI_PARAM_TARGET_NAME:
+ /* this should not change between logins */
+ if (session->targetname)
+@@ -2415,15 +1910,6 @@ int iscsi_session_get_param(struct iscsi_cls_session *cls_session,
+ int len;
+
+ switch(param) {
+- case ISCSI_PARAM_FAST_ABORT:
+- len = sprintf(buf, "%d\n", session->fast_abort);
+- break;
+- case ISCSI_PARAM_ABORT_TMO:
+- len = sprintf(buf, "%d\n", session->abort_timeout);
+- break;
+- case ISCSI_PARAM_LU_RESET_TMO:
+- len = sprintf(buf, "%d\n", session->lu_reset_timeout);
+- break;
+ case ISCSI_PARAM_INITIAL_R2T_EN:
+ len = sprintf(buf, "%d\n", session->initial_r2t_en);
+ break;
+@@ -2454,18 +1940,6 @@ int iscsi_session_get_param(struct iscsi_cls_session *cls_session,
+ case ISCSI_PARAM_TPGT:
+ len = sprintf(buf, "%d\n", session->tpgt);
+ break;
+- case ISCSI_PARAM_USERNAME:
+- len = sprintf(buf, "%s\n", session->username);
+- break;
+- case ISCSI_PARAM_USERNAME_IN:
+- len = sprintf(buf, "%s\n", session->username_in);
+- break;
+- case ISCSI_PARAM_PASSWORD:
+- len = sprintf(buf, "%s\n", session->password);
+- break;
+- case ISCSI_PARAM_PASSWORD_IN:
+- len = sprintf(buf, "%s\n", session->password_in);
+- break;
+ default:
+ return -ENOSYS;
+ }
+@@ -2481,12 +1955,6 @@ int iscsi_conn_get_param(struct iscsi_cls_conn *cls_conn,
+ int len;
+
+ switch(param) {
+- case ISCSI_PARAM_PING_TMO:
+- len = sprintf(buf, "%u\n", conn->ping_timeout);
+- break;
+- case ISCSI_PARAM_RECV_TMO:
+- len = sprintf(buf, "%u\n", conn->recv_timeout);
+- break;
+ case ISCSI_PARAM_MAX_RECV_DLENGTH:
+ len = sprintf(buf, "%u\n", conn->max_recv_dlength);
+ break;
+@@ -2522,66 +1990,6 @@ int iscsi_conn_get_param(struct iscsi_cls_conn *cls_conn,
+ }
+ EXPORT_SYMBOL_GPL(iscsi_conn_get_param);
+
+-int iscsi_host_get_param(struct Scsi_Host *shost, enum iscsi_host_param param,
+- char *buf)
+-{
+- struct iscsi_session *session = iscsi_hostdata(shost->hostdata);
+- int len;
+-
+- switch (param) {
+- case ISCSI_HOST_PARAM_NETDEV_NAME:
+- if (!session->netdev)
+- len = sprintf(buf, "%s\n", "default");
+- else
+- len = sprintf(buf, "%s\n", session->netdev);
+- break;
+- case ISCSI_HOST_PARAM_HWADDRESS:
+- if (!session->hwaddress)
+- len = sprintf(buf, "%s\n", "default");
+- else
+- len = sprintf(buf, "%s\n", session->hwaddress);
+- break;
+- case ISCSI_HOST_PARAM_INITIATOR_NAME:
+- if (!session->initiatorname)
+- len = sprintf(buf, "%s\n", "unknown");
+- else
+- len = sprintf(buf, "%s\n", session->initiatorname);
+- break;
+-
+- default:
+- return -ENOSYS;
+- }
+-
+- return len;
+-}
+-EXPORT_SYMBOL_GPL(iscsi_host_get_param);
+-
+-int iscsi_host_set_param(struct Scsi_Host *shost, enum iscsi_host_param param,
+- char *buf, int buflen)
+-{
+- struct iscsi_session *session = iscsi_hostdata(shost->hostdata);
+-
+- switch (param) {
+- case ISCSI_HOST_PARAM_NETDEV_NAME:
+- if (!session->netdev)
+- session->netdev = kstrdup(buf, GFP_KERNEL);
+- break;
+- case ISCSI_HOST_PARAM_HWADDRESS:
+- if (!session->hwaddress)
+- session->hwaddress = kstrdup(buf, GFP_KERNEL);
+- break;
+- case ISCSI_HOST_PARAM_INITIATOR_NAME:
+- if (!session->initiatorname)
+- session->initiatorname = kstrdup(buf, GFP_KERNEL);
+- break;
+- default:
+- return -ENOSYS;
+- }
+-
+- return 0;
+-}
+-EXPORT_SYMBOL_GPL(iscsi_host_set_param);
+-
+ MODULE_AUTHOR("Mike Christie");
+ MODULE_DESCRIPTION("iSCSI library functions");
+ MODULE_LICENSE("GPL");
+diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c
+index 65d1737..caf1836 100644
+--- a/drivers/scsi/scsi_transport_iscsi.c
++++ b/drivers/scsi/scsi_transport_iscsi.c
+@@ -30,27 +30,26 @@
+ #include <scsi/scsi_transport_iscsi.h>
+ #include <scsi/iscsi_if.h>
+
+-#define ISCSI_SESSION_ATTRS 19
+-#define ISCSI_CONN_ATTRS 13
+-#define ISCSI_HOST_ATTRS 4
+-#define ISCSI_TRANSPORT_VERSION "2.0-869"
++#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 device dev;
++ struct class_device cdev;
+
+- struct device_attribute *host_attrs[ISCSI_HOST_ATTRS + 1];
++ struct class_device_attribute *host_attrs[ISCSI_HOST_ATTRS + 1];
+ struct transport_container conn_cont;
+- struct device_attribute *conn_attrs[ISCSI_CONN_ATTRS + 1];
++ struct class_device_attribute *conn_attrs[ISCSI_CONN_ATTRS + 1];
+ struct transport_container session_cont;
+- struct device_attribute *session_attrs[ISCSI_SESSION_ATTRS + 1];
++ struct class_device_attribute *session_attrs[ISCSI_SESSION_ATTRS + 1];
+ };
+
+ static atomic_t iscsi_session_nr; /* sysfs session id for next new session */
+-static struct workqueue_struct *iscsi_eh_timer_workq;
+
+ /*
+ * list of registered transports and lock that must
+@@ -63,12 +62,12 @@ static DEFINE_SPINLOCK(iscsi_transport_lock);
+ #define to_iscsi_internal(tmpl) \
+ container_of(tmpl, struct iscsi_internal, t)
+
+-#define dev_to_iscsi_internal(_dev) \
+- container_of(_dev, struct iscsi_internal, dev)
++#define cdev_to_iscsi_internal(_cdev) \
++ container_of(_cdev, struct iscsi_internal, cdev)
+
+-static void iscsi_transport_release(struct device *dev)
++static void iscsi_transport_release(struct class_device *cdev)
+ {
+- struct iscsi_internal *priv = dev_to_iscsi_internal(dev);
++ struct iscsi_internal *priv = cdev_to_iscsi_internal(cdev);
+ kfree(priv);
+ }
+
+@@ -78,27 +77,25 @@ static void iscsi_transport_release(struct device *dev)
+ */
+ static struct class iscsi_transport_class = {
+ .name = "iscsi_transport",
+- .dev_release = iscsi_transport_release,
++ .release = iscsi_transport_release,
+ };
+
+ static ssize_t
+-show_transport_handle(struct device *dev, struct device_attribute *attr,
+- char *buf)
++show_transport_handle(struct class_device *cdev, char *buf)
+ {
+- struct iscsi_internal *priv = dev_to_iscsi_internal(dev);
++ struct iscsi_internal *priv = cdev_to_iscsi_internal(cdev);
+ return sprintf(buf, "%llu\n", (unsigned long long)iscsi_handle(priv->iscsi_transport));
+ }
+-static DEVICE_ATTR(handle, S_IRUGO, show_transport_handle, NULL);
++static CLASS_DEVICE_ATTR(handle, S_IRUGO, show_transport_handle, NULL);
+
+ #define show_transport_attr(name, format) \
+ static ssize_t \
+-show_transport_##name(struct device *dev, \
+- struct device_attribute *attr,char *buf) \
++show_transport_##name(struct class_device *cdev, char *buf) \
+ { \
+- struct iscsi_internal *priv = dev_to_iscsi_internal(dev); \
++ struct iscsi_internal *priv = cdev_to_iscsi_internal(cdev); \
+ return sprintf(buf, format"\n", priv->iscsi_transport->name); \
+ } \
+-static DEVICE_ATTR(name, S_IRUGO, show_transport_##name, NULL);
++static CLASS_DEVICE_ATTR(name, S_IRUGO, show_transport_##name, NULL);
+
+ show_transport_attr(caps, "0x%x");
+ show_transport_attr(max_lun, "%d");
+@@ -106,11 +103,11 @@ show_transport_attr(max_conn, "%d");
+ show_transport_attr(max_cmd_len, "%d");
+
+ static struct attribute *iscsi_transport_attrs[] = {
+- &dev_attr_handle.attr,
+- &dev_attr_caps.attr,
+- &dev_attr_max_lun.attr,
+- &dev_attr_max_conn.attr,
+- &dev_attr_max_cmd_len.attr,
++ &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,
+ };
+
+@@ -118,10 +115,8 @@ static struct attribute_group iscsi_transport_group = {
+ .attrs = iscsi_transport_attrs,
+ };
+
+-
+-
+ static int iscsi_setup_host(struct transport_container *tc, struct device *dev,
+- struct device *cdev)
++ struct class_device *cdev)
+ {
+ struct Scsi_Host *shost = dev_to_shost(dev);
+ struct iscsi_host *ihost = shost->shost_data;
+@@ -129,31 +124,13 @@ static int iscsi_setup_host(struct transport_container *tc, struct device *dev,
+ memset(ihost, 0, sizeof(*ihost));
+ INIT_LIST_HEAD(&ihost->sessions);
+ mutex_init(&ihost->mutex);
+- atomic_set(&ihost->nr_scans, 0);
+-
+- snprintf(ihost->scan_workq_name, KOBJ_NAME_LEN, "iscsi_scan_%d",
+- shost->host_no);
+- ihost->scan_workq = create_singlethread_workqueue(
+- ihost->scan_workq_name);
+- if (!ihost->scan_workq)
+- return -ENOMEM;
+- return 0;
+-}
+-
+-static int iscsi_remove_host(struct transport_container *tc, struct device *dev,
+- struct device *cdev)
+-{
+- struct Scsi_Host *shost = dev_to_shost(dev);
+- struct iscsi_host *ihost = shost->shost_data;
+-
+- destroy_workqueue(ihost->scan_workq);
+ return 0;
+ }
+
+ static DECLARE_TRANSPORT_CLASS(iscsi_host_class,
+ "iscsi_host",
+ iscsi_setup_host,
+- iscsi_remove_host,
++ NULL,
+ NULL);
+
+ static DECLARE_TRANSPORT_CLASS(iscsi_session_class,
+@@ -224,54 +201,6 @@ static struct iscsi_cls_conn *iscsi_conn_lookup(uint32_t sid, uint32_t cid)
+ * The following functions can be used by LLDs that allocate
+ * their own scsi_hosts or by software iscsi LLDs
+ */
+-static struct {
+- int value;
+- char *name;
+-} iscsi_session_state_names[] = {
+- { ISCSI_SESSION_LOGGED_IN, "LOGGED_IN" },
+- { ISCSI_SESSION_FAILED, "FAILED" },
+- { ISCSI_SESSION_FREE, "FREE" },
+-};
+-
+-static const char *iscsi_session_state_name(int state)
+-{
+- int i;
+- char *name = NULL;
+-
+- for (i = 0; i < ARRAY_SIZE(iscsi_session_state_names); i++) {
+- if (iscsi_session_state_names[i].value == state) {
+- name = iscsi_session_state_names[i].name;
+- break;
+- }
+- }
+- return name;
+-}
+-
+-int iscsi_session_chkready(struct iscsi_cls_session *session)
+-{
+- unsigned long flags;
+- int err;
+-
+- spin_lock_irqsave(&session->lock, flags);
+- switch (session->state) {
+- case ISCSI_SESSION_LOGGED_IN:
+- err = 0;
+- break;
+- case ISCSI_SESSION_FAILED:
+- err = DID_IMM_RETRY << 16;
+- break;
+- case ISCSI_SESSION_FREE:
+- err = DID_NO_CONNECT << 16;
+- break;
+- default:
+- err = DID_NO_CONNECT << 16;
+- break;
+- }
+- spin_unlock_irqrestore(&session->lock, flags);
+- return err;
+-}
+-EXPORT_SYMBOL_GPL(iscsi_session_chkready);
+-
+ static void iscsi_session_release(struct device *dev)
+ {
+ struct iscsi_cls_session *session = iscsi_dev_to_session(dev);
+@@ -287,25 +216,6 @@ static int iscsi_is_session_dev(const struct device *dev)
+ return dev->release == iscsi_session_release;
+ }
+
+-/**
+- * iscsi_scan_finished - helper to report when running scans are done
+- * @shost: scsi host
+- * @time: scan run time
+- *
+- * This function can be used by drives like qla4xxx to report to the scsi
+- * layer when the scans it kicked off at module load time are done.
+- */
+-int iscsi_scan_finished(struct Scsi_Host *shost, unsigned long time)
+-{
+- struct iscsi_host *ihost = shost->shost_data;
+- /*
+- * qla4xxx will have kicked off some session unblocks before calling
+- * scsi_scan_host, so just wait for them to complete.
+- */
+- return !atomic_read(&ihost->nr_scans);
+-}
+-EXPORT_SYMBOL_GPL(iscsi_scan_finished);
+-
+ static int iscsi_user_scan(struct Scsi_Host *shost, uint channel,
+ uint id, uint lun)
+ {
+@@ -324,50 +234,14 @@ static int iscsi_user_scan(struct Scsi_Host *shost, uint channel,
+ return 0;
+ }
+
+-static void iscsi_scan_session(struct work_struct *work)
+-{
+- struct iscsi_cls_session *session =
+- container_of(work, struct iscsi_cls_session, scan_work);
+- struct Scsi_Host *shost = iscsi_session_to_shost(session);
+- struct iscsi_host *ihost = shost->shost_data;
+- unsigned long flags;
+-
+- spin_lock_irqsave(&session->lock, flags);
+- if (session->state != ISCSI_SESSION_LOGGED_IN) {
+- spin_unlock_irqrestore(&session->lock, flags);
+- goto done;
+- }
+- spin_unlock_irqrestore(&session->lock, flags);
+-
+- scsi_scan_target(&session->dev, 0, session->target_id,
+- SCAN_WILD_CARD, 1);
+-done:
+- atomic_dec(&ihost->nr_scans);
+-}
+-
+ static void session_recovery_timedout(struct work_struct *work)
+ {
+ struct iscsi_cls_session *session =
+ container_of(work, struct iscsi_cls_session,
+ recovery_work.work);
+- unsigned long flags;
+-
+- iscsi_cls_session_printk(KERN_INFO, session,
+- "session recovery timed out after %d secs\n",
+- session->recovery_tmo);
+
+- spin_lock_irqsave(&session->lock, flags);
+- switch (session->state) {
+- case ISCSI_SESSION_FAILED:
+- session->state = ISCSI_SESSION_FREE;
+- break;
+- case ISCSI_SESSION_LOGGED_IN:
+- case ISCSI_SESSION_FREE:
+- /* we raced with the unblock's flush */
+- spin_unlock_irqrestore(&session->lock, flags);
+- return;
+- }
+- spin_unlock_irqrestore(&session->lock, flags);
++ 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);
+@@ -375,103 +249,22 @@ static void session_recovery_timedout(struct work_struct *work)
+ scsi_target_unblock(&session->dev);
+ }
+
+-static void __iscsi_unblock_session(struct work_struct *work)
+-{
+- struct iscsi_cls_session *session =
+- container_of(work, struct iscsi_cls_session,
+- unblock_work);
+- struct Scsi_Host *shost = iscsi_session_to_shost(session);
+- struct iscsi_host *ihost = shost->shost_data;
+- unsigned long flags;
+-
+- /*
+- * The recovery and unblock work get run from the same workqueue,
+- * so try to cancel it if it was going to run after this unblock.
+- */
+- cancel_delayed_work(&session->recovery_work);
+- spin_lock_irqsave(&session->lock, flags);
+- session->state = ISCSI_SESSION_LOGGED_IN;
+- spin_unlock_irqrestore(&session->lock, flags);
+- /* start IO */
+- scsi_target_unblock(&session->dev);
+- /*
+- * Only do kernel scanning if the driver is properly hooked into
+- * the async scanning code (drivers like iscsi_tcp do login and
+- * scanning from userspace).
+- */
+- if (shost->hostt->scan_finished) {
+- if (queue_work(ihost->scan_workq, &session->scan_work))
+- atomic_inc(&ihost->nr_scans);
+- }
+-}
+-
+-/**
+- * iscsi_unblock_session - set a session as logged in and start IO.
+- * @session: iscsi session
+- *
+- * Mark a session as ready to accept IO.
+- */
+ void iscsi_unblock_session(struct iscsi_cls_session *session)
+ {
+- queue_work(iscsi_eh_timer_workq, &session->unblock_work);
+- /*
+- * make sure all the events have completed before tell the driver
+- * it is safe
+- */
+- flush_workqueue(iscsi_eh_timer_workq);
++ if (!cancel_delayed_work(&session->recovery_work))
++ flush_scheduled_work();
++ scsi_target_unblock(&session->dev);
+ }
+ EXPORT_SYMBOL_GPL(iscsi_unblock_session);
+
+-static void __iscsi_block_session(struct work_struct *work)
+-{
+- struct iscsi_cls_session *session =
+- container_of(work, struct iscsi_cls_session,
+- block_work);
+- unsigned long flags;
+-
+- spin_lock_irqsave(&session->lock, flags);
+- session->state = ISCSI_SESSION_FAILED;
+- spin_unlock_irqrestore(&session->lock, flags);
+- scsi_target_block(&session->dev);
+- queue_delayed_work(iscsi_eh_timer_workq, &session->recovery_work,
+- session->recovery_tmo * HZ);
+-}
+-
+ void iscsi_block_session(struct iscsi_cls_session *session)
+ {
+- queue_work(iscsi_eh_timer_workq, &session->block_work);
++ scsi_target_block(&session->dev);
++ schedule_delayed_work(&session->recovery_work,
++ session->recovery_tmo * HZ);
+ }
+ EXPORT_SYMBOL_GPL(iscsi_block_session);
+
+-static void __iscsi_unbind_session(struct work_struct *work)
+-{
+- struct iscsi_cls_session *session =
+- container_of(work, struct iscsi_cls_session,
+- unbind_work);
+- struct Scsi_Host *shost = iscsi_session_to_shost(session);
+- struct iscsi_host *ihost = shost->shost_data;
+-
+- /* Prevent new scans and make sure scanning is not in progress */
+- mutex_lock(&ihost->mutex);
+- if (list_empty(&session->host_list)) {
+- mutex_unlock(&ihost->mutex);
+- return;
+- }
+- list_del_init(&session->host_list);
+- mutex_unlock(&ihost->mutex);
+-
+- scsi_remove_target(&session->dev);
+- iscsi_session_event(session, ISCSI_KEVENT_UNBIND_SESSION);
+-}
+-
+-static int iscsi_unbind_session(struct iscsi_cls_session *session)
+-{
+- struct Scsi_Host *shost = iscsi_session_to_shost(session);
+- struct iscsi_host *ihost = shost->shost_data;
+-
+- return queue_work(ihost->scan_workq, &session->unbind_work);
+-}
+-
+ struct iscsi_cls_session *
+ iscsi_alloc_session(struct Scsi_Host *shost,
+ struct iscsi_transport *transport)
+@@ -485,15 +278,9 @@ iscsi_alloc_session(struct Scsi_Host *shost,
+
+ session->transport = transport;
+ session->recovery_tmo = 120;
+- session->state = ISCSI_SESSION_FREE;
+ INIT_DELAYED_WORK(&session->recovery_work, session_recovery_timedout);
+ INIT_LIST_HEAD(&session->host_list);
+ INIT_LIST_HEAD(&session->sess_list);
+- INIT_WORK(&session->unblock_work, __iscsi_unblock_session);
+- INIT_WORK(&session->block_work, __iscsi_block_session);
+- INIT_WORK(&session->unbind_work, __iscsi_unbind_session);
+- INIT_WORK(&session->scan_work, iscsi_scan_session);
+- spin_lock_init(&session->lock);
+
+ /* this is released in the dev's release function */
+ scsi_host_get(shost);
+@@ -510,7 +297,6 @@ 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;
+- unsigned long flags;
+ int err;
+
+ ihost = shost->shost_data;
+@@ -521,21 +307,15 @@ int iscsi_add_session(struct iscsi_cls_session *session, unsigned int target_id)
+ session->sid);
+ err = device_add(&session->dev);
+ if (err) {
+- iscsi_cls_session_printk(KERN_ERR, session,
+- "could not register session's dev\n");
++ dev_printk(KERN_ERR, &session->dev, "iscsi: could not "
++ "register session's dev\n");
+ goto release_host;
+ }
+ transport_register_device(&session->dev);
+
+- spin_lock_irqsave(&sesslock, flags);
+- list_add(&session->sess_list, &sesslist);
+- spin_unlock_irqrestore(&sesslock, flags);
+-
+ mutex_lock(&ihost->mutex);
+ list_add(&session->host_list, &ihost->sessions);
+ mutex_unlock(&ihost->mutex);
+-
+- iscsi_session_event(session, ISCSI_KEVENT_CREATE_SESSION);
+ return 0;
+
+ release_host:
+@@ -548,10 +328,9 @@ EXPORT_SYMBOL_GPL(iscsi_add_session);
+ * iscsi_create_session - create iscsi class session
+ * @shost: scsi host
+ * @transport: iscsi transport
+- * @target_id: which target
+ *
+ * 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,
+@@ -571,65 +350,19 @@ iscsi_create_session(struct Scsi_Host *shost,
+ }
+ EXPORT_SYMBOL_GPL(iscsi_create_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;
+-}
+-
+-static int iscsi_iter_destroy_conn_fn(struct device *dev, void *data)
+-{
+- if (!iscsi_is_conn_dev(dev))
+- return 0;
+- return iscsi_destroy_conn(iscsi_dev_to_conn(dev));
+-}
+-
+ 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;
+- unsigned long flags;
+- int err;
+-
+- spin_lock_irqsave(&sesslock, flags);
+- list_del(&session->sess_list);
+- spin_unlock_irqrestore(&sesslock, flags);
+
+- /* make sure there are no blocks/unblocks queued */
+- flush_workqueue(iscsi_eh_timer_workq);
+- /* make sure the timedout callout is not running */
+ if (!cancel_delayed_work(&session->recovery_work))
+- flush_workqueue(iscsi_eh_timer_workq);
+- /*
+- * If we are blocked let commands flow again. The lld or iscsi
+- * layer should set up the queuecommand to fail commands.
+- * We assume that LLD will not be calling block/unblock while
+- * removing the session.
+- */
+- spin_lock_irqsave(&session->lock, flags);
+- session->state = ISCSI_SESSION_FREE;
+- spin_unlock_irqrestore(&session->lock, flags);
++ flush_scheduled_work();
+
+- scsi_target_unblock(&session->dev);
+- /* flush running scans then delete devices */
+- flush_workqueue(ihost->scan_workq);
+- __iscsi_unbind_session(&session->unbind_work);
++ mutex_lock(&ihost->mutex);
++ list_del(&session->host_list);
++ mutex_unlock(&ihost->mutex);
+
+- /* hw iscsi may not have removed all connections from session */
+- err = device_for_each_child(&session->dev, NULL,
+- iscsi_iter_destroy_conn_fn);
+- if (err)
+- iscsi_cls_session_printk(KERN_ERR, session,
+- "Could not delete all connections "
+- "for session. Error %d.\n", err);
++ scsi_remove_target(&session->dev);
+
+ transport_unregister_device(&session->dev);
+ device_del(&session->dev);
+@@ -638,9 +371,9 @@ EXPORT_SYMBOL_GPL(iscsi_remove_session);
+
+ void iscsi_free_session(struct iscsi_cls_session *session)
+ {
+- iscsi_session_event(session, ISCSI_KEVENT_DESTROY_SESSION);
+ put_device(&session->dev);
+ }
++
+ EXPORT_SYMBOL_GPL(iscsi_free_session);
+
+ /**
+@@ -649,7 +382,7 @@ EXPORT_SYMBOL_GPL(iscsi_free_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);
+@@ -658,6 +391,20 @@ int iscsi_destroy_session(struct iscsi_cls_session *session)
+ }
+ 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
+@@ -671,13 +418,12 @@ EXPORT_SYMBOL_GPL(iscsi_destroy_session);
+ * 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;
+- unsigned long flags;
+ int err;
+
+ conn = kzalloc(sizeof(*conn) + transport->conndata_size, GFP_KERNEL);
+@@ -701,16 +447,11 @@ iscsi_create_conn(struct iscsi_cls_session *session, uint32_t cid)
+ conn->dev.release = iscsi_conn_release;
+ err = device_register(&conn->dev);
+ if (err) {
+- iscsi_cls_session_printk(KERN_ERR, session, "could not "
+- "register connection's dev\n");
++ dev_printk(KERN_ERR, &conn->dev, "iscsi: could not register "
++ "connection's dev\n");
+ goto release_parent_ref;
+ }
+ transport_register_device(&conn->dev);
+-
+- spin_lock_irqsave(&connlock, flags);
+- list_add(&conn->conn_list, &connlist);
+- conn->active = 1;
+- spin_unlock_irqrestore(&connlock, flags);
+ return conn;
+
+ release_parent_ref:
+@@ -724,23 +465,17 @@ EXPORT_SYMBOL_GPL(iscsi_create_conn);
+
+ /**
+ * iscsi_destroy_conn - destroy iscsi class connection
+- * @conn: iscsi cls session
++ * @session: iscsi cls session
+ *
+ * This can be called from a LLD or iscsi_transport.
+- */
++ **/
+ int iscsi_destroy_conn(struct iscsi_cls_conn *conn)
+ {
+- unsigned long flags;
+-
+- spin_lock_irqsave(&connlock, flags);
+- conn->active = 0;
+- list_del(&conn->conn_list);
+- spin_unlock_irqrestore(&connlock, flags);
+-
+ transport_unregister_device(&conn->dev);
+ device_unregister(&conn->dev);
+ return 0;
+ }
++
+ EXPORT_SYMBOL_GPL(iscsi_destroy_conn);
+
+ /*
+@@ -809,8 +544,8 @@ int iscsi_recv_pdu(struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr,
+ skb = alloc_skb(len, GFP_ATOMIC);
+ if (!skb) {
+ iscsi_conn_error(conn, ISCSI_ERR_CONN_FAILED);
+- iscsi_cls_conn_printk(KERN_ERR, conn, "can not deliver "
+- "control PDU: OOM\n");
++ dev_printk(KERN_ERR, &conn->dev, "iscsi: can not deliver "
++ "control PDU: OOM\n");
+ return -ENOMEM;
+ }
+
+@@ -843,8 +578,8 @@ void iscsi_conn_error(struct iscsi_cls_conn *conn, enum iscsi_err error)
+
+ skb = alloc_skb(len, GFP_ATOMIC);
+ if (!skb) {
+- iscsi_cls_conn_printk(KERN_ERR, conn, "gracefully ignored "
+- "conn error (%d)\n", error);
++ dev_printk(KERN_ERR, &conn->dev, "iscsi: gracefully ignored "
++ "conn error (%d)\n", error);
+ return;
+ }
+
+@@ -858,8 +593,8 @@ void iscsi_conn_error(struct iscsi_cls_conn *conn, enum iscsi_err error)
+
+ iscsi_broadcast_skb(skb, GFP_ATOMIC);
+
+- iscsi_cls_conn_printk(KERN_INFO, conn, "detected conn error (%d)\n",
+- error);
++ dev_printk(KERN_INFO, &conn->dev, "iscsi: detected conn error (%d)\n",
++ error);
+ }
+ EXPORT_SYMBOL_GPL(iscsi_conn_error);
+
+@@ -874,10 +609,12 @@ iscsi_if_send_reply(int pid, int seq, int type, int done, int multi,
+ 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;
+- }
++ /*
++ * FIXME:
++ * user is supposed to react on iferror == -ENOMEM;
++ * see iscsi_if_rx().
++ */
++ BUG_ON(!skb);
+
+ nlh = __nlmsg_put(skb, pid, seq, t, (len - sizeof(*nlh)), 0);
+ nlh->nlmsg_flags = flags;
+@@ -914,8 +651,8 @@ iscsi_if_get_stats(struct iscsi_transport *transport, struct nlmsghdr *nlh)
+
+ skbstat = alloc_skb(len, GFP_ATOMIC);
+ if (!skbstat) {
+- iscsi_cls_conn_printk(KERN_ERR, conn, "can not "
+- "deliver stats: OOM\n");
++ dev_printk(KERN_ERR, &conn->dev, "iscsi: can not "
++ "deliver stats: OOM\n");
+ return -ENOMEM;
+ }
+
+@@ -950,87 +687,144 @@ iscsi_if_get_stats(struct iscsi_transport *transport, struct nlmsghdr *nlh)
+ }
+
+ /**
+- * iscsi_session_event - send session destr. completion event
+- * @session: iscsi class session
+- * @event: type of event
+- */
+-int iscsi_session_event(struct iscsi_cls_session *session,
+- enum iscsi_uevent_e event)
++ * 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(session->transport);
++ 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) {
+- iscsi_cls_session_printk(KERN_ERR, session,
+- "Cannot notify userspace of session "
+- "event %u\n", event);
++ 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(session->transport);
++ 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;
+
+- ev->type = event;
+- switch (event) {
+- case ISCSI_KEVENT_DESTROY_SESSION:
+- ev->r.d_session.host_no = shost->host_no;
+- ev->r.d_session.sid = session->sid;
+- break;
+- case ISCSI_KEVENT_CREATE_SESSION:
+- ev->r.c_session_ret.host_no = shost->host_no;
+- ev->r.c_session_ret.sid = session->sid;
+- break;
+- case ISCSI_KEVENT_UNBIND_SESSION:
+- ev->r.unbind_session.host_no = shost->host_no;
+- ev->r.unbind_session.sid = session->sid;
+- break;
+- default:
+- iscsi_cls_session_printk(KERN_ERR, session, "Invalid event "
+- "%u.\n", event);
+- kfree_skb(skb);
++ /*
++ * 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)
+- iscsi_cls_session_printk(KERN_ERR, session,
+- "Cannot notify userspace of session "
+- "event %u. Check iscsi daemon\n",
+- event);
++ 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_session_event);
++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.cmds_max,
+- ev->u.c_session.queue_depth,
+ 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;
+@@ -1041,34 +835,47 @@ 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",
++ 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) {
+- iscsi_cls_session_printk(KERN_ERR, session,
+- "couldn't create a new connection.");
++ 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);
+@@ -1140,50 +947,15 @@ static int
+ iscsi_tgt_dscvr(struct iscsi_transport *transport,
+ struct iscsi_uevent *ev)
+ {
+- struct Scsi_Host *shost;
+ struct sockaddr *dst_addr;
+- int err;
+
+ if (!transport->tgt_dscvr)
+ return -EINVAL;
+
+- shost = scsi_host_lookup(ev->u.tgt_dscvr.host_no);
+- if (IS_ERR(shost)) {
+- printk(KERN_ERR "target discovery could not find host no %u\n",
+- ev->u.tgt_dscvr.host_no);
+- return -ENODEV;
+- }
+-
+-
+ dst_addr = (struct sockaddr *)((char*)ev + sizeof(*ev));
+- err = transport->tgt_dscvr(shost, ev->u.tgt_dscvr.type,
+- ev->u.tgt_dscvr.enable, dst_addr);
+- scsi_host_put(shost);
+- return err;
+-}
+-
+-static int
+-iscsi_set_host_param(struct iscsi_transport *transport,
+- struct iscsi_uevent *ev)
+-{
+- char *data = (char*)ev + sizeof(*ev);
+- struct Scsi_Host *shost;
+- int err;
+-
+- if (!transport->set_host_param)
+- return -ENOSYS;
+-
+- shost = scsi_host_lookup(ev->u.set_host_param.host_no);
+- if (IS_ERR(shost)) {
+- printk(KERN_ERR "set_host_param could not find host no %u\n",
+- ev->u.set_host_param.host_no);
+- return -ENODEV;
+- }
+-
+- err = transport->set_host_param(shost, ev->u.set_host_param.param,
+- data, ev->u.set_host_param.len);
+- scsi_host_put(shost);
+- return err;
++ return transport->tgt_dscvr(ev->u.tgt_dscvr.type,
++ ev->u.tgt_dscvr.host_no,
++ ev->u.tgt_dscvr.enable, dst_addr);
+ }
+
+ static int
+@@ -1195,6 +967,7 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
+ 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)
+@@ -1212,16 +985,13 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
+ break;
+ case ISCSI_UEVENT_DESTROY_SESSION:
+ session = iscsi_session_lookup(ev->u.d_session.sid);
+- if (session)
++ 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_UNBIND_SESSION:
+- session = iscsi_session_lookup(ev->u.d_session.sid);
+- if (session)
+- iscsi_unbind_session(session);
+- else
++ } else
+ err = -EINVAL;
+ break;
+ case ISCSI_UEVENT_CREATE_CONN:
+@@ -1279,11 +1049,8 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
+ case ISCSI_UEVENT_TGT_DSCVR:
+ err = iscsi_tgt_dscvr(transport, ev);
+ break;
+- case ISCSI_UEVENT_SET_HOST_PARAM:
+- err = iscsi_set_host_param(transport, ev);
+- break;
+ default:
+- err = -ENOSYS;
++ err = -EINVAL;
+ break;
+ }
+
+@@ -1292,55 +1059,70 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
+ }
+
+ /*
+- * Get message from skb. Each message is processed by iscsi_if_recv_msg.
+- * Malformed skbs with wrong lengths or invalid creds are not processed.
++ * 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 sk_buff *skb)
++iscsi_if_rx(struct sock *sk, int len)
+ {
++ struct sk_buff *skb;
++
+ mutex_lock(&rx_queue_mutex);
+- while (skb->len >= NLMSG_SPACE(0)) {
+- int err;
+- uint32_t rlen;
+- struct nlmsghdr *nlh;
+- struct iscsi_uevent *ev;
+-
+- nlh = nlmsg_hdr(skb);
+- if (nlh->nlmsg_len < sizeof(*nlh) ||
+- skb->len < nlh->nlmsg_len) {
+- break;
++ while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) {
++ if (NETLINK_CREDS(skb)->uid) {
++ skb_pull(skb, skb->len);
++ goto free_skb;
+ }
+
+- ev = NLMSG_DATA(nlh);
+- rlen = NLMSG_ALIGN(nlh->nlmsg_len);
+- if (rlen > skb->len)
+- rlen = skb->len;
++ while (skb->len >= NLMSG_SPACE(0)) {
++ int err;
++ uint32_t rlen;
++ struct nlmsghdr *nlh;
++ struct iscsi_uevent *ev;
+
+- 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)
++ nlh = nlmsg_hdr(skb);
++ if (nlh->nlmsg_len < sizeof(*nlh) ||
++ skb->len < nlh->nlmsg_len) {
+ 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);
++ }
++
++ 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 device_attribute dev_attr_##_prefix##_##_name = \
++struct class_device_attribute class_device_attr_##_prefix##_##_name = \
+ __ATTR(_name,_mode,_show,_store)
+
+ /*
+@@ -1348,10 +1130,9 @@ struct device_attribute dev_attr_##_prefix##_##_name = \
+ */
+ #define iscsi_conn_attr_show(param) \
+ static ssize_t \
+-show_conn_param_##param(struct device *dev, \
+- struct device_attribute *attr, char *buf) \
++show_conn_param_##param(struct class_device *cdev, char *buf) \
+ { \
+- struct iscsi_cls_conn *conn = iscsi_dev_to_conn(dev->parent); \
++ struct iscsi_cls_conn *conn = iscsi_cdev_to_conn(cdev); \
+ struct iscsi_transport *t = conn->transport; \
+ return t->get_conn_param(conn, param, buf); \
+ }
+@@ -1372,66 +1153,43 @@ 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);
+-iscsi_conn_attr(ping_tmo, ISCSI_PARAM_PING_TMO);
+-iscsi_conn_attr(recv_tmo, ISCSI_PARAM_RECV_TMO);
++
++#define iscsi_cdev_to_session(_cdev) \
++ iscsi_dev_to_session(_cdev->dev)
+
+ /*
+ * iSCSI session attrs
+ */
+-#define iscsi_session_attr_show(param, perm) \
++#define iscsi_session_attr_show(param) \
+ static ssize_t \
+-show_session_param_##param(struct device *dev, \
+- struct device_attribute *attr, char *buf) \
++show_session_param_##param(struct class_device *cdev, char *buf) \
+ { \
+- struct iscsi_cls_session *session = \
+- iscsi_dev_to_session(dev->parent); \
++ struct iscsi_cls_session *session = iscsi_cdev_to_session(cdev); \
+ struct iscsi_transport *t = session->transport; \
+- \
+- if (perm && !capable(CAP_SYS_ADMIN)) \
+- return -EACCES; \
+ return t->get_session_param(session, param, buf); \
+ }
+
+-#define iscsi_session_attr(field, param, perm) \
+- iscsi_session_attr_show(param, perm) \
++#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, 0);
+-iscsi_session_attr(initial_r2t, ISCSI_PARAM_INITIAL_R2T_EN, 0);
+-iscsi_session_attr(max_outstanding_r2t, ISCSI_PARAM_MAX_R2T, 0);
+-iscsi_session_attr(immediate_data, ISCSI_PARAM_IMM_DATA_EN, 0);
+-iscsi_session_attr(first_burst_len, ISCSI_PARAM_FIRST_BURST, 0);
+-iscsi_session_attr(max_burst_len, ISCSI_PARAM_MAX_BURST, 0);
+-iscsi_session_attr(data_pdu_in_order, ISCSI_PARAM_PDU_INORDER_EN, 0);
+-iscsi_session_attr(data_seq_in_order, ISCSI_PARAM_DATASEQ_INORDER_EN, 0);
+-iscsi_session_attr(erl, ISCSI_PARAM_ERL, 0);
+-iscsi_session_attr(tpgt, ISCSI_PARAM_TPGT, 0);
+-iscsi_session_attr(username, ISCSI_PARAM_USERNAME, 1);
+-iscsi_session_attr(username_in, ISCSI_PARAM_USERNAME_IN, 1);
+-iscsi_session_attr(password, ISCSI_PARAM_PASSWORD, 1);
+-iscsi_session_attr(password_in, ISCSI_PARAM_PASSWORD_IN, 1);
+-iscsi_session_attr(fast_abort, ISCSI_PARAM_FAST_ABORT, 0);
+-iscsi_session_attr(abort_tmo, ISCSI_PARAM_ABORT_TMO, 0);
+-iscsi_session_attr(lu_reset_tmo, ISCSI_PARAM_LU_RESET_TMO, 0);
+-
+-static ssize_t
+-show_priv_session_state(struct device *dev, struct device_attribute *attr,
+- char *buf)
+-{
+- struct iscsi_cls_session *session = iscsi_dev_to_session(dev->parent);
+- return sprintf(buf, "%s\n", iscsi_session_state_name(session->state));
+-}
+-static ISCSI_CLASS_ATTR(priv_sess, state, S_IRUGO, show_priv_session_state,
+- 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 device *dev, \
+- struct device_attribute *attr, char *buf) \
++show_priv_session_##field(struct class_device *cdev, char *buf) \
+ { \
+- struct iscsi_cls_session *session = \
+- iscsi_dev_to_session(dev->parent); \
++ struct iscsi_cls_session *session = iscsi_cdev_to_session(cdev);\
+ return sprintf(buf, format"\n", session->field); \
+ }
+
+@@ -1441,32 +1199,9 @@ static ISCSI_CLASS_ATTR(priv_sess, field, S_IRUGO, show_priv_session_##field, \
+ NULL)
+ iscsi_priv_session_attr(recovery_tmo, "%d");
+
+-/*
+- * iSCSI host attrs
+- */
+-#define iscsi_host_attr_show(param) \
+-static ssize_t \
+-show_host_param_##param(struct device *dev, \
+- struct device_attribute *attr, char *buf) \
+-{ \
+- struct Scsi_Host *shost = transport_class_to_shost(dev); \
+- struct iscsi_internal *priv = to_iscsi_internal(shost->transportt); \
+- return priv->iscsi_transport->get_host_param(shost, param, buf); \
+-}
+-
+-#define iscsi_host_attr(field, param) \
+- iscsi_host_attr_show(param) \
+-static ISCSI_CLASS_ATTR(host, field, S_IRUGO, show_host_param_##param, \
+- NULL);
+-
+-iscsi_host_attr(netdev, ISCSI_HOST_PARAM_NETDEV_NAME);
+-iscsi_host_attr(hwaddress, ISCSI_HOST_PARAM_HWADDRESS);
+-iscsi_host_attr(ipaddress, ISCSI_HOST_PARAM_IPADDRESS);
+-iscsi_host_attr(initiatorname, ISCSI_HOST_PARAM_INITIATOR_NAME);
+-
+ #define SETUP_PRIV_SESSION_RD_ATTR(field) \
+ do { \
+- priv->session_attrs[count] = &dev_attr_priv_sess_##field; \
++ priv->session_attrs[count] = &class_device_attr_priv_sess_##field; \
+ count++; \
+ } while (0)
+
+@@ -1474,7 +1209,7 @@ do { \
+ #define SETUP_SESSION_RD_ATTR(field, param_flag) \
+ do { \
+ if (tt->param_mask & param_flag) { \
+- priv->session_attrs[count] = &dev_attr_sess_##field; \
++ priv->session_attrs[count] = &class_device_attr_sess_##field; \
+ count++; \
+ } \
+ } while (0)
+@@ -1482,15 +1217,7 @@ do { \
+ #define SETUP_CONN_RD_ATTR(field, param_flag) \
+ do { \
+ if (tt->param_mask & param_flag) { \
+- priv->conn_attrs[count] = &dev_attr_conn_##field; \
+- count++; \
+- } \
+-} while (0)
+-
+-#define SETUP_HOST_RD_ATTR(field, param_flag) \
+-do { \
+- if (tt->host_param_mask & param_flag) { \
+- priv->host_attrs[count] = &dev_attr_host_##field; \
++ priv->conn_attrs[count] = &class_device_attr_conn_##field; \
+ count++; \
+ } \
+ } while (0)
+@@ -1581,31 +1308,24 @@ iscsi_register_transport(struct iscsi_transport *tt)
+ priv->iscsi_transport = tt;
+ priv->t.user_scan = iscsi_user_scan;
+
+- priv->dev.class = &iscsi_transport_class;
+- snprintf(priv->dev.bus_id, BUS_ID_SIZE, "%s", tt->name);
+- err = device_register(&priv->dev);
++ 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->dev.kobj, &iscsi_transport_group);
++ err = sysfs_create_group(&priv->cdev.kobj, &iscsi_transport_group);
+ if (err)
+- goto unregister_dev;
++ 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);
+
+- SETUP_HOST_RD_ATTR(netdev, ISCSI_HOST_NETDEV_NAME);
+- SETUP_HOST_RD_ATTR(ipaddress, ISCSI_HOST_IPADDRESS);
+- SETUP_HOST_RD_ATTR(hwaddress, ISCSI_HOST_HWADDRESS);
+- SETUP_HOST_RD_ATTR(initiatorname, ISCSI_HOST_INITIATOR_NAME);
+- BUG_ON(count > ISCSI_HOST_ATTRS);
+- priv->host_attrs[count] = NULL;
+- count = 0;
+-
+ /* connection parameters */
+ priv->conn_cont.ac.attrs = &priv->conn_attrs[0];
+ priv->conn_cont.ac.class = &iscsi_connection_class.class;
+@@ -1623,8 +1343,6 @@ iscsi_register_transport(struct iscsi_transport *tt)
+ 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);
+- SETUP_CONN_RD_ATTR(ping_tmo, ISCSI_PING_TMO);
+- SETUP_CONN_RD_ATTR(recv_tmo, ISCSI_RECV_TMO);
+
+ BUG_ON(count > ISCSI_CONN_ATTRS);
+ priv->conn_attrs[count] = NULL;
+@@ -1646,15 +1364,7 @@ iscsi_register_transport(struct iscsi_transport *tt)
+ SETUP_SESSION_RD_ATTR(erl, ISCSI_ERL);
+ SETUP_SESSION_RD_ATTR(targetname, ISCSI_TARGET_NAME);
+ SETUP_SESSION_RD_ATTR(tpgt, ISCSI_TPGT);
+- SETUP_SESSION_RD_ATTR(password, ISCSI_USERNAME);
+- SETUP_SESSION_RD_ATTR(password_in, ISCSI_USERNAME_IN);
+- SETUP_SESSION_RD_ATTR(username, ISCSI_PASSWORD);
+- SETUP_SESSION_RD_ATTR(username_in, ISCSI_PASSWORD_IN);
+- SETUP_SESSION_RD_ATTR(fast_abort, ISCSI_FAST_ABORT);
+- SETUP_SESSION_RD_ATTR(abort_tmo, ISCSI_ABORT_TMO);
+- SETUP_SESSION_RD_ATTR(lu_reset_tmo,ISCSI_LU_RESET_TMO);
+ SETUP_PRIV_SESSION_RD_ATTR(recovery_tmo);
+- SETUP_PRIV_SESSION_RD_ATTR(state);
+
+ BUG_ON(count > ISCSI_SESSION_ATTRS);
+ priv->session_attrs[count] = NULL;
+@@ -1666,8 +1376,8 @@ iscsi_register_transport(struct iscsi_transport *tt)
+ printk(KERN_NOTICE "iscsi: registered transport (%s)\n", tt->name);
+ return &priv->t;
+
+-unregister_dev:
+- device_unregister(&priv->dev);
++unregister_cdev:
++ class_device_unregister(&priv->cdev);
+ free_priv:
+ kfree(priv);
+ return NULL;
+@@ -1694,8 +1404,8 @@ int iscsi_unregister_transport(struct iscsi_transport *tt)
+ transport_container_unregister(&priv->session_cont);
+ transport_container_unregister(&priv->t.host_attrs);
+
+- sysfs_remove_group(&priv->dev.kobj, &iscsi_transport_group);
+- device_unregister(&priv->dev);
++ sysfs_remove_group(&priv->cdev.kobj, &iscsi_transport_group);
++ class_device_unregister(&priv->cdev);
+ mutex_unlock(&rx_queue_mutex);
+
+ return 0;
+@@ -1727,21 +1437,15 @@ static __init int iscsi_transport_init(void)
+ if (err)
+ goto unregister_conn_class;
+
+- nls = netlink_kernel_create(&init_net, NETLINK_ISCSI, 1, iscsi_if_rx, NULL,
++ nls = netlink_kernel_create(NETLINK_ISCSI, 1, iscsi_if_rx, NULL,
+ THIS_MODULE);
+ if (!nls) {
+ err = -ENOBUFS;
+ goto unregister_session_class;
+ }
+
+- iscsi_eh_timer_workq = create_singlethread_workqueue("iscsi_eh");
+- if (!iscsi_eh_timer_workq)
+- goto release_nls;
+-
+ return 0;
+
+-release_nls:
+- netlink_kernel_release(nls);
+ unregister_session_class:
+ transport_class_unregister(&iscsi_session_class);
+ unregister_conn_class:
+@@ -1755,8 +1459,7 @@ unregister_transport_class:
+
+ static void __exit iscsi_transport_exit(void)
+ {
+- destroy_workqueue(iscsi_eh_timer_workq);
+- netlink_kernel_release(nls);
++ sock_release(nls->sk_socket);
+ transport_class_unregister(&iscsi_connection_class);
+ transport_class_unregister(&iscsi_session_class);
+ transport_class_unregister(&iscsi_host_class);
+diff --git a/include/scsi/iscsi_if.h b/include/scsi/iscsi_if.h
+index e19e584..55ebf03 100644
+--- a/include/scsi/iscsi_if.h
++++ b/include/scsi/iscsi_if.h
+@@ -48,16 +48,12 @@ enum iscsi_uevent_e {
+ ISCSI_UEVENT_TRANSPORT_EP_DISCONNECT = UEVENT_BASE + 14,
+
+ ISCSI_UEVENT_TGT_DSCVR = UEVENT_BASE + 15,
+- ISCSI_UEVENT_SET_HOST_PARAM = UEVENT_BASE + 16,
+- ISCSI_UEVENT_UNBIND_SESSION = UEVENT_BASE + 17,
+
+ /* up events */
+ ISCSI_KEVENT_RECV_PDU = KEVENT_BASE + 1,
+ ISCSI_KEVENT_CONN_ERROR = KEVENT_BASE + 2,
+ ISCSI_KEVENT_IF_ERROR = KEVENT_BASE + 3,
+ ISCSI_KEVENT_DESTROY_SESSION = KEVENT_BASE + 4,
+- ISCSI_KEVENT_UNBIND_SESSION = KEVENT_BASE + 5,
+- ISCSI_KEVENT_CREATE_SESSION = KEVENT_BASE + 6,
+ };
+
+ enum iscsi_tgt_dscvr {
+@@ -75,8 +71,6 @@ struct iscsi_uevent {
+ /* messages u -> k */
+ struct msg_create_session {
+ uint32_t initial_cmdsn;
+- uint16_t cmds_max;
+- uint16_t queue_depth;
+ } c_session;
+ struct msg_destroy_session {
+ uint32_t sid;
+@@ -142,11 +136,6 @@ struct iscsi_uevent {
+ */
+ uint32_t enable;
+ } tgt_dscvr;
+- struct msg_set_host_param {
+- uint32_t host_no;
+- uint32_t param; /* enum iscsi_host_param */
+- uint32_t len;
+- } set_host_param;
+ } u;
+ union {
+ /* messages k -> u */
+@@ -159,10 +148,6 @@ struct iscsi_uevent {
+ uint32_t sid;
+ uint32_t cid;
+ } c_conn_ret;
+- struct msg_unbind_session {
+- uint32_t sid;
+- uint32_t host_no;
+- } unbind_session;
+ struct msg_recv_req {
+ uint32_t sid;
+ uint32_t cid;
+@@ -238,18 +223,6 @@ enum iscsi_param {
+ ISCSI_PARAM_CONN_PORT,
+ ISCSI_PARAM_CONN_ADDRESS,
+
+- ISCSI_PARAM_USERNAME,
+- ISCSI_PARAM_USERNAME_IN,
+- ISCSI_PARAM_PASSWORD,
+- ISCSI_PARAM_PASSWORD_IN,
+-
+- ISCSI_PARAM_FAST_ABORT,
+- ISCSI_PARAM_ABORT_TMO,
+- ISCSI_PARAM_LU_RESET_TMO,
+- ISCSI_PARAM_HOST_RESET_TMO,
+-
+- ISCSI_PARAM_PING_TMO,
+- ISCSI_PARAM_RECV_TMO,
+ /* must always be last */
+ ISCSI_PARAM_MAX,
+ };
+@@ -276,30 +249,6 @@ enum iscsi_param {
+ #define ISCSI_SESS_RECOVERY_TMO (1 << ISCSI_PARAM_SESS_RECOVERY_TMO)
+ #define ISCSI_CONN_PORT (1 << ISCSI_PARAM_CONN_PORT)
+ #define ISCSI_CONN_ADDRESS (1 << ISCSI_PARAM_CONN_ADDRESS)
+-#define ISCSI_USERNAME (1 << ISCSI_PARAM_USERNAME)
+-#define ISCSI_USERNAME_IN (1 << ISCSI_PARAM_USERNAME_IN)
+-#define ISCSI_PASSWORD (1 << ISCSI_PARAM_PASSWORD)
+-#define ISCSI_PASSWORD_IN (1 << ISCSI_PARAM_PASSWORD_IN)
+-#define ISCSI_FAST_ABORT (1 << ISCSI_PARAM_FAST_ABORT)
+-#define ISCSI_ABORT_TMO (1 << ISCSI_PARAM_ABORT_TMO)
+-#define ISCSI_LU_RESET_TMO (1 << ISCSI_PARAM_LU_RESET_TMO)
+-#define ISCSI_HOST_RESET_TMO (1 << ISCSI_PARAM_HOST_RESET_TMO)
+-#define ISCSI_PING_TMO (1 << ISCSI_PARAM_PING_TMO)
+-#define ISCSI_RECV_TMO (1 << ISCSI_PARAM_RECV_TMO)
+-
+-/* iSCSI HBA params */
+-enum iscsi_host_param {
+- ISCSI_HOST_PARAM_HWADDRESS,
+- ISCSI_HOST_PARAM_INITIATOR_NAME,
+- ISCSI_HOST_PARAM_NETDEV_NAME,
+- ISCSI_HOST_PARAM_IPADDRESS,
+- ISCSI_HOST_PARAM_MAX,
+-};
+-
+-#define ISCSI_HOST_HWADDRESS (1 << ISCSI_HOST_PARAM_HWADDRESS)
+-#define ISCSI_HOST_INITIATOR_NAME (1 << ISCSI_HOST_PARAM_INITIATOR_NAME)
+-#define ISCSI_HOST_NETDEV_NAME (1 << ISCSI_HOST_PARAM_NETDEV_NAME)
+-#define ISCSI_HOST_IPADDRESS (1 << ISCSI_HOST_PARAM_IPADDRESS)
+
+ #define iscsi_ptr(_handle) ((void*)(unsigned long)_handle)
+ #define iscsi_handle(_ptr) ((uint64_t)(unsigned long)_ptr)
+@@ -323,9 +272,6 @@ enum iscsi_host_param {
+ #define CAP_MULTI_CONN 0x40
+ #define CAP_TEXT_NEGO 0x80
+ #define CAP_MARKERS 0x100
+-#define CAP_FW_DB 0x200
+-#define CAP_SENDTARGETS_OFFLOAD 0x400
+-#define CAP_DATA_PATH_OFFLOAD 0x800
+
+ /*
+ * These flags describes reason of stop_conn() call
+diff --git a/include/scsi/iscsi_proto.h b/include/scsi/iscsi_proto.h
+index e0593bf..8d1e4e8 100644
+--- a/include/scsi/iscsi_proto.h
++++ b/include/scsi/iscsi_proto.h
+@@ -21,15 +21,13 @@
+ #ifndef ISCSI_PROTO_H
+ #define ISCSI_PROTO_H
+
+-#include <linux/types.h>
+-
+ #define ISCSI_DRAFT20_VERSION 0x00
+
+ /* default iSCSI listen port for incoming connections */
+ #define ISCSI_LISTEN_PORT 3260
+
+ /* Padding word length */
+-#define ISCSI_PAD_LEN 4
++#define PAD_WORD_LEN 4
+
+ /*
+ * useful common(control and data pathes) macro
+@@ -45,8 +43,8 @@
+ /* 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, age) ((__force itt_t)\
+- ((itt) | ((age) << ISCSI_AGE_SHIFT)))
++#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)
+
+@@ -112,7 +110,6 @@ struct iscsi_ahs_hdr {
+
+ #define ISCSI_AHSTYPE_CDB 1
+ #define ISCSI_AHSTYPE_RLENGTH 2
+-#define ISCSI_CDB_SIZE 16
+
+ /* iSCSI PDU Header */
+ struct iscsi_cmd {
+@@ -126,7 +123,7 @@ struct iscsi_cmd {
+ __be32 data_length;
+ __be32 cmdsn;
+ __be32 exp_statsn;
+- uint8_t cdb[ISCSI_CDB_SIZE]; /* SCSI Command Block */
++ uint8_t cdb[16]; /* SCSI Command Block */
+ /* Additional Data (Command Dependent) */
+ };
+
+@@ -150,15 +147,6 @@ struct iscsi_rlength_ahdr {
+ __be32 read_length;
+ };
+
+-/* Extended CDB AHS */
+-struct iscsi_ecdb_ahdr {
+- __be16 ahslength; /* CDB length - 15, including reserved byte */
+- uint8_t ahstype;
+- uint8_t reserved;
+- /* 4-byte aligned extended CDB spillover */
+- uint8_t ecdb[260 - ISCSI_CDB_SIZE];
+-};
+-
+ /* SCSI Response Header */
+ struct iscsi_cmd_rsp {
+ uint8_t opcode;
+@@ -612,8 +600,6 @@ struct iscsi_reject {
+ #define ISCSI_MIN_MAX_BURST_LEN 512
+ #define ISCSI_MAX_MAX_BURST_LEN 16777215
+
+-#define ISCSI_DEF_TIME2WAIT 2
+-
+ /************************* RFC 3720 End *****************************/
+
+ #endif /* ISCSI_PROTO_H */
+diff --git a/include/scsi/libiscsi.h b/include/scsi/libiscsi.h
+index cd3ca63..ea0816d 100644
+--- a/include/scsi/libiscsi.h
++++ b/include/scsi/libiscsi.h
+@@ -48,8 +48,9 @@ struct iscsi_nopin;
+ #define debug_scsi(fmt...)
+ #endif
+
+-#define ISCSI_DEF_XMIT_CMDS_MAX 128 /* must be power of 2 */
+-#define ISCSI_MGMT_CMDS_MAX 16 /* must be power of 2 */
++#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
+
+@@ -57,31 +58,21 @@ struct iscsi_nopin;
+ #define ISCSI_MAX_CMD_PER_LUN 128
+
+ /* Task Mgmt states */
+-enum {
+- TMF_INITIAL,
+- TMF_QUEUED,
+- TMF_SUCCESS,
+- TMF_FAILED,
+- TMF_TIMEDOUT,
+- TMF_NOT_FOUND,
+-};
++#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)
+
+-#define ISCSI_ADDRESS_BUF_LEN 64
+-
+-enum {
+- /* this is the maximum possible storage for AHSs */
+- ISCSI_MAX_AHS_SIZE = sizeof(struct iscsi_ecdb_ahdr) +
+- sizeof(struct iscsi_rlength_ahdr),
+- ISCSI_DIGEST_SIZE = sizeof(__u32),
+-};
+-
+ struct iscsi_mgmt_task {
+ /*
+ * Becuae LLDs allocate their hdr differently, this is a pointer to
+@@ -89,7 +80,7 @@ struct iscsi_mgmt_task {
+ */
+ struct iscsi_hdr *hdr;
+ char *data; /* mgmt payload */
+- unsigned data_count; /* counts data to be sent */
++ int data_count; /* counts data to be sent */
+ uint32_t itt; /* this ITT */
+ void *dd_data; /* driver/transport data */
+ struct list_head running;
+@@ -103,23 +94,23 @@ enum {
+
+ struct iscsi_cmd_task {
+ /*
+- * Because LLDs allocate their hdr differently, this is a pointer
+- * and length to that storage. It must be setup at session
+- * creation time.
++ * 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;
+- unsigned short hdr_max;
+- unsigned short hdr_len; /* accumulated size of hdr used */
+ int itt; /* this ITT */
++ int datasn; /* DataSN */
+
+ uint32_t unsol_datasn;
+- unsigned imm_count; /* imm-data (bytes) */
+- unsigned unsol_count; /* unsolicited (bytes)*/
++ int imm_count; /* imm-data (bytes) */
++ int unsol_count; /* unsolicited (bytes)*/
+ /* offset in unsolicited stream (bytes); */
+- unsigned unsol_offset;
+- unsigned data_count; /* remaining Data-Out */
++ 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;
+@@ -128,19 +119,6 @@ struct iscsi_cmd_task {
+ void *dd_data; /* driver/transport data */
+ };
+
+-static inline void* iscsi_next_hdr(struct iscsi_cmd_task *ctask)
+-{
+- return (void*)ctask->hdr + ctask->hdr_len;
+-}
+-
+-/* Connection's states */
+-enum {
+- ISCSI_CONN_INITIAL_STAGE,
+- ISCSI_CONN_STARTED,
+- ISCSI_CONN_STOPPED,
+- ISCSI_CONN_CLEANUP_WAIT,
+-};
+-
+ struct iscsi_conn {
+ struct iscsi_cls_conn *cls_conn; /* ptr to class connection */
+ void *dd_data; /* iscsi_transport data */
+@@ -154,12 +132,6 @@ struct iscsi_conn {
+ * conn_stop() flag: stop to recover, stop to terminate
+ */
+ int stop_stage;
+- struct timer_list transport_timer;
+- unsigned long last_recv;
+- unsigned long last_ping;
+- int ping_timeout;
+- int recv_timeout;
+- struct iscsi_mgmt_task *ping_mtask;
+
+ /* iSCSI connection-wide sequencing */
+ uint32_t exp_statsn;
+@@ -180,24 +152,30 @@ struct iscsi_conn {
+ struct iscsi_cmd_task *ctask; /* xmit ctask in progress */
+
+ /* xmit */
+- struct list_head mgmtqueue; /* mgmt (control) xmit queue */
++ 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 list_head requeue; /* tasks needing another run */
+ 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 tmf_timer;
+- int tmf_state; /* see TMF_INITIAL, etc.*/
++ struct timer_list tmabort_timer;
++ int tmabort_state; /* see TMABORT_INITIAL, etc.*/
+
+ /* negotiated params */
+- unsigned max_recv_dlength; /* initiator_max_recv_dsl*/
+- unsigned max_xmit_dlength; /* target_max_recv_dsl */
++ 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;
+@@ -205,12 +183,6 @@ struct iscsi_conn {
+ /* values userspace uses to id a conn */
+ int persistent_port;
+ char *persistent_address;
+- /* remote portal currently connected to */
+- int portal_port;
+- char portal_address[ISCSI_ADDRESS_BUF_LEN];
+- /* local address */
+- int local_port;
+- char local_address[ISCSI_ADDRESS_BUF_LEN];
+
+ /* MIB-statistics */
+ uint64_t txdata_octets;
+@@ -225,66 +197,34 @@ struct iscsi_conn {
+
+ /* custom statistics */
+ uint32_t eh_abort_cnt;
+- uint32_t fmr_unalign_cnt;
+ };
+
+-struct iscsi_pool {
++struct iscsi_queue {
+ struct kfifo *queue; /* FIFO Queue */
+ void **pool; /* Pool of elements */
+ int max; /* Max number of elements */
+ };
+
+-/* Session's states */
+-enum {
+- ISCSI_STATE_FREE = 1,
+- ISCSI_STATE_LOGGED_IN,
+- ISCSI_STATE_FAILED,
+- ISCSI_STATE_TERMINATE,
+- ISCSI_STATE_IN_RECOVERY,
+- ISCSI_STATE_RECOVERY_FAILED,
+- ISCSI_STATE_LOGGING_OUT,
+-};
+-
+ struct iscsi_session {
+- /*
+- * Syncs up the scsi eh thread with the iscsi eh thread when sending
+- * task management functions. This must be taken before the session
+- * and recv lock.
+- */
+- struct mutex eh_mutex;
+-
+ /* iSCSI session-wide sequencing */
+ uint32_t cmdsn;
+ uint32_t exp_cmdsn;
+ uint32_t max_cmdsn;
+
+- /* This tracks the reqs queued into the initiator */
+- uint32_t queued_cmdsn;
+-
+ /* configuration */
+- int abort_timeout;
+- int lu_reset_timeout;
+ int initial_r2t_en;
+- unsigned max_r2t;
++ int max_r2t;
+ int imm_data_en;
+- unsigned first_burst;
+- unsigned max_burst;
++ int first_burst;
++ int max_burst;
+ int time2wait;
+ int time2retain;
+ int pdu_inorder_en;
+ int dataseq_inorder_en;
+ int erl;
+- int fast_abort;
+ int tpgt;
+- char *username;
+- char *username_in;
+- char *password;
+- char *password_in;
+ char *targetname;
+- char *initiatorname;
+- /* hw address or netdev iscsi connection is bound to */
+- char *hwaddress;
+- char *netdev;
++
+ /* control data */
+ struct iscsi_transport *tt;
+ struct Scsi_Host *host;
+@@ -300,10 +240,10 @@ struct iscsi_session {
+
+ int cmds_max; /* size of cmds array */
+ struct iscsi_cmd_task **cmds; /* Original Cmds arr */
+- struct iscsi_pool cmdpool; /* PDU's pool */
++ 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_pool mgmtpool; /* Mgmt PDU's pool */
++ struct iscsi_queue mgmtpool; /* Mgmt PDU's pool */
+ };
+
+ /*
+@@ -312,26 +252,15 @@ struct iscsi_session {
+ 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_eh_device_reset(struct scsi_cmnd *sc);
+ extern int iscsi_queuecommand(struct scsi_cmnd *sc,
+ void (*done)(struct scsi_cmnd *));
+
+-
+-/*
+- * iSCSI host helpers.
+- */
+-extern int iscsi_host_set_param(struct Scsi_Host *shost,
+- enum iscsi_host_param param, char *buf,
+- int buflen);
+-extern int iscsi_host_get_param(struct Scsi_Host *shost,
+- enum iscsi_host_param param, char *buf);
+-
+ /*
+ * session management
+ */
+ extern struct iscsi_cls_session *
+ iscsi_session_setup(struct iscsi_transport *, struct scsi_transport_template *,
+- uint16_t, uint16_t, int, int, uint32_t, uint32_t *);
++ 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 *);
+@@ -343,10 +272,6 @@ extern int iscsi_session_get_param(struct iscsi_cls_session *cls_session,
+ #define session_to_cls(_sess) \
+ hostdata_session(_sess->host->hostdata)
+
+-#define iscsi_session_printk(prefix, _sess, fmt, a...) \
+- iscsi_cls_session_printk(prefix, \
+- (struct iscsi_cls_session *)session_to_cls(_sess), fmt, ##a)
+-
+ /*
+ * connection management
+ */
+@@ -361,47 +286,26 @@ 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);
+
+-#define iscsi_conn_printk(prefix, _c, fmt, a...) \
+- iscsi_cls_conn_printk(prefix, _c->cls_conn, fmt, ##a)
+-
+ /*
+ * pdu and task processing
+ */
+-extern void iscsi_update_cmdsn(struct iscsi_session *, struct iscsi_nopin *);
++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 *);
+-extern void iscsi_requeue_ctask(struct iscsi_cmd_task *ctask);
+-extern void iscsi_free_mgmt_task(struct iscsi_conn *conn,
+- struct iscsi_mgmt_task *mtask);
+
+ /*
+ * generic helpers
+ */
+-extern void iscsi_pool_free(struct iscsi_pool *);
+-extern int iscsi_pool_init(struct iscsi_pool *, int, void ***, int);
+-
+-/*
+- * inline functions to deal with padding.
+- */
+-static inline unsigned int
+-iscsi_padded(unsigned int len)
+-{
+- return (len + ISCSI_PAD_LEN - 1) & ~(ISCSI_PAD_LEN - 1);
+-}
+-
+-static inline unsigned int
+-iscsi_padding(unsigned int len)
+-{
+- len &= (ISCSI_PAD_LEN - 1);
+- if (len)
+- len = ISCSI_PAD_LEN - len;
+- return len;
+-}
++extern void iscsi_pool_free(struct iscsi_queue *, void **);
++extern int iscsi_pool_init(struct iscsi_queue *, int, void ***, int);
+
+ #endif
+diff --git a/include/scsi/scsi_transport_iscsi.h b/include/scsi/scsi_transport_iscsi.h
+index aab1eae..d5c218d 100644
+--- a/include/scsi/scsi_transport_iscsi.h
++++ b/include/scsi/scsi_transport_iscsi.h
+@@ -24,8 +24,6 @@
+ #define SCSI_TRANSPORT_ISCSI_H
+
+ #include <linux/device.h>
+-#include <linux/list.h>
+-#include <linux/mutex.h>
+ #include <scsi/iscsi_if.h>
+
+ struct scsi_transport_template;
+@@ -81,8 +79,7 @@ struct iscsi_transport {
+ char *name;
+ unsigned int caps;
+ /* LLD sets this to indicate what values it can export to sysfs */
+- uint64_t param_mask;
+- uint64_t host_param_mask;
++ unsigned int param_mask;
+ struct scsi_host_template *host_template;
+ /* LLD connection data size */
+ int conndata_size;
+@@ -92,8 +89,7 @@ struct iscsi_transport {
+ unsigned int max_conn;
+ unsigned int max_cmd_len;
+ struct iscsi_cls_session *(*create_session) (struct iscsi_transport *it,
+- struct scsi_transport_template *t, uint16_t, uint16_t,
+- uint32_t sn, uint32_t *hn);
++ 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);
+@@ -109,18 +105,14 @@ struct iscsi_transport {
+ enum iscsi_param param, char *buf);
+ int (*get_session_param) (struct iscsi_cls_session *session,
+ enum iscsi_param param, char *buf);
+- int (*get_host_param) (struct Scsi_Host *shost,
+- enum iscsi_host_param param, char *buf);
+- int (*set_host_param) (struct Scsi_Host *shost,
+- enum iscsi_host_param param, char *buf,
+- int buflen);
+ 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);
+- int (*init_cmd_task) (struct iscsi_cmd_task *ctask);
++ void (*init_cmd_task) (struct iscsi_cmd_task *ctask);
+ void (*init_mgmt_task) (struct iscsi_conn *conn,
+- struct iscsi_mgmt_task *mtask);
++ 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,
+@@ -132,7 +124,7 @@ struct iscsi_transport {
+ uint64_t *ep_handle);
+ int (*ep_poll) (uint64_t ep_handle, int timeout_ms);
+ void (*ep_disconnect) (uint64_t ep_handle);
+- int (*tgt_dscvr) (struct Scsi_Host *shost, enum iscsi_tgt_dscvr type,
++ int (*tgt_dscvr) (enum iscsi_tgt_dscvr type, uint32_t host_no,
+ uint32_t enable, struct sockaddr *dst_addr);
+ };
+
+@@ -149,6 +141,13 @@ 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 */
+@@ -162,25 +161,18 @@ struct iscsi_cls_conn {
+ #define iscsi_dev_to_conn(_dev) \
+ container_of(_dev, struct iscsi_cls_conn, dev)
+
+-#define iscsi_conn_to_session(_conn) \
+- iscsi_dev_to_session(_conn->dev.parent)
+-
+-/* iscsi class session state */
+-enum {
+- ISCSI_SESSION_LOGGED_IN,
+- ISCSI_SESSION_FAILED,
+- ISCSI_SESSION_FREE,
+-};
++/* 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;
+- spinlock_t lock;
+- struct work_struct block_work;
+- struct work_struct unblock_work;
+- struct work_struct scan_work;
+- struct work_struct unbind_work;
+
+ /* recovery fields */
+ int recovery_tmo;
+@@ -188,7 +180,6 @@ struct iscsi_cls_session {
+
+ int target_id;
+
+- int state;
+ int sid; /* session id */
+ void *dd_data; /* LLD private data */
+ struct device dev; /* sysfs transport/container device */
+@@ -205,28 +196,18 @@ struct iscsi_cls_session {
+
+ struct iscsi_host {
+ struct list_head sessions;
+- atomic_t nr_scans;
+ struct mutex mutex;
+- struct workqueue_struct *scan_workq;
+- char scan_workq_name[KOBJ_NAME_LEN];
+ };
+
+ /*
+ * session and connection functions that can be used by HW iSCSI LLDs
+ */
+-#define iscsi_cls_session_printk(prefix, _cls_session, fmt, a...) \
+- dev_printk(prefix, &(_cls_session)->dev, fmt, ##a)
+-
+-#define iscsi_cls_conn_printk(prefix, _cls_conn, fmt, a...) \
+- dev_printk(prefix, &(_cls_conn)->dev, fmt, ##a)
+-
+-extern int iscsi_session_chkready(struct iscsi_cls_session *session);
+ 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_session_event(struct iscsi_cls_session *session,
+- enum iscsi_uevent_e event);
++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);
+@@ -238,6 +219,6 @@ extern struct iscsi_cls_conn *iscsi_create_conn(struct iscsi_cls_session *sess,
+ 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);
+-extern int iscsi_scan_finished(struct Scsi_Host *shost, unsigned long time);
++
+
+ #endif
+--
+1.5.3.8
+
diff --git a/kernel_patches/backport/2.6.9_U6/iscsi_02_add_to_2_6_9.patch b/kernel_patches/backport/2.6.9_U6/iscsi_02_add_to_2_6_9.patch
new file mode 100644
index 0000000..1f05d95
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U6/iscsi_02_add_to_2_6_9.patch
@@ -0,0 +1,180 @@
+diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c
+index 4376840..11dfaf9 100644
+--- a/drivers/scsi/iscsi_tcp.c
++++ b/drivers/scsi/iscsi_tcp.c
+@@ -2145,7 +2145,6 @@ static void iscsi_tcp_session_destroy(struct iscsi_cls_session *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,
+diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
+index d37048c..60f5846 100644
+--- a/drivers/scsi/libiscsi.c
++++ b/drivers/scsi/libiscsi.c
+@@ -1366,7 +1366,6 @@ iscsi_session_setup(struct iscsi_transport *iscsit,
+ 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);
+diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c
+index 8133c22..f1c68f7 100644
+--- a/drivers/scsi/scsi_transport_iscsi.c
++++ b/drivers/scsi/scsi_transport_iscsi.c
+@@ -65,6 +65,8 @@ static DEFINE_SPINLOCK(iscsi_transport_lock);
+ #define cdev_to_iscsi_internal(_cdev) \
+ container_of(_cdev, struct iscsi_internal, cdev)
+
++extern int attribute_container_init(void);
++
+ static void iscsi_transport_release(struct class_device *cdev)
+ {
+ struct iscsi_internal *priv = cdev_to_iscsi_internal(cdev);
+@@ -80,6 +82,17 @@ static struct class iscsi_transport_class = {
+ .release = iscsi_transport_release,
+ };
+
++static void iscsi_host_class_release(struct class_device *class_dev)
++{
++ struct Scsi_Host *shost = transport_class_to_shost(class_dev);
++ put_device(&shost->shost_gendev);
++}
++
++struct class iscsi_host_class = {
++ .name = "iscsi_host",
++ .release = iscsi_host_class_release,
++};
++
+ static ssize_t
+ show_transport_handle(struct class_device *cdev, char *buf)
+ {
+@@ -115,10 +128,8 @@ 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)
++static int iscsi_setup_host(struct Scsi_Host *shost)
+ {
+- struct Scsi_Host *shost = dev_to_shost(dev);
+ struct iscsi_host *ihost = shost->shost_data;
+
+ memset(ihost, 0, sizeof(*ihost));
+@@ -127,12 +138,6 @@ static int iscsi_setup_host(struct transport_container *tc, struct device *dev,
+ 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,
+@@ -216,24 +221,6 @@ 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 =
+@@ -362,8 +349,6 @@ void iscsi_remove_session(struct iscsi_cls_session *session)
+ list_del(&session->host_list);
+ mutex_unlock(&ihost->mutex);
+
+- scsi_remove_target(&session->dev);
+-
+ transport_unregister_device(&session->dev);
+ device_del(&session->dev);
+ }
+@@ -1269,24 +1254,6 @@ static int iscsi_conn_match(struct attribute_container *cont,
+ 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)
+ {
+@@ -1306,7 +1273,6 @@ iscsi_register_transport(struct iscsi_transport *tt)
+ 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);
+@@ -1319,12 +1285,10 @@ iscsi_register_transport(struct iscsi_transport *tt)
+ 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_attrs = &priv->host_attrs[0];
++ priv->t.host_class = &iscsi_host_class;
++ priv->t.host_setup = iscsi_setup_host;
+ 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];
+@@ -1402,7 +1366,6 @@ int iscsi_unregister_transport(struct iscsi_transport *tt)
+
+ 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);
+@@ -1420,6 +1420,7 @@ static __init int iscsi_transport_init(void)
+ ISCSI_TRANSPORT_VERSION);
+
+ atomic_set(&iscsi_session_nr, 0);
++ attribute_container_init();
+
+ err = class_register(&iscsi_transport_class);
+ if (err)
+ return err;
diff --git a/kernel_patches/backport/2.6.9_U6/iscsi_03_add_session_wq.patch b/kernel_patches/backport/2.6.9_U6/iscsi_03_add_session_wq.patch
new file mode 100644
index 0000000..55564c5
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U6/iscsi_03_add_session_wq.patch
@@ -0,0 +1,76 @@
+diff --git a/drivers/infiniband/ulp/iser/iser_initiator.c b/drivers/infiniband/ulp/iser/iser_initiator.c
+index a6f2303..5d62cc0 100644
+--- a/drivers/infiniband/ulp/iser/iser_initiator.c
++++ b/drivers/infiniband/ulp/iser/iser_initiator.c
+@@ -610,7 +610,7 @@ void iser_snd_completion(struct iser_desc *tx_desc)
+
+ if (resume_tx) {
+ iser_dbg("%ld resuming tx\n",jiffies);
+- scsi_queue_work(conn->session->host, &conn->xmitwork);
++ queue_work(conn->session->wq, &conn->xmitwork);
+ }
+
+ if (tx_desc->type == ISCSI_TX_CONTROL) {
+diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
+index e8020a5..43e9128 100644
+--- a/drivers/scsi/libiscsi.c
++++ b/drivers/scsi/libiscsi.c
+@@ -828,7 +828,7 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
+ session->cmdsn, session->max_cmdsn - session->exp_cmdsn + 1);
+ spin_unlock(&session->lock);
+
+- scsi_queue_work(host, &conn->xmitwork);
++ queue_work(session->wq, &conn->xmitwork);
+ return 0;
+
+ reject:
+@@ -928,7 +928,7 @@ iscsi_conn_send_generic(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ else
+ __kfifo_put(conn->mgmtqueue, (void*)&mtask, sizeof(void*));
+
+- scsi_queue_work(session->host, &conn->xmitwork);
++ queue_work(session->wq, &conn->xmitwork);
+ return 0;
+ }
+
+@@ -1415,6 +1415,9 @@ iscsi_session_setup(struct iscsi_transport *iscsit,
+ INIT_LIST_HEAD(&mtask->running);
+ }
+
++ session->wq = create_singlethread_workqueue("");
++ BUG_ON(!session->wq);
++
+ if (scsi_add_host(shost, NULL))
+ goto add_host_fail;
+
+@@ -1462,6 +1465,8 @@ void iscsi_session_teardown(struct iscsi_cls_session *cls_session)
+
+ kfree(session->targetname);
+
++ destroy_workqueue(session->wq);
++
+ iscsi_destroy_session(cls_session);
+ scsi_host_put(shost);
+ module_put(owner);
+@@ -1595,7 +1600,7 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn)
+ }
+
+ /* flush queued up work because we free the connection below */
+- scsi_flush_work(session->host);
++ flush_workqueue(session->wq);
+
+ spin_lock_bh(&session->lock);
+ kfree(conn->data);
+diff --git a/include/scsi/libiscsi.h b/include/scsi/libiscsi.h
+index ea0816d..e8a95f5 100644
+--- a/include/scsi/libiscsi.h
++++ b/include/scsi/libiscsi.h
+@@ -244,6 +244,8 @@ struct iscsi_session {
+ int mgmtpool_max; /* size of mgmt array */
+ struct iscsi_mgmt_task **mgmt_cmds; /* Original mgmt arr */
+ struct iscsi_queue mgmtpool; /* Mgmt PDU's pool */
++
++ struct workqueue_struct *wq;
+ };
+
+ /*
diff --git a/kernel_patches/backport/2.6.9_U6/iscsi_04_inet_sock_to_opt.patch b/kernel_patches/backport/2.6.9_U6/iscsi_04_inet_sock_to_opt.patch
new file mode 100644
index 0000000..1fb2376
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U6/iscsi_04_inet_sock_to_opt.patch
@@ -0,0 +1,13 @@
+diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c
+index 905efc4..f73a743 100644
+--- a/drivers/scsi/iscsi_tcp.c
++++ b/drivers/scsi/iscsi_tcp.c
+@@ -2027,7 +2027,7 @@ iscsi_tcp_conn_get_param(struct iscsi_cls_conn *cls_conn,
+ {
+ struct iscsi_conn *conn = cls_conn->dd_data;
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+- struct inet_sock *inet;
++ struct inet_opt *inet;
+ struct ipv6_pinfo *np;
+ struct sock *sk;
+ int len;
diff --git a/kernel_patches/backport/2.6.9_U6/iscsi_05_release_host_lock_before_eh.patch b/kernel_patches/backport/2.6.9_U6/iscsi_05_release_host_lock_before_eh.patch
new file mode 100644
index 0000000..c994506
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U6/iscsi_05_release_host_lock_before_eh.patch
@@ -0,0 +1,60 @@
+diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
+index 7db081b..211944e 100644
+--- a/drivers/scsi/libiscsi.c
++++ b/drivers/scsi/libiscsi.c
+@@ -968,12 +968,14 @@ int iscsi_eh_host_reset(struct scsi_cmnd *sc)
+ struct iscsi_conn *conn = session->leadconn;
+ int fail_session = 0;
+
++ spin_unlock_irq(host->host_lock);
+ 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);
++ spin_lock_irq(host->host_lock);
+ return FAILED;
+ }
+
+@@ -1005,6 +1007,7 @@ failed:
+ else
+ goto failed;
+ spin_unlock_bh(&session->lock);
++ spin_lock_irq(host->host_lock);
+
+ return SUCCESS;
+ }
+@@ -1162,13 +1165,16 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
+ struct iscsi_conn *conn;
+ struct iscsi_session *session;
+ int rc;
++ struct Scsi_Host *shost = sc->device->host;
+
++ spin_unlock_irq(shost->host_lock);
+ /*
+ * 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");
++ spin_lock_irq(shost->host_lock);
+ return SUCCESS;
+ }
+
+@@ -1253,6 +1259,7 @@ success_cleanup:
+
+ success_rel_mutex:
+ mutex_unlock(&conn->xmitmutex);
++ spin_lock_irq(shost->host_lock);
+ return SUCCESS;
+
+ failed:
+@@ -1260,6 +1267,7 @@ failed:
+ mutex_unlock(&conn->xmitmutex);
+
+ debug_scsi("abort failed [sc %lx itt 0x%x]\n", (long)sc, ctask->itt);
++ spin_lock_irq(shost->host_lock);
+ return FAILED;
+ }
+ EXPORT_SYMBOL_GPL(iscsi_eh_abort);
diff --git a/kernel_patches/backport/2.6.9_U6/iscsi_06_scsi_addons.patch b/kernel_patches/backport/2.6.9_U6/iscsi_06_scsi_addons.patch
new file mode 100644
index 0000000..a114696
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U6/iscsi_06_scsi_addons.patch
@@ -0,0 +1,75 @@
+diff --git a/drivers/scsi/init.c b/drivers/scsi/init.c
+new file mode 100644
+index 0000000..58cf933
+--- /dev/null
++++ b/drivers/scsi/init.c
+@@ -0,0 +1 @@
++#include "src/init.c"
+diff --git a/drivers/scsi/attribute_container.c b/drivers/scsi/attribute_container.c
+new file mode 100644
+index 0000000..58cf933
+--- /dev/null
++++ b/drivers/scsi/attribute_container.c
+@@ -0,0 +1 @@
++#include "../drivers/base/attribute_container.c"
+diff --git a/drivers/scsi/transport_class.c b/drivers/scsi/transport_class.c
+new file mode 100644
+index 0000000..58cf933
+--- /dev/null
++++ b/drivers/scsi/transport_class.c
+@@ -0,0 +1 @@
++#include "../drivers/base/transport_class.c"
+diff --git a/drivers/scsi/klist.c b/drivers/scsi/klist.c
+new file mode 100644
+index 0000000..58cf933
+--- /dev/null
++++ b/drivers/scsi/klist.c
+@@ -0,0 +1 @@
++#include "../../lib/klist.c"
+diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
+new file mode 100644
+index 0000000..58cf933
+--- /dev/null
++++ b/drivers/scsi/scsi.c
+@@ -0,0 +1 @@
++#include "src/scsi.c"
+diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
+new file mode 100644
+index 0000000..58cf933
+--- /dev/null
++++ b/drivers/scsi/scsi_lib.c
+@@ -0,0 +1 @@
++#include "src/scsi_lib.c"
+diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
+new file mode 100644
+index 0000000..58cf933
+--- /dev/null
++++ b/drivers/scsi/scsi_scan.c
+@@ -0,0 +1 @@
++#include "src/scsi_scan.c"
+diff --git a/drivers/scsi/libiscsi_f.c b/drivers/scsi/libiscsi_f.c
+new file mode 100644
+index 0000000..58cf933
+--- /dev/null
++++ b/drivers/scsi/libiscsi_f.c
+@@ -0,0 +1 @@
++#include "libiscsi.c"
+diff --git a/drivers/scsi/scsi_transport_iscsi_f.c b/drivers/scsi/scsi_transport_iscsi_f.c
+new file mode 100644
+index 0000000..58cf933
+--- /dev/null
++++ b/drivers/scsi/scsi_transport_iscsi_f.c
+@@ -0,0 +1 @@
++#include "scsi_transport_iscsi.c"
+diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
+index e212608..3bf2015 100644
+--- a/drivers/scsi/Makefile
++++ b/drivers/scsi/Makefile
+@@ -3,2 +3,7 @@
+ obj-$(CONFIG_SCSI_ISCSI_ATTRS) += scsi_transport_iscsi.o
+ obj-$(CONFIG_ISCSI_TCP) += libiscsi.o iscsi_tcp.o
++
++CFLAGS_attribute_container.o = $(BACKPORT_INCLUDES)/src/
++
++scsi_transport_iscsi-y := scsi_transport_iscsi_f.o scsi.o scsi_lib.o init.o klist.o attribute_container.o transport_class.o
++libiscsi-y := libiscsi_f.o scsi_scan.o
diff --git a/kernel_patches/backport/2.6.9_U6/iser_01_revert_da9c0c770e775e655e3f77c96d91ee557b117adb.patch b/kernel_patches/backport/2.6.9_U6/iser_01_revert_da9c0c770e775e655e3f77c96d91ee557b117adb.patch
new file mode 100644
index 0000000..101fdc6
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U6/iser_01_revert_da9c0c770e775e655e3f77c96d91ee557b117adb.patch
@@ -0,0 +1,44 @@
+diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
+index bad8dac..75ecabe 100644
+--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
++++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
+@@ -211,10 +211,10 @@ iscsi_iser_ctask_xmit(struct iscsi_conn *conn,
+ int error = 0;
+
+ if (ctask->sc->sc_data_direction == DMA_TO_DEVICE) {
+- BUG_ON(scsi_bufflen(ctask->sc) == 0);
++ BUG_ON(ctask->sc->request_bufflen == 0);
+
+ debug_scsi("cmd [itt %x total %d imm %d unsol_data %d\n",
+- ctask->itt, scsi_bufflen(ctask->sc),
++ ctask->itt, ctask->sc->request_bufflen,
+ ctask->imm_count, ctask->unsol_count);
+ }
+
+diff --git a/drivers/infiniband/ulp/iser/iser_initiator.c b/drivers/infiniband/ulp/iser/iser_initiator.c
+index 5d62cc0..1ae80d8 100644
+--- a/drivers/infiniband/ulp/iser/iser_initiator.c
++++ b/drivers/infiniband/ulp/iser/iser_initiator.c
+@@ -349,12 +349,18 @@ int iser_send_command(struct iscsi_conn *conn,
+ else
+ data_buf = &iser_ctask->data[ISER_DIR_OUT];
+
+- if (scsi_sg_count(sc)) { /* using a scatter list */
+- data_buf->buf = scsi_sglist(sc);
+- data_buf->size = scsi_sg_count(sc);
++ if (sc->use_sg) { /* using a scatter list */
++ data_buf->buf = sc->request_buffer;
++ data_buf->size = sc->use_sg;
++ } else if (sc->request_bufflen) {
++ /* using a single buffer - convert it into one entry SG */
++ sg_init_one(&data_buf->sg_single,
++ sc->request_buffer, sc->request_bufflen);
++ data_buf->buf = &data_buf->sg_single;
++ data_buf->size = 1;
+ }
+
+- data_buf->data_len = scsi_bufflen(sc);
++ data_buf->data_len = sc->request_bufflen;
+
+ if (hdr->flags & ISCSI_FLAG_CMD_READ) {
+ err = iser_prepare_read_cmd(ctask, edtl);
diff --git a/kernel_patches/backport/2.6.9_U6/iser_02_revert_d8196ed2181b4595eaf464a5bcbddb6c28649a39.patch b/kernel_patches/backport/2.6.9_U6/iser_02_revert_d8196ed2181b4595eaf464a5bcbddb6c28649a39.patch
new file mode 100644
index 0000000..7b21cba
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U6/iser_02_revert_d8196ed2181b4595eaf464a5bcbddb6c28649a39.patch
@@ -0,0 +1,12 @@
+diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
+index bad8dac..933429b 100644
+--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
++++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
+@@ -586,7 +586,6 @@ static struct iscsi_transport iscsi_iser_transport = {
+ ISCSI_FAST_ABORT | ISCSI_ABORT_TMO |
+ ISCSI_PING_TMO | ISCSI_RECV_TMO,
+ .host_param_mask = ISCSI_HOST_HWADDRESS |
+- ISCSI_HOST_NETDEV_NAME |
+ ISCSI_HOST_INITIATOR_NAME,
+ .host_template = &iscsi_iser_sht,
+ .conndata_size = sizeof(struct iscsi_conn),
diff --git a/kernel_patches/backport/2.6.9_U6/iser_03_revert_1548271ece9e9312fd5feb41fd58773b56a71d39.patch b/kernel_patches/backport/2.6.9_U6/iser_03_revert_1548271ece9e9312fd5feb41fd58773b56a71d39.patch
new file mode 100644
index 0000000..d72eb5a
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U6/iser_03_revert_1548271ece9e9312fd5feb41fd58773b56a71d39.patch
@@ -0,0 +1,74 @@
+diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
+index bad8dac..7baac99 100644
+--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
++++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
+@@ -368,8 +368,7 @@ static struct iscsi_transport iscsi_iser_transport;
+ static struct iscsi_cls_session *
+ iscsi_iser_session_create(struct iscsi_transport *iscsit,
+ struct scsi_transport_template *scsit,
+- uint16_t cmds_max, uint16_t qdepth,
+- uint32_t initial_cmdsn, uint32_t *hostno)
++ uint32_t initial_cmdsn, uint32_t *hostno)
+ {
+ struct iscsi_cls_session *cls_session;
+ struct iscsi_session *session;
+@@ -380,13 +380,7 @@ iscsi_iser_session_create(struct iscsi_transport *iscsit,
+ struct iscsi_iser_cmd_task *iser_ctask;
+ struct iser_desc *desc;
+
+- /*
+- * we do not support setting can_queue cmd_per_lun from userspace yet
+- * because we preallocate so many resources
+- */
+ cls_session = iscsi_session_setup(iscsit, scsit,
+- ISCSI_DEF_XMIT_CMDS_MAX,
+- ISCSI_MAX_CMD_PER_LUN,
+ sizeof(struct iscsi_iser_cmd_task),
+ sizeof(struct iser_desc),
+ initial_cmdsn, &hn);
+@@ -550,7 +550,7 @@ static struct scsi_host_template iscsi_iser_sht = {
+ .name = "iSCSI Initiator over iSER, v." DRV_VER,
+ .queuecommand = iscsi_queuecommand,
+ .change_queue_depth = iscsi_change_queue_depth,
+- .can_queue = ISCSI_DEF_XMIT_CMDS_MAX - 1,
++ .can_queue = ISCSI_XMIT_CMDS_MAX - 1,
+ .sg_tablesize = ISCSI_ISER_SG_TABLESIZE,
+ .max_sectors = 1024,
+ .cmd_per_lun = ISCSI_MAX_CMD_PER_LUN,
+diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.h b/drivers/infiniband/ulp/iser/iscsi_iser.h
+index 1ee867b..671faff 100644
+--- a/drivers/infiniband/ulp/iser/iscsi_iser.h
++++ b/drivers/infiniband/ulp/iser/iscsi_iser.h
+@@ -105,7 +105,7 @@
+ #define ISER_MAX_TX_MISC_PDUS 6 /* NOOP_OUT(2), TEXT(1), *
+ * SCSI_TMFUNC(2), LOGOUT(1) */
+
+-#define ISER_QP_MAX_RECV_DTOS (ISCSI_DEF_XMIT_CMDS_MAX + \
++#define ISER_QP_MAX_RECV_DTOS (ISCSI_XMIT_CMDS_MAX + \
+ ISER_MAX_RX_MISC_PDUS + \
+ ISER_MAX_TX_MISC_PDUS)
+
+@@ -117,7 +117,7 @@
+
+ #define ISER_INFLIGHT_DATAOUTS 8
+
+-#define ISER_QP_MAX_REQ_DTOS (ISCSI_DEF_XMIT_CMDS_MAX * \
++#define ISER_QP_MAX_REQ_DTOS (ISCSI_XMIT_CMDS_MAX * \
+ (1 + ISER_INFLIGHT_DATAOUTS) + \
+ ISER_MAX_TX_MISC_PDUS + \
+ ISER_MAX_RX_MISC_PDUS)
+diff --git a/drivers/infiniband/ulp/iser/iser_verbs.c b/drivers/infiniband/ulp/iser/iser_verbs.c
+index 654a4dc..f3d8ba5 100644
+--- a/drivers/infiniband/ulp/iser/iser_verbs.c
++++ b/drivers/infiniband/ulp/iser/iser_verbs.c
+@@ -154,8 +154,8 @@ static int iser_create_ib_conn_res(struct iser_conn *ib_conn)
+ params.max_pages_per_fmr = ISCSI_ISER_SG_TABLESIZE + 1;
+ /* make the pool size twice the max number of SCSI commands *
+ * the ML is expected to queue, watermark for unmap at 50% */
+- params.pool_size = ISCSI_DEF_XMIT_CMDS_MAX * 2;
+- params.dirty_watermark = ISCSI_DEF_XMIT_CMDS_MAX;
++ params.pool_size = ISCSI_XMIT_CMDS_MAX * 2;
++ params.dirty_watermark = ISCSI_XMIT_CMDS_MAX;
+ params.cache = 0;
+ params.flush_function = NULL;
+ params.access = (IB_ACCESS_LOCAL_WRITE |
diff --git a/kernel_patches/backport/2.6.9_U6/iser_04_revert_77a23c21aaa723f6b0ffc4a701be8c8e5a32346d.patch b/kernel_patches/backport/2.6.9_U6/iser_04_revert_77a23c21aaa723f6b0ffc4a701be8c8e5a32346d.patch
new file mode 100644
index 0000000..26fa09c
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U6/iser_04_revert_77a23c21aaa723f6b0ffc4a701be8c8e5a32346d.patch
@@ -0,0 +1,38 @@
+diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
+index 8f7b859..5f82d6c 100644
+--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
++++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
+@@ -134,9 +134,18 @@ iscsi_iser_cmd_init(struct iscsi_cmd_task *ctask)
+ {
+ struct iscsi_iser_conn *iser_conn = ctask->conn->dd_data;
+ struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
++ struct scsi_cmnd *sc = ctask->sc;
+
+ iser_ctask->command_sent = 0;
+ iser_ctask->iser_conn = iser_conn;
++ if (sc->sc_data_direction == DMA_TO_DEVICE) {
++ BUG_ON(sc->request_bufflen == 0);
++
++ debug_scsi("cmd [itt %x total %d imm %d unsol_data %d\n",
++ ctask->itt, sc->request_bufflen, ctask->imm_count,
++ ctask->unsol_count);
++ }
++
+ iser_ctask_rdma_init(iser_ctask);
+ return 0;
+ }
+@@ -210,14 +219,6 @@ iscsi_iser_ctask_xmit(struct iscsi_conn *conn,
+ struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
+ int error = 0;
+
+- if (ctask->sc->sc_data_direction == DMA_TO_DEVICE) {
+- BUG_ON(ctask->sc->request_bufflen == 0);
+-
+- debug_scsi("cmd [itt %x total %d imm %d unsol_data %d\n",
+- ctask->itt, ctask->sc->request_bufflen,
+- ctask->imm_count, ctask->unsol_count);
+- }
+-
+ debug_scsi("ctask deq [cid %d itt 0x%x]\n",
+ conn->id, ctask->itt);
+
diff --git a/kernel_patches/backport/2.6.9_U6/iser_05_revert_b2c6416736b847b91950bd43cc5153e11a1f83ee.patch b/kernel_patches/backport/2.6.9_U6/iser_05_revert_b2c6416736b847b91950bd43cc5153e11a1f83ee.patch
new file mode 100644
index 0000000..417415f
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U6/iser_05_revert_b2c6416736b847b91950bd43cc5153e11a1f83ee.patch
@@ -0,0 +1,18 @@
+diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
+index 5f82d6c..3a67d76 100644
+--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
++++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
+@@ -574,11 +574,8 @@ static struct iscsi_transport iscsi_iser_transport = {
+ ISCSI_EXP_STATSN |
+ ISCSI_PERSISTENT_PORT |
+ ISCSI_PERSISTENT_ADDRESS |
+- ISCSI_TARGET_NAME | ISCSI_TPGT |
+- ISCSI_USERNAME | ISCSI_PASSWORD |
+- ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN |
+- ISCSI_FAST_ABORT | ISCSI_ABORT_TMO |
+- ISCSI_PING_TMO | ISCSI_RECV_TMO,
++ ISCSI_TARGET_NAME |
++ ISCSI_TPGT,
+ .host_param_mask = ISCSI_HOST_HWADDRESS |
+ ISCSI_HOST_INITIATOR_NAME,
+ .host_template = &iscsi_iser_sht,
diff --git a/kernel_patches/backport/2.6.9_U6/iser_06_revert_857ae0bdb72999936a28ce621e38e2e288c485da.patch b/kernel_patches/backport/2.6.9_U6/iser_06_revert_857ae0bdb72999936a28ce621e38e2e288c485da.patch
new file mode 100644
index 0000000..0b1a4c4
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U6/iser_06_revert_857ae0bdb72999936a28ce621e38e2e288c485da.patch
@@ -0,0 +1,16 @@
+diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
+index c5941fa..2f4f125 100644
+--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
++++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
+@@ -140,8 +140,8 @@ iscsi_iser_cmd_init(struct iscsi_cmd_task *ctask)
+ iser_ctask->iser_conn = iser_conn;
+ if (sc->sc_data_direction == DMA_TO_DEVICE) {
+- BUG_ON(sc->request_bufflen == 0);
++ BUG_ON(ctask->total_length == 0);
+
+ debug_scsi("cmd [itt %x total %d imm %d unsol_data %d\n",
+- ctask->itt, sc->request_bufflen, ctask->imm_count,
++ ctask->itt, ctask->total_length, ctask->imm_count,
+ ctask->unsol_count);
+ }
+
diff --git a/kernel_patches/backport/2.6.9_U6/iser_07_revert_8ad5781ae9702a8f95cfdf30967752e4297613ee.patch b/kernel_patches/backport/2.6.9_U6/iser_07_revert_8ad5781ae9702a8f95cfdf30967752e4297613ee.patch
new file mode 100644
index 0000000..f207af3
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U6/iser_07_revert_8ad5781ae9702a8f95cfdf30967752e4297613ee.patch
@@ -0,0 +1,14 @@
+diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
+index 2f4f125..940bf98 100644
+--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
++++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
+@@ -576,8 +576,7 @@ static struct iscsi_transport iscsi_iser_transport = {
+ ISCSI_PERSISTENT_ADDRESS |
+ ISCSI_TARGET_NAME |
+ ISCSI_TPGT,
+- .host_param_mask = ISCSI_HOST_HWADDRESS |
+- ISCSI_HOST_INITIATOR_NAME,
++ .host_param_mask = ISCSI_HOST_HWADDRESS,
+ .host_template = &iscsi_iser_sht,
+ .conndata_size = sizeof(struct iscsi_conn),
+ .max_lun = ISCSI_ISER_MAX_LUN,
diff --git a/kernel_patches/backport/2.6.9_U6/iser_08_revert_0801c242a33426fddc005c2f559a3d2fa6fca7eb.patch b/kernel_patches/backport/2.6.9_U6/iser_08_revert_0801c242a33426fddc005c2f559a3d2fa6fca7eb.patch
new file mode 100644
index 0000000..f9dceb1
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U6/iser_08_revert_0801c242a33426fddc005c2f559a3d2fa6fca7eb.patch
@@ -0,0 +1,22 @@
+diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
+index 940bf98..6a35eff 100644
+--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
++++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
+@@ -576,7 +576,6 @@ static struct iscsi_transport iscsi_iser_transport = {
+ ISCSI_PERSISTENT_ADDRESS |
+ ISCSI_TARGET_NAME |
+ ISCSI_TPGT,
+- .host_param_mask = ISCSI_HOST_HWADDRESS,
+ .host_template = &iscsi_iser_sht,
+ .conndata_size = sizeof(struct iscsi_conn),
+ .max_lun = ISCSI_ISER_MAX_LUN,
+@@ -593,9 +593,6 @@ static struct iscsi_transport iscsi_iser_transport = {
+ .get_session_param = iscsi_session_get_param,
+ .start_conn = iscsi_iser_conn_start,
+ .stop_conn = iscsi_conn_stop,
+- /* iscsi host params */
+- .get_host_param = iscsi_host_get_param,
+- .set_host_param = iscsi_host_set_param,
+ /* IO */
+ .send_pdu = iscsi_conn_send_pdu,
+ .get_stats = iscsi_iser_conn_get_stats,
diff --git a/kernel_patches/backport/2.6.9_U6/iser_09_fix_inclusion_order.patch b/kernel_patches/backport/2.6.9_U6/iser_09_fix_inclusion_order.patch
new file mode 100644
index 0000000..3c2a969
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U6/iser_09_fix_inclusion_order.patch
@@ -0,0 +1,13 @@
+--- linux-2.6.20-rc7-orig/drivers/infiniband/ulp/iser/iscsi_iser.c 2007-02-08 09:13:43.000000000 +0200
++++ linux-2.6.20-rc7/drivers/infiniband/ulp/iser/iscsi_iser.c 2007-02-08 09:14:31.000000000 +0200
+@@ -70,9 +70,8 @@
+ #include <scsi/scsi_tcq.h>
+ #include <scsi/scsi_host.h>
+ #include <scsi/scsi.h>
+-#include <scsi/scsi_transport_iscsi.h>
+-
+ #include "iscsi_iser.h"
++#include <scsi/scsi_transport_iscsi.h>
+
+ static unsigned int iscsi_max_lun = 512;
+ module_param_named(max_lun, iscsi_max_lun, uint, S_IRUGO);
diff --git a/kernel_patches/backport/2.6.9_U6/iser_10_fix_struct_scsi_host_template.patch b/kernel_patches/backport/2.6.9_U6/iser_10_fix_struct_scsi_host_template.patch
new file mode 100644
index 0000000..5b28ac4
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U6/iser_10_fix_struct_scsi_host_template.patch
@@ -0,0 +1,31 @@
+From 828e0ad429b92cf75781770ceb9ef7086f34fde2 Mon Sep 17 00:00:00 2001
+From: Doron Shoham <dorons at voltaire.com>
+Date: Thu, 3 Jul 2008 14:31:42 +0300
+Subject: [PATCH] fix_struct_scsi_host_template
+
+Signed-off-by: Doron Shoham <dorons at voltaire.com>
+---
+ drivers/infiniband/ulp/iser/iscsi_iser.c | 2 --
+ 1 files changed, 0 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
+index 9bf24c6..de1e783 100644
+--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
++++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
+@@ -542,13 +542,11 @@ static struct scsi_host_template iscsi_iser_sht = {
+ .module = THIS_MODULE,
+ .name = "iSCSI Initiator over iSER, v." DRV_VER,
+ .queuecommand = iscsi_queuecommand,
+- .change_queue_depth = iscsi_change_queue_depth,
+ .can_queue = ISCSI_XMIT_CMDS_MAX - 1,
+ .sg_tablesize = ISCSI_ISER_SG_TABLESIZE,
+ .max_sectors = 1024,
+ .cmd_per_lun = ISCSI_MAX_CMD_PER_LUN,
+ .eh_abort_handler = iscsi_eh_abort,
+- .eh_device_reset_handler= iscsi_eh_device_reset,
+ .eh_host_reset_handler = iscsi_eh_host_reset,
+ .use_clustering = DISABLE_CLUSTERING,
+ .proc_name = "iscsi_iser",
+--
+1.5.3.8
+
diff --git a/kernel_patches/backport/2.6.9_U6/iser_11_add_fmr_unalign_cnt.patch b/kernel_patches/backport/2.6.9_U6/iser_11_add_fmr_unalign_cnt.patch
new file mode 100644
index 0000000..ef2a2d6
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U6/iser_11_add_fmr_unalign_cnt.patch
@@ -0,0 +1,25 @@
+From 1255c8e5209ce19644e83e353c260f2eddc62cca Mon Sep 17 00:00:00 2001
+From: Doron Shoham <dorons at voltaire.com>
+Date: Thu, 3 Jul 2008 14:54:57 +0300
+Subject: [PATCH] add fmr_unalign_cnt to struct iscsi_conn
+
+Signed-off-by: Doron Shoham <dorons at voltaire.com>
+---
+ include/scsi/libiscsi.h | 1 +
+ 1 files changed, 1 insertions(+), 0 deletions(-)
+
+diff --git a/include/scsi/libiscsi.h b/include/scsi/libiscsi.h
+index ea0816d..182421f 100644
+--- a/include/scsi/libiscsi.h
++++ b/include/scsi/libiscsi.h
+@@ -197,6 +197,7 @@ struct iscsi_conn {
+
+ /* custom statistics */
+ uint32_t eh_abort_cnt;
++ uint32_t fmr_unalign_cnt;
+ };
+
+ struct iscsi_queue {
+--
+1.5.3.8
+
diff --git a/kernel_patches/backport/2.6.9_U6/iser_12_remove_hdr_max.patch b/kernel_patches/backport/2.6.9_U6/iser_12_remove_hdr_max.patch
new file mode 100644
index 0000000..c475001
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U6/iser_12_remove_hdr_max.patch
@@ -0,0 +1,25 @@
+From 97672ef8a29da5e16774d1de9527b2cc29415e36 Mon Sep 17 00:00:00 2001
+From: Doron Shoham <dorons at voltaire.com>
+Date: Thu, 3 Jul 2008 14:59:16 +0300
+Subject: [PATCH] remove hdr_max
+
+Signed-off-by: Doron Shoham <dorons at voltaire.com>
+---
+ drivers/infiniband/ulp/iser/iscsi_iser.c | 1 -
+ 1 files changed, 0 insertions(+), 1 deletions(-)
+
+diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
+index de1e783..6451e9d 100644
+--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
++++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
+@@ -394,7 +394,6 @@ iscsi_iser_session_create(struct iscsi_transport *iscsit,
+ ctask = session->cmds[i];
+ iser_ctask = ctask->dd_data;
+ ctask->hdr = (struct iscsi_cmd *)&iser_ctask->desc.iscsi_header;
+- ctask->hdr_max = sizeof(iser_ctask->desc.iscsi_header);
+ }
+
+ for (i = 0; i < session->mgmtpool_max; i++) {
+--
+1.5.3.8
+
diff --git a/kernel_patches/backport/2.6.9_U6/iser_13_fix_netlink_kernel_create.patch b/kernel_patches/backport/2.6.9_U6/iser_13_fix_netlink_kernel_create.patch
new file mode 100644
index 0000000..d47df44
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U6/iser_13_fix_netlink_kernel_create.patch
@@ -0,0 +1,26 @@
+From db61fe2c3062d8918e793ddc7e1a8cc3694bf620 Mon Sep 17 00:00:00 2001
+From: Doron Shoham <dorons at voltaire.com>
+Date: Sun, 6 Jul 2008 15:20:42 +0300
+Subject: [PATCH] fix netlink_kernel_create
+
+Signed-off-by: Doron Shoham <dorons at voltaire.com>
+---
+ drivers/scsi/scsi_transport_iscsi.c | 2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c
+index e969ef7..a2f4fb7 100644
+--- a/drivers/scsi/scsi_transport_iscsi.c
++++ b/drivers/scsi/scsi_transport_iscsi.c
+@@ -1401,7 +1401,7 @@ static __init int iscsi_transport_init(void)
+ if (err)
+ goto unregister_conn_class;
+
+- nls = netlink_kernel_create(NETLINK_ISCSI, 1, iscsi_if_rx, NULL,
++ nls = netlink_kernel_create(NULL, NETLINK_ISCSI, 1, iscsi_if_rx, NULL,
+ THIS_MODULE);
+ if (!nls) {
+ err = -ENOBUFS;
+--
+1.5.3.8
+
diff --git a/kernel_patches/backport/2.6.9_U6/iser_14_sync_attribute_container.c_from_ofed1.3.patch b/kernel_patches/backport/2.6.9_U6/iser_14_sync_attribute_container.c_from_ofed1.3.patch
new file mode 100644
index 0000000..e926007
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U6/iser_14_sync_attribute_container.c_from_ofed1.3.patch
@@ -0,0 +1,394 @@
+From bed65721f623039a119b5ff03c6c1fe44a1ccfb3 Mon Sep 17 00:00:00 2001
+From: Doron Shoham <dorons at voltaire.com>
+Date: Sun, 6 Jul 2008 15:26:20 +0300
+Subject: [PATCH] sync attribute_container.c from ofed1.3
+
+Signed-off-by: Doron Shoham <dorons at voltaire.com>
+---
+ drivers/base/attribute_container.c | 100 +++++++++++++++++------------------
+ drivers/base/transport_class.c | 21 ++++----
+ 2 files changed, 60 insertions(+), 61 deletions(-)
+
+diff --git a/drivers/base/attribute_container.c b/drivers/base/attribute_container.c
+index f57652d..7370d7c 100644
+--- a/drivers/base/attribute_container.c
++++ b/drivers/base/attribute_container.c
+@@ -27,21 +27,21 @@
+ struct internal_container {
+ struct klist_node node;
+ struct attribute_container *cont;
+- struct device classdev;
++ struct class_device classdev;
+ };
+
+ static void internal_container_klist_get(struct klist_node *n)
+ {
+ struct internal_container *ic =
+ container_of(n, struct internal_container, node);
+- get_device(&ic->classdev);
++ class_device_get(&ic->classdev);
+ }
+
+ static void internal_container_klist_put(struct klist_node *n)
+ {
+ struct internal_container *ic =
+ container_of(n, struct internal_container, node);
+- put_device(&ic->classdev);
++ class_device_put(&ic->classdev);
+ }
+
+
+@@ -53,7 +53,7 @@ static void internal_container_klist_put(struct klist_node *n)
+ * Returns the container associated with this classdev.
+ */
+ struct attribute_container *
+-attribute_container_classdev_to_container(struct device *classdev)
++attribute_container_classdev_to_container(struct class_device *classdev)
+ {
+ struct internal_container *ic =
+ container_of(classdev, struct internal_container, classdev);
+@@ -61,7 +61,7 @@ attribute_container_classdev_to_container(struct device *classdev)
+ }
+ EXPORT_SYMBOL_GPL(attribute_container_classdev_to_container);
+
+-static LIST_HEAD(attribute_container_list);
++static struct list_head attribute_container_list;
+
+ static DEFINE_MUTEX(attribute_container_mutex);
+
+@@ -110,11 +110,11 @@ attribute_container_unregister(struct attribute_container *cont)
+ EXPORT_SYMBOL_GPL(attribute_container_unregister);
+
+ /* private function used as class release */
+-static void attribute_container_release(struct device *classdev)
++static void attribute_container_release(struct class_device *classdev)
+ {
+ struct internal_container *ic
+ = container_of(classdev, struct internal_container, classdev);
+- struct device *dev = classdev->parent;
++ struct device *dev = classdev->dev;
+
+ kfree(ic);
+ put_device(dev);
+@@ -129,12 +129,12 @@ static void attribute_container_release(struct device *classdev)
+ * This function allocates storage for the class device(s) to be
+ * attached to dev (one for each matching attribute_container). If no
+ * fn is provided, the code will simply register the class device via
+- * device_add. If a function is provided, it is expected to add
++ * class_device_add. If a function is provided, it is expected to add
+ * the class device at the appropriate time. One of the things that
+ * might be necessary is to allocate and initialise the classdev and
+ * then add it a later time. To do this, call this routine for
+ * allocation and initialisation and then use
+- * attribute_container_device_trigger() to call device_add() on
++ * attribute_container_device_trigger() to call class_device_add() on
+ * it. Note: after this, the class device contains a reference to dev
+ * which is not relinquished until the release of the classdev.
+ */
+@@ -142,7 +142,7 @@ void
+ attribute_container_add_device(struct device *dev,
+ int (*fn)(struct attribute_container *,
+ struct device *,
+- struct device *))
++ struct class_device *))
+ {
+ struct attribute_container *cont;
+
+@@ -163,11 +163,11 @@ attribute_container_add_device(struct device *dev,
+ }
+
+ ic->cont = cont;
+- device_initialize(&ic->classdev);
+- ic->classdev.parent = get_device(dev);
++ class_device_initialize(&ic->classdev);
++ ic->classdev.dev = get_device(dev);
+ ic->classdev.class = cont->class;
+- cont->class->dev_release = attribute_container_release;
+- strcpy(ic->classdev.bus_id, dev->bus_id);
++ cont->class->release = attribute_container_release;
++ strcpy(ic->classdev.class_id, dev->bus_id);
+ if (fn)
+ fn(cont, dev, &ic->classdev);
+ else
+@@ -195,19 +195,20 @@ attribute_container_add_device(struct device *dev,
+ * @fn: A function to call to remove the device
+ *
+ * This routine triggers device removal. If fn is NULL, then it is
+- * simply done via device_unregister (note that if something
++ * simply done via class_device_unregister (note that if something
+ * still has a reference to the classdev, then the memory occupied
+ * will not be freed until the classdev is released). If you want a
+ * two phase release: remove from visibility and then delete the
+ * device, then you should use this routine with a fn that calls
+- * device_del() and then use attribute_container_device_trigger()
+- * to do the final put on the classdev.
++ * class_device_del() and then use
++ * attribute_container_device_trigger() to do the final put on the
++ * classdev.
+ */
+ void
+ attribute_container_remove_device(struct device *dev,
+ void (*fn)(struct attribute_container *,
+ struct device *,
+- struct device *))
++ struct class_device *))
+ {
+ struct attribute_container *cont;
+
+@@ -223,14 +224,14 @@ attribute_container_remove_device(struct device *dev,
+ continue;
+
+ klist_for_each_entry(ic, &cont->containers, node, &iter) {
+- if (dev != ic->classdev.parent)
++ if (dev != ic->classdev.dev)
+ continue;
+ klist_del(&ic->node);
+ if (fn)
+ fn(cont, dev, &ic->classdev);
+ else {
+ attribute_container_remove_attrs(&ic->classdev);
+- device_unregister(&ic->classdev);
++ class_device_unregister(&ic->classdev);
+ }
+ }
+ }
+@@ -251,7 +252,7 @@ void
+ attribute_container_device_trigger(struct device *dev,
+ int (*fn)(struct attribute_container *,
+ struct device *,
+- struct device *))
++ struct class_device *))
+ {
+ struct attribute_container *cont;
+
+@@ -269,7 +270,7 @@ attribute_container_device_trigger(struct device *dev,
+ }
+
+ klist_for_each_entry(ic, &cont->containers, node, &iter) {
+- if (dev == ic->classdev.parent)
++ if (dev == ic->classdev.dev)
+ fn(cont, dev, &ic->classdev);
+ }
+ }
+@@ -312,23 +313,18 @@ attribute_container_trigger(struct device *dev,
+ * attributes listed in the container
+ */
+ int
+-attribute_container_add_attrs(struct device *classdev)
++attribute_container_add_attrs(struct class_device *classdev)
+ {
+ struct attribute_container *cont =
+ attribute_container_classdev_to_container(classdev);
+- struct device_attribute **attrs = cont->attrs;
++ struct class_device_attribute **attrs = cont->attrs;
+ int i, error;
+
+- BUG_ON(attrs && cont->grp);
+-
+- if (!attrs && !cont->grp)
++ if (!attrs)
+ return 0;
+
+- if (cont->grp)
+- return sysfs_create_group(&classdev->kobj, cont->grp);
+-
+ for (i = 0; attrs[i]; i++) {
+- error = device_create_file(classdev, attrs[i]);
++ error = class_device_create_file(classdev, attrs[i]);
+ if (error)
+ return error;
+ }
+@@ -337,18 +333,18 @@ attribute_container_add_attrs(struct device *classdev)
+ }
+
+ /**
+- * attribute_container_add_class_device - same function as device_add
++ * attribute_container_add_class_device - same function as class_device_add
+ *
+ * @classdev: the class device to add
+ *
+- * This performs essentially the same function as device_add except for
++ * This performs essentially the same function as class_device_add except for
+ * attribute containers, namely add the classdev to the system and then
+ * create the attribute files
+ */
+ int
+-attribute_container_add_class_device(struct device *classdev)
++attribute_container_add_class_device(struct class_device *classdev)
+ {
+- int error = device_add(classdev);
++ int error = class_device_add(classdev);
+ if (error)
+ return error;
+ return attribute_container_add_attrs(classdev);
+@@ -363,7 +359,7 @@ attribute_container_add_class_device(struct device *classdev)
+ int
+ attribute_container_add_class_device_adapter(struct attribute_container *cont,
+ struct device *dev,
+- struct device *classdev)
++ struct class_device *classdev)
+ {
+ return attribute_container_add_class_device(classdev);
+ }
+@@ -375,23 +371,18 @@ attribute_container_add_class_device_adapter(struct attribute_container *cont,
+ *
+ */
+ void
+-attribute_container_remove_attrs(struct device *classdev)
++attribute_container_remove_attrs(struct class_device *classdev)
+ {
+ struct attribute_container *cont =
+ attribute_container_classdev_to_container(classdev);
+- struct device_attribute **attrs = cont->attrs;
++ struct class_device_attribute **attrs = cont->attrs;
+ int i;
+
+- if (!attrs && !cont->grp)
++ if (!attrs)
+ return;
+
+- if (cont->grp) {
+- sysfs_remove_group(&classdev->kobj, cont->grp);
+- return ;
+- }
+-
+ for (i = 0; attrs[i]; i++)
+- device_remove_file(classdev, attrs[i]);
++ class_device_remove_file(classdev, attrs[i]);
+ }
+
+ /**
+@@ -400,13 +391,13 @@ attribute_container_remove_attrs(struct device *classdev)
+ * @classdev: the class device
+ *
+ * This function simply removes all the attribute files and then calls
+- * device_del.
++ * class_device_del.
+ */
+ void
+-attribute_container_class_device_del(struct device *classdev)
++attribute_container_class_device_del(struct class_device *classdev)
+ {
+ attribute_container_remove_attrs(classdev);
+- device_del(classdev);
++ class_device_del(classdev);
+ }
+
+ /**
+@@ -418,16 +409,16 @@ attribute_container_class_device_del(struct device *classdev)
+ * Looks up the device in the container's list of class devices and returns
+ * the corresponding class_device.
+ */
+-struct device *
++struct class_device *
+ attribute_container_find_class_device(struct attribute_container *cont,
+ struct device *dev)
+ {
+- struct device *cdev = NULL;
++ struct class_device *cdev = NULL;
+ struct internal_container *ic;
+ struct klist_iter iter;
+
+ klist_for_each_entry(ic, &cont->containers, node, &iter) {
+- if (ic->classdev.parent == dev) {
++ if (ic->classdev.dev == dev) {
+ cdev = &ic->classdev;
+ /* FIXME: must exit iterator then break */
+ klist_iter_exit(&iter);
+@@ -438,3 +429,10 @@ attribute_container_find_class_device(struct attribute_container *cont,
+ return cdev;
+ }
+ EXPORT_SYMBOL_GPL(attribute_container_find_class_device);
++
++int __init
++attribute_container_init(void)
++{
++ INIT_LIST_HEAD(&attribute_container_list);
++ return 0;
++}
+diff --git a/drivers/base/transport_class.c b/drivers/base/transport_class.c
+index 84997ef..f25e7c6 100644
+--- a/drivers/base/transport_class.c
++++ b/drivers/base/transport_class.c
+@@ -66,7 +66,7 @@ EXPORT_SYMBOL_GPL(transport_class_unregister);
+
+ static int anon_transport_dummy_function(struct transport_container *tc,
+ struct device *dev,
+- struct device *cdev)
++ struct class_device *cdev)
+ {
+ /* do nothing */
+ return 0;
+@@ -108,14 +108,13 @@ EXPORT_SYMBOL_GPL(anon_transport_class_register);
+ */
+ void anon_transport_class_unregister(struct anon_transport_class *atc)
+ {
+- if (unlikely(attribute_container_unregister(&atc->container)))
+- BUG();
++ attribute_container_unregister(&atc->container);
+ }
+ EXPORT_SYMBOL_GPL(anon_transport_class_unregister);
+
+ static int transport_setup_classdev(struct attribute_container *cont,
+ struct device *dev,
+- struct device *classdev)
++ struct class_device *classdev)
+ {
+ struct transport_class *tclass = class_to_transport_class(cont->class);
+ struct transport_container *tcont = attribute_container_to_transport_container(cont);
+@@ -127,7 +126,9 @@ static int transport_setup_classdev(struct attribute_container *cont,
+ }
+
+ /**
+- * transport_setup_device - declare a new dev for transport class association but don't make it visible yet.
++ * transport_setup_device - declare a new dev for transport class association
++ * but don't make it visible yet.
++ *
+ * @dev: the generic device representing the entity being added
+ *
+ * Usually, dev represents some component in the HBA system (either
+@@ -149,7 +150,7 @@ EXPORT_SYMBOL_GPL(transport_setup_device);
+
+ static int transport_add_class_device(struct attribute_container *cont,
+ struct device *dev,
+- struct device *classdev)
++ struct class_device *classdev)
+ {
+ int error = attribute_container_add_class_device(classdev);
+ struct transport_container *tcont =
+@@ -181,7 +182,7 @@ EXPORT_SYMBOL_GPL(transport_add_device);
+
+ static int transport_configure(struct attribute_container *cont,
+ struct device *dev,
+- struct device *cdev)
++ struct class_device *cdev)
+ {
+ struct transport_class *tclass = class_to_transport_class(cont->class);
+ struct transport_container *tcont = attribute_container_to_transport_container(cont);
+@@ -212,7 +213,7 @@ EXPORT_SYMBOL_GPL(transport_configure_device);
+
+ static int transport_remove_classdev(struct attribute_container *cont,
+ struct device *dev,
+- struct device *classdev)
++ struct class_device *classdev)
+ {
+ struct transport_container *tcont =
+ attribute_container_to_transport_container(cont);
+@@ -251,12 +252,12 @@ EXPORT_SYMBOL_GPL(transport_remove_device);
+
+ static void transport_destroy_classdev(struct attribute_container *cont,
+ struct device *dev,
+- struct device *classdev)
++ struct class_device *classdev)
+ {
+ struct transport_class *tclass = class_to_transport_class(cont->class);
+
+ if (tclass->remove != anon_transport_dummy_function)
+- put_device(classdev);
++ class_device_put(classdev);
+ }
+
+
+--
+1.5.3.8
+
diff --git a/kernel_patches/backport/2.6.9_U6/iser_15_fix_iscsi_free_mgmt_task.patch b/kernel_patches/backport/2.6.9_U6/iser_15_fix_iscsi_free_mgmt_task.patch
new file mode 100644
index 0000000..7a3a3ea
--- /dev/null
+++ b/kernel_patches/backport/2.6.9_U6/iser_15_fix_iscsi_free_mgmt_task.patch
@@ -0,0 +1,28 @@
+From 5a9fd2300982aca58f1306bdb98cab878998a607 Mon Sep 17 00:00:00 2001
+From: Doron Shoham <dorons at voltaire.com>
+Date: Sun, 6 Jul 2008 15:53:59 +0300
+Subject: [PATCH] fix iscsi_free_mgmt_task
+
+Signed-off-by: Doron Shoham <dorons at voltaire.com>
+---
+ drivers/infiniband/ulp/iser/iser_initiator.c | 4 +++-
+ 1 files changed, 3 insertions(+), 1 deletions(-)
+
+diff --git a/drivers/infiniband/ulp/iser/iser_initiator.c b/drivers/infiniband/ulp/iser/iser_initiator.c
+index 4e20c8b..e7f2399 100644
+--- a/drivers/infiniband/ulp/iser/iser_initiator.c
++++ b/drivers/infiniband/ulp/iser/iser_initiator.c
+@@ -627,7 +627,9 @@ void iser_snd_completion(struct iser_desc *tx_desc)
+ struct iscsi_session *session = conn->session;
+
+ spin_lock(&conn->session->lock);
+- iscsi_free_mgmt_task(conn, mtask);
++ list_del(&mtask->running);
++ __kfifo_put(session->mgmtpool.queue, (void*)&mtask,
++ sizeof(void*));
+ spin_unlock(&session->lock);
+ }
+ }
+--
+1.5.3.8
+
--
1.5.3.8
More information about the ewg
mailing list