Skip to contentSkip to conversation

RESEND: iscsi update for 2.6.25

PATCH In review [email protected] 2 participants 26 messages Dec 13, 2007 – Dec 16, 2007

Subsystems (5)
lore
Share & tools
lore
#0 lore raw

Between the time I made my last patchset and when James rebased scsi-misc a patch to scsi_transport_iscsi.c got merged and that broke my patchset. This is a resend of the patchset, but rebased against scsi-misc.

Sorry for the bigger than normal update. There are patches from Olaf and Boaz which do the following:

- Rewrite iscsi_tcp data path (from Olaf), so we do not have two ulgy abstractions for the send and recv path. Now we have one nice iscsi_segment struct that works for both paths.
- Add basic iscsi spec handling we will need for Bidi support (from Boaz). This does not add bidi support. It just adds code that is common.
- Device reset support. Some clustering software needed at least device reset support, so I cleaned up that code and added device logical unit reset support. I will add warm target reset support later.
- Lots of fixes for things like shutdown, r2t leaks, and lots of clean up from Olaf.
- sg chaining support.

#1 lore raw

PATCH 1/24 libiscsi, iscsi_tcp: add device support

From: Mike Christie <[email protected]>

This patch adds logical unit reset support. This should work for ib_iser, but I have not finished testing that driver so it is not hooked in yet.

This patch also temporarily reverts the iscsi_tcp r2t write out patch. That code is completely rewritten in this patchset.

---
 drivers/infiniband/ulp/iser/iscsi_iser.c |    6 -
 drivers/scsi/iscsi_tcp.c                 |  151 +++++-----
 drivers/scsi/iscsi_tcp.h                 |   34 +-
 drivers/scsi/libiscsi.c                  |  494 +++++++++++++++++-------------
 drivers/scsi/scsi_transport_iscsi.c      |    4 +-
 include/scsi/iscsi_if.h                  |    2 +
 include/scsi/iscsi_proto.h               |    2 +
 include/scsi/libiscsi.h                  |   25 +-
 8 files changed, 396 insertions(+), 322 deletions(-)
drivers/infiniband/ulp/iser/iscsi_iser.c +0 -6
index bad8dac..2eadb6d 100644
@@ -220,12 +220,6 @@ iscsi_iser_ctask_xmit(struct iscsi_conn *conn,
 	debug_scsi("ctask deq [cid %d itt 0x%x]\n",
 		   conn->id, ctask->itt);
 
-	/*
-	 * serialize with TMF AbortTask
-	 */
-	if (ctask->mtask)
-		return error;
-
 	/* Send the cmd PDU */
 	if (!iser_ctask->command_sent) {
 		error = iser_send_command(conn, ctask);
drivers/scsi/iscsi_tcp.c +76 -75

408 lines not on this page · raw message

drivers/scsi/iscsi_tcp.h +17 -17
index 68c36cc..7eba44d 100644
@@ -32,21 +32,21 @@
 #define IN_PROGRESS_PAD_RECV		0x4
 
 /* xmit state machine */
-#define XMSTATE_VALUE_IDLE			0
-#define XMSTATE_BIT_CMD_HDR_INIT		0
-#define XMSTATE_BIT_CMD_HDR_XMIT		1
-#define XMSTATE_BIT_IMM_HDR			2
-#define XMSTATE_BIT_IMM_DATA			3
-#define XMSTATE_BIT_UNS_INIT			4
-#define XMSTATE_BIT_UNS_HDR			5
-#define XMSTATE_BIT_UNS_DATA			6
-#define XMSTATE_BIT_SOL_HDR			7
-#define XMSTATE_BIT_SOL_DATA			8
-#define XMSTATE_BIT_W_PAD			9
-#define XMSTATE_BIT_W_RESEND_PAD		10
-#define XMSTATE_BIT_W_RESEND_DATA_DIGEST	11
-#define XMSTATE_BIT_IMM_HDR_INIT		12
-#define XMSTATE_BIT_SOL_HDR_INIT		13
+#define XMSTATE_IDLE			0x0
+#define XMSTATE_CMD_HDR_INIT		0x1
+#define XMSTATE_CMD_HDR_XMIT		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 XMSTATE_IMM_HDR_INIT		0x1000
+#define XMSTATE_SOL_HDR_INIT		0x2000
 
 #define ISCSI_PAD_LEN			4
 #define ISCSI_SG_TABLESIZE		SG_ALL
@@ -122,7 +122,7 @@ struct iscsi_data_task {
 struct iscsi_tcp_mgmt_task {
 	struct iscsi_hdr	hdr;
 	char			hdrext[sizeof(__u32)]; /* Header-Digest */
-	unsigned long		xmstate;	/* mgmt xmit progress */
+	int			xmstate;	/* mgmt xmit progress */
 	struct iscsi_buf	headbuf;	/* header buffer */
 	struct iscsi_buf	sendbuf;	/* in progress buffer */
 	int			sent;
@@ -150,7 +150,7 @@ struct iscsi_tcp_cmd_task {
 	int			pad_count;		/* padded bytes */
 	struct iscsi_buf	headbuf;		/* header buf (xmit) */
 	struct iscsi_buf	sendbuf;		/* in progress buffer*/
-	unsigned long		xmstate;		/* xmit xtate machine */
+	int			xmstate;		/* xmit xtate machine */
 	int			sent;
 	struct scatterlist	*sg;			/* per-cmd SG list  */
 	struct scatterlist	*bad_sg;		/* assert statement */
drivers/scsi/libiscsi.c +281 -213

806 lines not on this page · raw message

drivers/scsi/scsi_transport_iscsi.c +3 -1
index cb48b80..75d3069 100644
@@ -30,7 +30,7 @@
 #include <scsi/scsi_transport_iscsi.h>
 #include <scsi/iscsi_if.h>
 
-#define ISCSI_SESSION_ATTRS 15
+#define ISCSI_SESSION_ATTRS 16
 #define ISCSI_CONN_ATTRS 11
 #define ISCSI_HOST_ATTRS 4
 #define ISCSI_TRANSPORT_VERSION "2.0-724"
@@ -1218,6 +1218,7 @@ 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, 1);
 
 #define iscsi_priv_session_attr_show(field, format)			\
 static ssize_t								\
@@ -1439,6 +1440,7 @@ iscsi_register_transport(struct iscsi_transport *tt)
 	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_PRIV_SESSION_RD_ATTR(recovery_tmo);
 
 	BUG_ON(count > ISCSI_SESSION_ATTRS);
include/scsi/iscsi_if.h +2 -0
index 50e907f..bff0b1f 100644
@@ -236,6 +236,7 @@ enum iscsi_param {
 	ISCSI_PARAM_PASSWORD,
 	ISCSI_PARAM_PASSWORD_IN,
 
+	ISCSI_PARAM_FAST_ABORT,
 	/* must always be last */
 	ISCSI_PARAM_MAX,
 };
@@ -266,6 +267,7 @@ enum iscsi_param {
 #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)
 
 /* iSCSI HBA params */
 enum iscsi_host_param {
include/scsi/iscsi_proto.h +2 -0
index 8d1e4e8..751c81e 100644
@@ -600,6 +600,8 @@ 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 */
include/scsi/libiscsi.h +15 -10
index b4b3113..89429f4 100644
@@ -57,11 +57,14 @@ struct iscsi_nopin;
 #define ISCSI_MAX_CMD_PER_LUN		128
 
 /* Task Mgmt states */
-#define TMABORT_INITIAL			0x0
-#define TMABORT_SUCCESS			0x1
-#define TMABORT_FAILED			0x2
-#define TMABORT_TIMEDOUT		0x3
-#define TMABORT_NOT_FOUND		0x4
+enum {
+	TMF_INITIAL,
+	TMF_QUEUED,
+	TMF_SUCCESS,
+	TMF_FAILED,
+	TMF_TIMEDOUT,
+	TMF_NOT_FOUND,
+};
 
 /* Connection suspend "bit" */
 #define ISCSI_SUSPEND_BIT		1
@@ -91,7 +94,6 @@ enum {
 	ISCSI_TASK_COMPLETED,
 	ISCSI_TASK_PENDING,
 	ISCSI_TASK_RUNNING,
-	ISCSI_TASK_ABORTING,
 };
 
 struct iscsi_cmd_task {
@@ -110,7 +112,6 @@ struct iscsi_cmd_task {
 	unsigned		data_count;	/* remaining Data-Out */
 	struct scsi_cmnd	*sc;		/* associated SCSI cmd*/
 	struct iscsi_conn	*conn;		/* used connection    */
-	struct iscsi_mgmt_task	*mtask;		/* tmf mtask in progr */
 
 	/* state set/tested under session->lock */
 	int			state;
@@ -152,10 +153,11 @@ struct iscsi_conn {
 	struct iscsi_cmd_task	*ctask;		/* xmit ctask in progress */
 
 	/* xmit */
-	struct kfifo		*mgmtqueue;	/* mgmt (control) xmit queue */
+	struct list_head	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 */
 	unsigned long		suspend_tx;	/* suspend Tx */
 	unsigned long		suspend_rx;	/* suspend Rx */
@@ -163,8 +165,8 @@ struct iscsi_conn {
 	/* abort */
 	wait_queue_head_t	ehwait;		/* used in eh_abort() */
 	struct iscsi_tm		tmhdr;
-	struct timer_list	tmabort_timer;
-	int			tmabort_state;	/* see TMABORT_INITIAL, etc.*/
+	struct timer_list	tmf_timer;
+	int			tmf_state;	/* see TMF_INITIAL, etc.*/
 
 	/* negotiated params */
 	unsigned		max_recv_dlength; /* initiator_max_recv_dsl*/
@@ -231,6 +233,7 @@ struct iscsi_session {
 	int			pdu_inorder_en;
 	int			dataseq_inorder_en;
 	int			erl;
+	int			fast_abort;
 	int			tpgt;
 	char			*username;
 	char			*username_in;
@@ -268,6 +271,7 @@ 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 *));
 
@@ -326,6 +330,7 @@ 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);
 
 /*
  * generic helpers
Signature · 1 line
1.5.1.2
#2 lore raw

PATCH 2/24 iscsi_tcp: rewrite recv path

From: Mike Christie <[email protected]>

Quoted 1 line

From Olaf Kirch:

Rewrite recv path. Fixes:
- data digest processing and error handling.
- ahs support.

Some fixups by Mike Christie

---
 drivers/scsi/iscsi_tcp.c | 1018 +++++++++++++++++++++++-----------------------
 drivers/scsi/iscsi_tcp.h |   66 ++--
 include/scsi/libiscsi.h  |    4 +
 3 files changed, 552 insertions(+), 536 deletions(-)
drivers/scsi/iscsi_tcp.c +507 -511
index 4b226b8..1b540e0 100644
@@ -48,7 +48,7 @@ MODULE_AUTHOR("Dmitry Yusupov <[email protected]>, "
 	      "Alex Aizman <[email protected]>");
 MODULE_DESCRIPTION("iSCSI/TCP data-path");
 MODULE_LICENSE("GPL");
-/* #define DEBUG_TCP */
+#undef DEBUG_TCP
 #define DEBUG_ASSERT
 
 #ifdef DEBUG_TCP
@@ -67,10 +67,15 @@ 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_chunk *chunk);
+
 static inline void
 iscsi_buf_init_iov(struct iscsi_buf *ibuf, char *vbuf, int size)
 {
-	sg_init_one(&ibuf->sg, vbuf, size);
+	ibuf->sg.page = virt_to_page(vbuf);
+	ibuf->sg.offset = offset_in_page(vbuf);
+	ibuf->sg.length = size;
 	ibuf->sent = 0;
 	ibuf->use_sendmsg = 1;
 }
@@ -78,12 +83,13 @@ iscsi_buf_init_iov(struct iscsi_buf *ibuf, char *vbuf, int size)
 static inline void
 iscsi_buf_init_sg(struct iscsi_buf *ibuf, struct scatterlist *sg)
 {
-	sg_init_table(&ibuf->sg, 1);
-	sg_set_page(&ibuf->sg, sg_page(sg), sg->length, sg->offset);
+	ibuf->sg.page = sg->page;
+	ibuf->sg.offset = sg->offset;
+	ibuf->sg.length = sg->length;
 	/*
 	 * Fastpath: sg element fits into single page
 	 */
-	if (sg->length + sg->offset <= PAGE_SIZE && !PageSlab(sg_page(sg)))
+	if (sg->length + sg->offset <= PAGE_SIZE && !PageSlab(sg->page))
 		ibuf->use_sendmsg = 0;
 	else
 		ibuf->use_sendmsg = 1;
@@ -110,72 +116,331 @@ iscsi_hdr_digest(struct iscsi_conn *conn, struct iscsi_buf *buf,
 	buf->sg.length += sizeof(u32);
 }
 
+/*
+ * Scatterlist handling: inside the iscsi_chunk, 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_chunk_init_sg - init indicated scatterlist entry
+ * @chunk: the buffer object
+ * @idx: index into scatterlist
+ * @offset: byte offset into that sg entry
+ *
+ * This function sets up the chunk so that subsequent
+ * data is copied to the indicated sg entry, at the given
+ * offset.
+ */
+static inline void
+iscsi_tcp_chunk_init_sg(struct iscsi_chunk *chunk,
+			unsigned int idx, unsigned int offset)
+{
+	struct scatterlist *sg;
+
+	BUG_ON(chunk->sg == NULL);
+
+	sg = &chunk->sg[idx];
+	chunk->sg_index = idx;
+	chunk->sg_offset = offset;
+	chunk->size = min(sg->length - offset, chunk->total_size);
+	chunk->data = NULL;
+}
+
+/**
+ * iscsi_tcp_chunk_map - map the current S/G page
+ * @chunk: iscsi chunk
+ *
+ * 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_chunk_map(struct iscsi_chunk *chunk)
+{
+	struct scatterlist *sg;
+
+	if (chunk->data != NULL || !chunk->sg)
+		return;
+
+	sg = &chunk->sg[chunk->sg_index];
+	BUG_ON(chunk->sg_mapped);
+	BUG_ON(sg->length == 0);
+	chunk->sg_mapped = kmap_atomic(sg->page, KM_SOFTIRQ0);
+	chunk->data = chunk->sg_mapped + sg->offset + chunk->sg_offset;
+}
+
+static inline void
+iscsi_tcp_chunk_unmap(struct iscsi_chunk *chunk)
+{
+	if (chunk->sg_mapped) {
+		kunmap_atomic(chunk->sg_mapped, KM_SOFTIRQ0);
+		chunk->sg_mapped = NULL;
+		chunk->data = NULL;
+	}
+}
+
+/*
+ * Splice the digest buffer into the buffer
+ */
+static inline void
+iscsi_tcp_chunk_splice_digest(struct iscsi_chunk *chunk, void *digest)
+{
+	chunk->data = digest;
+	chunk->digest_len = ISCSI_DIGEST_SIZE;
+	chunk->total_size += ISCSI_DIGEST_SIZE;
+	chunk->size = ISCSI_DIGEST_SIZE;
+	chunk->copied = 0;
+	chunk->sg = NULL;
+	chunk->sg_index = 0;
+	chunk->hash = NULL;
+}
+
+/**
+ * iscsi_tcp_chunk_done - check whether the chunk is complete
+ * @chunk: iscsi chunk to check
+ *
+ * Check if we're done receiving this chunk. 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_hdr_extract(struct iscsi_tcp_conn *tcp_conn)
+iscsi_tcp_chunk_done(struct iscsi_chunk *chunk)
 {
-	struct sk_buff *skb = tcp_conn->in.skb;
+	static unsigned char padbuf[ISCSI_PAD_LEN];
+
+	if (chunk->copied < chunk->size) {
+		iscsi_tcp_chunk_map(chunk);
+		return 0;
+	}
 
-	tcp_conn->in.zero_copy_hdr = 0;
+	chunk->total_copied += chunk->copied;
+	chunk->copied = 0;
+	chunk->size = 0;
 
-	if (tcp_conn->in.copy >= tcp_conn->hdr_size &&
-	    tcp_conn->in_progress == IN_PROGRESS_WAIT_HEADER) {
-		/*
-		 * Zero-copy PDU Header: using connection context
-		 * to store header pointer.
-		 */
-		if (skb_shinfo(skb)->frag_list == NULL &&
-		    !skb_shinfo(skb)->nr_frags) {
-			tcp_conn->in.hdr = (struct iscsi_hdr *)
-				((char*)skb->data + tcp_conn->in.offset);
-			tcp_conn->in.zero_copy_hdr = 1;
-		} else {
-			/* ignoring return code since we checked
-			 * in.copy before */
-			skb_copy_bits(skb, tcp_conn->in.offset,
-				&tcp_conn->hdr, tcp_conn->hdr_size);
-			tcp_conn->in.hdr = &tcp_conn->hdr;
+	/* Unmap the current scatterlist page, if there is one. */
+	iscsi_tcp_chunk_unmap(chunk);
+
+	/* Do we have more scatterlist entries? */
+	if (chunk->total_copied < chunk->total_size) {
+		/* Proceed to the next entry in the scatterlist. */
+		iscsi_tcp_chunk_init_sg(chunk, chunk->sg_index + 1, 0);
+		iscsi_tcp_chunk_map(chunk);
+		BUG_ON(chunk->size == 0);
+		return 0;
+	}
+
+	/* Do we need to handle padding? */
+	if (chunk->total_copied & (ISCSI_PAD_LEN-1)) {
+		unsigned int pad;
+
+		pad = ISCSI_PAD_LEN - (chunk->total_copied & (ISCSI_PAD_LEN-1));
+		debug_tcp("consume %d pad bytes\n", pad);
+		chunk->total_size += pad;
+		chunk->size = pad;
+		chunk->data = padbuf;
+		return 0;
+	}
+
+	/*
+	 * Set us up for receiving the data digest. hdr digest
+	 * is completely handled in hdr done function.
+	 */
+	if (chunk->hash) {
+		if (chunk->digest_len == 0) {
+			crypto_hash_final(chunk->hash, chunk->digest);
+			iscsi_tcp_chunk_splice_digest(chunk,
+						      chunk->recv_digest);
+			return 0;
 		}
-		tcp_conn->in.offset += tcp_conn->hdr_size;
-		tcp_conn->in.copy -= tcp_conn->hdr_size;
-	} else {
-		int hdr_remains;
-		int copylen;
+	}
 
-		/*
-		 * PDU header scattered across SKB's,
-		 * copying it... This'll happen quite rarely.
-		 */
+	return 1;
+}
+
+/**
+ * iscsi_tcp_chunk_recv - copy data to chunk
+ * @tcp_conn: the iSCSI TCP connection
+ * @chunk: 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_chunk_recv(struct iscsi_tcp_conn *tcp_conn,
+		     struct iscsi_chunk *chunk, const void *ptr,
+		     unsigned int len)
+{
+	struct scatterlist sg;
+	unsigned int copy, copied = 0;
+
+	while (!iscsi_tcp_chunk_done(chunk)) {
+		if (copied == len)
+			goto out;
+
+		copy = min(len - copied, chunk->size - chunk->copied);
+		memcpy(chunk->data + chunk->copied, ptr + copied, copy);
+
+		if (chunk->hash) {
+			sg_init_one(&sg, ptr + copied, copy);
+			crypto_hash_update(chunk->hash, &sg, copy);
+		}
+		chunk->copied += copy;
+		copied += copy;
+	}
+
+out:
+	return copied;
+}
 
-		if (tcp_conn->in_progress == IN_PROGRESS_WAIT_HEADER)
-			tcp_conn->in.hdr_offset = 0;
+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;
+
+	sg_init_one(&sg, hdr, hdrlen);
+	crypto_hash_digest(hash, &sg, hdrlen, digest);
+}
+
+static inline int
+iscsi_tcp_dgst_verify(struct iscsi_tcp_conn *tcp_conn,
+		      struct iscsi_chunk *chunk)
+{
+	if (!chunk->digest_len)
+		return 1;
+
+	if (memcmp(chunk->recv_digest, chunk->digest, chunk->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 chunk buffer
+ */
+static inline void
+__iscsi_chunk_init(struct iscsi_chunk *chunk, size_t size,
+		   iscsi_chunk_done_fn_t *done, struct hash_desc *hash)
+{
+	memset(chunk, 0, sizeof(*chunk));
+	chunk->total_size = size;
+	chunk->done = done;
 
-		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);
+	if (hash) {
+		chunk->hash = hash;
+		crypto_hash_init(hash);
+	}
+}
 
-		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;
+static inline void
+iscsi_chunk_init_linear(struct iscsi_chunk *chunk, void *data, size_t size,
+			iscsi_chunk_done_fn_t *done, struct hash_desc *hash)
+{
+	__iscsi_chunk_init(chunk, size, done, hash);
+	chunk->data = data;
+	chunk->size = size;
+}
+
+static inline int
+iscsi_chunk_seek_sg(struct iscsi_chunk *chunk,
+		    struct scatterlist *sg, unsigned int sg_count,
+		    unsigned int offset, size_t size,
+		    iscsi_chunk_done_fn_t *done, struct hash_desc *hash)
+{
+	unsigned int i;
+
+	__iscsi_chunk_init(chunk, size, done, hash);
+	for (i = 0; i < sg_count; ++i) {
+		if (offset < sg[i].length) {
+			chunk->sg = sg;
+			chunk->sg_count = sg_count;
+			iscsi_tcp_chunk_init_sg(chunk, i, offset);
+			return 0;
 		}
-		tcp_conn->in.hdr = &tcp_conn->hdr;
-		tcp_conn->discontiguous_hdr_cnt++;
-	        tcp_conn->in_progress = IN_PROGRESS_WAIT_HEADER;
+		offset -= sg[i].length;
 	}
 
+	return ISCSI_ERR_DATA_OFFSET;
+}
+
+/**
+ * iscsi_tcp_hdr_recv_prep - prep chunk 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_chunk_init_linear(&tcp_conn->in.chunk,
+				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_chunk *chunk)
+{
+	struct iscsi_conn *conn = tcp_conn->iscsi_conn;
+	int rc = 0;
+
+	if (!iscsi_tcp_dgst_verify(tcp_conn, chunk))
+		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_chunk_init_linear(&tcp_conn->in.chunk,
+				conn->data, tcp_conn->in.datalen,
+				iscsi_tcp_data_recv_done, rx_hash);
+}
+
 /*
  * must be called with session lock
  */
@@ -417,16 +682,49 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
 	return 0;
 }
 
+/*
+ * Handle incoming reply to DataIn command
+ */
 static int
-iscsi_tcp_hdr_recv(struct iscsi_conn *conn)
+iscsi_tcp_process_data_in(struct iscsi_tcp_conn *tcp_conn,
+			  struct iscsi_chunk *chunk)
+{
+	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, chunk))
+		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;
+	}
+
+	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)
 {
 	int rc = 0, opcode, ahslen;
-	struct iscsi_hdr *hdr;
 	struct iscsi_session *session = conn->session;
 	struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
-	uint32_t cdgst, rdgst = 0, itt;
-
-	hdr = tcp_conn->in.hdr;
+	struct iscsi_cmd_task *ctask;
+	uint32_t itt;
 
 	/* verify PDU length */
 	tcp_conn->in.datalen = ntoh24(hdr->dlength);
@@ -435,77 +733,72 @@ iscsi_tcp_hdr_recv(struct iscsi_conn *conn)
 		       tcp_conn->in.datalen, conn->max_recv_dlength);
 		return ISCSI_ERR_DATALEN;
 	}
-	tcp_conn->data_copied = 0;
 
-	/* read AHS */
+	/* Additional header segments. So far, we don't
+	 * process additional headers.
+	 */
 	ahslen = hdr->hlength << 2;
-	tcp_conn->in.offset += ahslen;
-	tcp_conn->in.copy -= ahslen;
-	if (tcp_conn->in.copy < 0) {
-		printk(KERN_ERR "iscsi_tcp: can't handle AHS with length "
-		       "%d bytes\n", ahslen);
-		return ISCSI_ERR_AHSLEN;
-	}
-
-	/* calculate read padding */
-	tcp_conn->in.padding = tcp_conn->in.datalen & (ISCSI_PAD_LEN-1);
-	if (tcp_conn->in.padding) {
-		tcp_conn->in.padding = ISCSI_PAD_LEN - tcp_conn->in.padding;
-		debug_scsi("read padding %d bytes\n", tcp_conn->in.padding);
-	}
-
-	if (conn->hdrdgst_en) {
-		struct scatterlist sg;
-
-		sg_init_one(&sg, (u8 *)hdr,
-			    sizeof(struct iscsi_hdr) + ahslen);
-		crypto_hash_digest(&tcp_conn->rx_hash, &sg, sg.length,
-				   (u8 *)&cdgst);
-		rdgst = *(uint32_t*)((char*)hdr + sizeof(struct iscsi_hdr) +
-				     ahslen);
-		if (cdgst != rdgst) {
-			printk(KERN_ERR "iscsi_tcp: hdrdgst error "
-			       "recv 0x%x calc 0x%x\n", rdgst, cdgst);
-			return ISCSI_ERR_HDR_DGST;
-		}
-	}
 
 	opcode = hdr->opcode & ISCSI_OPCODE_MASK;
 	/* verify itt (itt encoding: age+cid+itt) */
 	rc = iscsi_verify_itt(conn, hdr, &itt);
 	if (rc == ISCSI_ERR_NO_SCSI_CMD) {
+		/* XXX: what does this do? */
 		tcp_conn->in.datalen = 0; /* force drop */
 		return 0;
 	} else if (rc)
 		return rc;
 
-	debug_tcp("opcode 0x%x offset %d copy %d ahslen %d datalen %d\n",
-		  opcode, tcp_conn->in.offset, tcp_conn->in.copy,
-		  ahslen, tcp_conn->in.datalen);
+	debug_tcp("opcode 0x%x ahslen %d datalen %d\n",
+		  opcode, ahslen, tcp_conn->in.datalen);
 
 	switch(opcode) {
 	case ISCSI_OP_SCSI_DATA_IN:
-		tcp_conn->in.ctask = session->cmds[itt];
-		rc = iscsi_data_rsp(conn, tcp_conn->in.ctask);
+		ctask = session->cmds[itt];
+		rc = iscsi_data_rsp(conn, 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;
+
+			/*
+			 * Setup copy of Data-In into the Scsi_Cmnd
+			 * Scatterlist case:
+			 * We set up the iscsi_chunk 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_chunk_seek_sg(&tcp_conn->in.chunk,
+						scsi_sglist(ctask->sc),
+						scsi_sg_count(ctask->sc),
+						tcp_ctask->data_offset,
+						tcp_conn->in.datalen,
+						iscsi_tcp_process_data_in,
+						rx_hash);
+		}
 		/* fall through */
 	case ISCSI_OP_SCSI_CMD_RSP:
-		tcp_conn->in.ctask = session->cmds[itt];
-		if (tcp_conn->in.datalen)
-			goto copy_hdr;
-
-		spin_lock(&session->lock);
-		rc = __iscsi_complete_pdu(conn, hdr, NULL, 0);
-		spin_unlock(&session->lock);
+		if (tcp_conn->in.datalen) {
+			iscsi_tcp_data_recv_prep(tcp_conn);
+			return 0;
+		}
+		rc = iscsi_complete_pdu(conn, hdr, NULL, 0);
 		break;
 	case ISCSI_OP_R2T:
-		tcp_conn->in.ctask = session->cmds[itt];
+		ctask = session->cmds[itt];
 		if (ahslen)
 			rc = ISCSI_ERR_AHSLEN;
-		else if (tcp_conn->in.ctask->sc->sc_data_direction ==
-								DMA_TO_DEVICE)
-			rc = iscsi_r2t_rsp(conn, tcp_conn->in.ctask);
+		else if (ctask->sc->sc_data_direction == DMA_TO_DEVICE)
+			rc = iscsi_r2t_rsp(conn, ctask);
 		else
 			rc = ISCSI_ERR_PROTO;
 		break;
@@ -518,8 +811,7 @@ iscsi_tcp_hdr_recv(struct iscsi_conn *conn)
 		 * 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) {
+		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,
@@ -528,8 +820,13 @@ iscsi_tcp_hdr_recv(struct iscsi_conn *conn)
 			break;
 		}
 
-		if (tcp_conn->in.datalen)
-			goto copy_hdr;
+		/* 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;
+		}
 	/* fall through */
 	case ISCSI_OP_LOGOUT_RSP:
 	case ISCSI_OP_NOOP_IN:
@@ -541,129 +838,15 @@ iscsi_tcp_hdr_recv(struct iscsi_conn *conn)
 		break;
 	}
 
-	return rc;
-
-copy_hdr:
-	/*
-	 * if we did zero copy for the header but we will need multiple
-	 * skbs to complete the command then we have to copy the header
-	 * for later use
-	 */
-	if (tcp_conn->in.zero_copy_hdr && tcp_conn->in.copy <=
-	   (tcp_conn->in.datalen + tcp_conn->in.padding +
-	    (conn->datadgst_en ? 4 : 0))) {
-		debug_tcp("Copying header for later use. in.copy %d in.datalen"
-			  " %d\n", tcp_conn->in.copy, tcp_conn->in.datalen);
-		memcpy(&tcp_conn->hdr, tcp_conn->in.hdr,
-		       sizeof(struct iscsi_hdr));
-		tcp_conn->in.hdr = &tcp_conn->hdr;
-		tcp_conn->in.zero_copy_hdr = 0;
-	}
-	return 0;
-}
-
-/**
- * iscsi_ctask_copy - copy skb bits to the destanation cmd task
- * @conn: iscsi tcp connection
- * @ctask: scsi command task
- * @buf: buffer to copy to
- * @buf_size: size of buffer
- * @offset: offset within the buffer
- *
- * Notes:
- *	The function calls skb_copy_bits() and updates per-connection and
- *	per-cmd byte counters.
- *
- *	Read counters (in bytes):
- *
- *	conn->in.offset		offset within in progress SKB
- *	conn->in.copy		left to copy from in progress SKB
- *				including padding
- *	conn->in.copied		copied already from in progress SKB
- *	conn->data_copied	copied already from in progress buffer
- *	ctask->sent		total bytes sent up to the MidLayer
- *	ctask->data_count	left to copy from in progress Data-In
- *	buf_left		left to copy from in progress buffer
- **/
-static inline int
-iscsi_ctask_copy(struct iscsi_tcp_conn *tcp_conn, struct iscsi_cmd_task *ctask,
-		void *buf, int buf_size, int offset)
-{
-	struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
-	int buf_left = buf_size - (tcp_conn->data_copied + offset);
-	unsigned 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 > scsi_bufflen(ctask->sc));
-
-	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;
+	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);
 	}
 
-	/* done with this buffer or with both - PDU and buffer */
-	tcp_conn->data_copied = 0;
-	return 0;
-}
-
-/**
- * iscsi_tcp_copy - copy skb bits to the destanation buffer
- * @conn: iscsi tcp connection
- *
- * Notes:
- *	The function calls skb_copy_bits() and updates per-connection
- *	byte counters.
- **/
-static inline int
-iscsi_tcp_copy(struct iscsi_conn *conn, int buf_size)
-{
-	struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
-	int buf_left = buf_size - tcp_conn->data_copied;
-	int size = min(tcp_conn->in.copy, buf_left);
-	int rc;
-
-	debug_tcp("tcp_copy %d bytes at offset %d copied %d\n",
-	       size, tcp_conn->in.offset, tcp_conn->data_copied);
-	BUG_ON(size <= 0);
-
-	rc = skb_copy_bits(tcp_conn->in.skb, tcp_conn->in.offset,
-			   (char*)conn->data + tcp_conn->data_copied, size);
-	BUG_ON(rc);
-
-	tcp_conn->in.offset += size;
-	tcp_conn->in.copy -= size;
-	tcp_conn->in.copied += size;
-	tcp_conn->data_copied += size;
-
-	if (buf_size != tcp_conn->data_copied)
-		return -EAGAIN;
-
-	return 0;
+	return rc;
 }
 
 static inline void
@@ -677,325 +860,146 @@ partial_sg_digest_update(struct hash_desc *desc, struct scatterlist *sg,
 	crypto_hash_update(desc, &temp, length);
 }
 
-static void
-iscsi_recv_digest_update(struct iscsi_tcp_conn *tcp_conn, char* buf, int len)
-{
-	struct scatterlist tmp;
-
-	sg_init_one(&tmp, buf, len);
-	crypto_hash_update(&tcp_conn->rx_hash, &tmp, len);
-}
-
-static int iscsi_scsi_data_in(struct iscsi_conn *conn)
+/**
+ * iscsi_tcp_hdr_recv_done - process PDU header
+ *
+ * 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_chunk *chunk)
 {
-	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);
-
-	offset = tcp_ctask->data_offset;
-	sg = scsi_sglist(sc);
-
-	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 < scsi_sg_count(sc); i++) {
-		char *dest;
-
-		dest = kmap_atomic(sg_page(&sg[i]), KM_SOFTIRQ0);
-		rc = iscsi_ctask_copy(tcp_conn, ctask, dest + sg[i].offset,
-				      sg[i].length, offset);
-		kunmap_atomic(dest, KM_SOFTIRQ0);
-		if (rc == -EAGAIN)
-			/* continue with the next SKB/PDU */
-			return rc;
-		if (!rc) {
-			if (conn->datadgst_en) {
-				if (!offset)
-					crypto_hash_update(
-							&tcp_conn->rx_hash,
-							&sg[i], sg[i].length);
-				else
-					partial_sg_digest_update(
-							&tcp_conn->rx_hash,
-							&sg[i],
-							sg[i].offset + offset,
-							sg[i].length - offset);
-			}
-			offset = 0;
-			tcp_ctask->sg_count++;
-		}
-
-		if (!ctask->data_count) {
-			if (rc && conn->datadgst_en)
-				/*
-				 * data-in is complete, but buffer not...
-				 */
-				partial_sg_digest_update(&tcp_conn->rx_hash,
-							 &sg[i],
-							 sg[i].offset,
-							 sg[i].length-rc);
-			rc = 0;
-			break;
-		}
-
-		if (!tcp_conn->in.copy)
-			return -EAGAIN;
-	}
-	BUG_ON(ctask->data_count);
+	struct iscsi_conn *conn = tcp_conn->iscsi_conn;
+	struct iscsi_hdr *hdr;
 
-	/* 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);
+	/* Check if there are additional header segments
+	 * *prior* to computing the digest, because we
+	 * may need to go back to the caller for more.
+	 */
+	hdr = (struct iscsi_hdr *) tcp_conn->in.hdr_buf;
+	if (chunk->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;
+
+		chunk->total_size += ahslen;
+		chunk->size += ahslen;
+		return 0;
 	}
 
-	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;
+	/* 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 (chunk->digest_len == 0) {
+			iscsi_tcp_chunk_splice_digest(chunk,
+						      chunk->recv_digest);
+			return 0;
 		}
+		iscsi_tcp_dgst_header(&tcp_conn->rx_hash, hdr,
+				      chunk->total_copied - ISCSI_DIGEST_SIZE,
+				      chunk->digest);
 
-		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);
+		if (!iscsi_tcp_dgst_verify(tcp_conn, chunk))
+			return ISCSI_ERR_HDR_DGST;
 	}
-exit:
-	return rc;
+
+	tcp_conn->in.hdr = hdr;
+	return iscsi_tcp_hdr_dissect(conn, hdr);
 }
 
 /**
- * iscsi_tcp_data_recv - TCP receive in sendfile fashion
+ * iscsi_tcp_recv - TCP receive in sendfile fashion
  * @rd_desc: read descriptor
  * @skb: socket buffer
  * @offset: offset in skb
  * @len: skb->len - offset
  **/
 static int
-iscsi_tcp_data_recv(read_descriptor_t *rd_desc, struct sk_buff *skb,
-		unsigned int offset, size_t len)
+iscsi_tcp_recv(read_descriptor_t *rd_desc, struct sk_buff *skb,
+	       unsigned int offset, size_t len)
 {
-	int rc;
 	struct iscsi_conn *conn = rd_desc->arg.data;
 	struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
-	int processed;
-	char pad[ISCSI_PAD_LEN];
-	struct scatterlist sg;
-
-	/*
-	 * Save current SKB and its offset in the corresponding
-	 * connection context.
-	 */
-	tcp_conn->in.copy = skb->len - offset;
-	tcp_conn->in.offset = offset;
-	tcp_conn->in.skb = skb;
-	tcp_conn->in.len = tcp_conn->in.copy;
-	BUG_ON(tcp_conn->in.copy <= 0);
-	debug_tcp("in %d bytes\n", tcp_conn->in.copy);
+	struct iscsi_chunk *chunk = &tcp_conn->in.chunk;
+	struct skb_seq_state seq;
+	unsigned int consumed = 0;
+	int rc = 0;
 
-more:
-	tcp_conn->in.copied = 0;
-	rc = 0;
+	debug_tcp("in %d bytes\n", skb->len - offset);
 
 	if (unlikely(conn->suspend_rx)) {
 		debug_tcp("conn %d Rx suspended!\n", conn->id);
 		return 0;
 	}
 
-	if (tcp_conn->in_progress == IN_PROGRESS_WAIT_HEADER ||
-	    tcp_conn->in_progress == IN_PROGRESS_HEADER_GATHER) {
-		rc = iscsi_hdr_extract(tcp_conn);
-		if (rc) {
-		       if (rc == -EAGAIN)
-				goto nomore;
-		       else {
-				iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
-				return 0;
-		       }
-		}
-
-		/*
-		 * Verify and process incoming PDU header.
-		 */
-		rc = iscsi_tcp_hdr_recv(conn);
-		if (!rc && tcp_conn->in.datalen) {
-			if (conn->datadgst_en)
-				crypto_hash_init(&tcp_conn->rx_hash);
-			tcp_conn->in_progress = IN_PROGRESS_DATA_RECV;
-		} else if (rc) {
-			iscsi_conn_failure(conn, rc);
-			return 0;
-		}
-	}
-
-	if (tcp_conn->in_progress == IN_PROGRESS_DDIGEST_RECV &&
-	    tcp_conn->in.copy) {
-		uint32_t recv_digest;
+	skb_prepare_seq_read(skb, offset, skb->len, &seq);
+	while (1) {
+		unsigned int avail;
+		const u8 *ptr;
 
-		debug_tcp("extra data_recv offset %d copy %d\n",
-			  tcp_conn->in.offset, tcp_conn->in.copy);
-
-		if (!tcp_conn->data_copied) {
-			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);
+		avail = skb_seq_read(consumed, &ptr, &seq);
+		if (avail == 0)
+			break;
+		BUG_ON(chunk->copied >= chunk->size);
+
+		debug_tcp("skb %p ptr=%p avail=%u\n", skb, ptr, avail);
+		rc = iscsi_tcp_chunk_recv(tcp_conn, chunk, ptr, avail);
+		BUG_ON(rc == 0);
+		consumed += rc;
+
+		if (chunk->total_copied >= chunk->total_size) {
+			rc = chunk->done(tcp_conn, chunk);
+			if (rc != 0) {
+				skb_abort_seq_read(&seq);
+				goto error;
 			}
-			crypto_hash_final(&tcp_conn->rx_hash,
-					  (u8 *) &tcp_conn->in.datadgst);
-			debug_tcp("rx digest 0x%x\n", tcp_conn->in.datadgst);
-		}
-
-		rc = iscsi_tcp_copy(conn, sizeof(uint32_t));
-		if (rc) {
-			if (rc == -EAGAIN)
-				goto again;
-			iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
-			return 0;
-		}
-
-		memcpy(&recv_digest, conn->data, sizeof(uint32_t));
-		if (recv_digest != tcp_conn->in.datadgst) {
-			debug_tcp("iscsi_tcp: data digest error!"
-				  "0x%x != 0x%x\n", recv_digest,
-				  tcp_conn->in.datadgst);
-			iscsi_conn_failure(conn, ISCSI_ERR_DATA_DGST);
-			return 0;
-		} else {
-			debug_tcp("iscsi_tcp: data digest match!"
-				  "0x%x == 0x%x\n", recv_digest,
-				  tcp_conn->in.datadgst);
-			tcp_conn->in_progress = IN_PROGRESS_WAIT_HEADER;
-		}
-	}
-
-	if (tcp_conn->in_progress == IN_PROGRESS_DATA_RECV &&
-	    tcp_conn->in.copy) {
-		debug_tcp("data_recv offset %d copy %d\n",
-		       tcp_conn->in.offset, tcp_conn->in.copy);
 
-		rc = iscsi_data_recv(conn);
-		if (rc) {
-			if (rc == -EAGAIN)
-				goto again;
-			iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
-			return 0;
+			/* The done() functions sets up the
+			 * next chunk. */
 		}
-
-		if (tcp_conn->in.padding)
-			tcp_conn->in_progress = IN_PROGRESS_PAD_RECV;
-		else if (conn->datadgst_en)
-			tcp_conn->in_progress = IN_PROGRESS_DDIGEST_RECV;
-		else
-			tcp_conn->in_progress = IN_PROGRESS_WAIT_HEADER;
-		tcp_conn->data_copied = 0;
-	}
-
-	if (tcp_conn->in_progress == IN_PROGRESS_PAD_RECV &&
-	    tcp_conn->in.copy) {
-		int copylen = min(tcp_conn->in.padding - tcp_conn->data_copied,
-				  tcp_conn->in.copy);
-
-		tcp_conn->in.copy -= copylen;
-		tcp_conn->in.offset += copylen;
-		tcp_conn->data_copied += copylen;
-
-		if (tcp_conn->data_copied != tcp_conn->in.padding)
-			tcp_conn->in_progress = IN_PROGRESS_PAD_RECV;
-		else if (conn->datadgst_en)
-			tcp_conn->in_progress = IN_PROGRESS_DDIGEST_RECV;
-		else
-			tcp_conn->in_progress = IN_PROGRESS_WAIT_HEADER;
-		tcp_conn->data_copied = 0;
 	}
 
-	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);
+	conn->rxdata_octets += consumed;
+	return consumed;
 
-	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;
+error:
+	debug_tcp("Error receiving PDU, errno=%d\n", rc);
+	iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
+	return 0;
 }
 
 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_data_recv.
+	 * Use rd_desc to pass 'conn' to iscsi_tcp_recv.
 	 * We set count to 1 because we want the network layer to
-	 * hand us all the skbs that are available. iscsi_tcp_data_recv
+	 * hand us all the skbs that are available. iscsi_tcp_recv
 	 * handled pdus that cross buffers or pdus that still need data.
 	 */
 	rd_desc.arg.data = conn;
 	rd_desc.count = 1;
-	tcp_read_sock(sk, &rd_desc, iscsi_tcp_data_recv);
+	tcp_read_sock(sk, &rd_desc, iscsi_tcp_recv);
 
 	read_unlock(&sk->sk_callback_lock);
+
+	/* If we had to (atomically) map a highmem page,
+	 * unmap it now. */
+	iscsi_tcp_chunk_unmap(&tcp_conn->in.chunk);
 }
 
 static void
@@ -1097,9 +1101,9 @@ iscsi_send(struct iscsi_conn *conn, struct iscsi_buf *buf, int size, int flags)
 	 * slab case.
 	 */
 	if (buf->use_sendmsg)
-		res = sock_no_sendpage(sk, sg_page(&buf->sg), offset, size, flags);
+		res = sock_no_sendpage(sk, buf->sg.page, offset, size, flags);
 	else
-		res = tcp_conn->sendpage(sk, sg_page(&buf->sg), offset, size, flags);
+		res = tcp_conn->sendpage(sk, buf->sg.page, offset, size, flags);
 
 	if (res >= 0) {
 		conn->txdata_octets += res;
@@ -1783,9 +1787,6 @@ 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);
@@ -1862,11 +1863,9 @@ static void
 iscsi_tcp_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
 {
 	struct iscsi_conn *conn = cls_conn->dd_data;
-	struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
 
 	iscsi_conn_stop(cls_conn, flag);
 	iscsi_tcp_release_conn(conn);
-	tcp_conn->hdr_size = sizeof(struct iscsi_hdr);
 }
 
 static int iscsi_tcp_get_addr(struct iscsi_conn *conn, struct socket *sock,
@@ -1966,7 +1965,7 @@ iscsi_tcp_conn_bind(struct iscsi_cls_session *cls_session,
 	/*
 	 * set receive state machine into initial state
 	 */
-	tcp_conn->in_progress = IN_PROGRESS_WAIT_HEADER;
+	iscsi_tcp_hdr_recv_prep(tcp_conn);
 	return 0;
 
 free_socket:
@@ -2059,9 +2058,6 @@ 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);
drivers/scsi/iscsi_tcp.h +41 -25

95 lines not on this page · raw message

include/scsi/libiscsi.h +4 -0
index 89429f4..e1fb3d0 100644
@@ -77,6 +77,10 @@ enum {
 
 #define ISCSI_ADDRESS_BUF_LEN		64
 
+enum {
+	ISCSI_DIGEST_SIZE = sizeof(__u32),
+};
+
 struct iscsi_mgmt_task {
 	/*
 	 * Becuae LLDs allocate their hdr differently, this is a pointer to
Signature · 1 line
1.5.1.2
#3 lore raw

PATCH 3/24 Prettify resid handling and some extra checks

From: Mike Christie <[email protected]>

from Boaz Harrosh:
- Check to see that OVERFLOW is not negative indicating a bug.
- Unify handling of UNDERFLOW and OVERFLOW to the same code.
- Also handle BIDI_OVERFLOW.

---
 drivers/scsi/iscsi_tcp.c |   16 +++++++---------
 drivers/scsi/libiscsi.c  |   12 +++++++-----
 2 files changed, 14 insertions(+), 14 deletions(-)
drivers/scsi/iscsi_tcp.c +7 -9
index 1b540e0..fd88777 100644
@@ -507,22 +507,20 @@ iscsi_data_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
 	}
 
 	if (rhdr->flags & ISCSI_FLAG_DATA_STATUS) {
+		sc->result = (DID_OK << 16) | rhdr->cmd_status;
 		conn->exp_statsn = be32_to_cpu(rhdr->statsn) + 1;
-		if (rhdr->flags & ISCSI_FLAG_DATA_UNDERFLOW) {
+		if (rhdr->flags & (ISCSI_FLAG_DATA_UNDERFLOW |
+		                   ISCSI_FLAG_DATA_OVERFLOW)) {
 			int res_count = be32_to_cpu(rhdr->residual_count);
 
 			if (res_count > 0 &&
-			    res_count <= scsi_bufflen(sc)) {
+			    (rhdr->flags & ISCSI_FLAG_CMD_OVERFLOW ||
+			     res_count <= scsi_bufflen(sc)))
 				scsi_set_resid(sc, res_count);
-				sc->result = (DID_OK << 16) | rhdr->cmd_status;
-			} else
+			else
 				sc->result = (DID_BAD_TARGET << 16) |
 					rhdr->cmd_status;
-		} else if (rhdr->flags & ISCSI_FLAG_DATA_OVERFLOW) {
-			scsi_set_resid(sc, 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++;
drivers/scsi/libiscsi.c +7 -5
index 176458f..0beb4c6 100644
@@ -291,17 +291,19 @@ invalid_datalen:
 			   min_t(uint16_t, senselen, SCSI_SENSE_BUFFERSIZE));
 	}
 
-	if (rhdr->flags & ISCSI_FLAG_CMD_UNDERFLOW) {
+	if (rhdr->flags & (ISCSI_FLAG_CMD_UNDERFLOW |
+	                   ISCSI_FLAG_CMD_OVERFLOW)) {
 		int res_count = be32_to_cpu(rhdr->residual_count);
 
-		if (res_count > 0 && res_count <= scsi_bufflen(sc))
+		if (res_count > 0 &&
+		    (rhdr->flags & ISCSI_FLAG_CMD_OVERFLOW ||
+		     res_count <= scsi_bufflen(sc)))
 			scsi_set_resid(sc, res_count);
 		else
 			sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status;
-	} else if (rhdr->flags & ISCSI_FLAG_CMD_BIDI_UNDERFLOW)
+	} else if (rhdr->flags & (ISCSI_FLAG_CMD_BIDI_UNDERFLOW |
+	                          ISCSI_FLAG_CMD_BIDI_OVERFLOW))
 		sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status;
-	else if (rhdr->flags & ISCSI_FLAG_CMD_OVERFLOW)
-		scsi_set_resid(sc, be32_to_cpu(rhdr->residual_count));
 
 out:
 	debug_scsi("done [sc %lx res %d itt 0x%x]\n",
Signature · 1 line
1.5.1.2
#4 lore raw

PATCH 4/24 iscsi_tcp, libiscsi: initial AHS Support

From: Mike Christie <[email protected]>

at libiscsi generic code
- currently code assumes a storage space of pdu header is allocated at llds ctask and is pointed to by iscsi_cmd_task->hdr. Here I add a hdr_max field pertaining to that storage, and an hdr_len that accumulates the current use of the pdu-header.

- Add an iscsi_next_hdr() inline which returns the next free space to write new Header at. Also iscsi_next_hdr() is used to retrieve the address at which to write the header-digest.

- Add iscsi_add_hdr(length). What the user do is calls iscsi_next_hdr() for address of the new header, than calls iscsi_add_hdr(length) with the size of the new header. iscsi_add_hdr() will check if space is available and update to the new size. length must be padded according to standard.

- Add 2 padding inline helpers thanks to Olaf. Current patch does not use them but Following patches will.
Also moved definition of ISCSI_PAD_LEN to iscsi_proto.h which had PAD_WORD_LEN that was never used anywhere.

- Let iscsi_prep_scsi_cmd_pdu() signal an Error return since now it is possible that it will fail.

- I was tired of yet again writing a "this is a digest" comment next to sizeof(__u32) so I defined a new ISCSI_DIGEST_SIZE. Now I don't need any comments. Changed all places that used sizeof(__u32) or "4" in connection to a digest.

iscsi_tcp specific code
- At struct iscsi_tcp_cmd_task allocate maximum space allowed in standard for all headers following the iscsi_cmd header. and mark it so in iscsi_tcp_session_create()
- At iscsi_send_cmd_hdr() retrieve the correct headers size and write header digest at iscsi_next_hdr().

---
 drivers/scsi/iscsi_tcp.c   |   16 ++++++++--------
 drivers/scsi/iscsi_tcp.h   |   13 +++++++------
 drivers/scsi/libiscsi.c    |   41 +++++++++++++++++++++++++++++++++++++++--
 include/scsi/iscsi_proto.h |   10 +++++++++-
 include/scsi/libiscsi.h    |   33 +++++++++++++++++++++++++++++++--
 5 files changed, 94 insertions(+), 19 deletions(-)
drivers/scsi/iscsi_tcp.c +8 -8
index fd88777..491845f 100644
@@ -113,7 +113,7 @@ iscsi_hdr_digest(struct iscsi_conn *conn, struct iscsi_buf *buf,
 	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 += sizeof(u32);
+	buf->sg.length += ISCSI_DIGEST_SIZE;
 }
 
 /*
@@ -220,6 +220,7 @@ static inline int
 iscsi_tcp_chunk_done(struct iscsi_chunk *chunk)
 {
 	static unsigned char padbuf[ISCSI_PAD_LEN];
+	unsigned int pad;
 
 	if (chunk->copied < chunk->size) {
 		iscsi_tcp_chunk_map(chunk);
@@ -243,10 +244,8 @@ iscsi_tcp_chunk_done(struct iscsi_chunk *chunk)
 	}
 
 	/* Do we need to handle padding? */
-	if (chunk->total_copied & (ISCSI_PAD_LEN-1)) {
-		unsigned int pad;
-
-		pad = ISCSI_PAD_LEN - (chunk->total_copied & (ISCSI_PAD_LEN-1));
+	pad = iscsi_padding(chunk->total_copied);
+	if (pad != 0) {
 		debug_tcp("consume %d pad bytes\n", pad);
 		chunk->total_size += pad;
 		chunk->size = pad;
@@ -1385,11 +1384,11 @@ iscsi_send_cmd_hdr(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
 		}
 
 		iscsi_buf_init_iov(&tcp_ctask->headbuf, (char*)ctask->hdr,
-				  sizeof(struct iscsi_hdr));
+				  ctask->hdr_len);
 
 		if (conn->hdrdgst_en)
 			iscsi_hdr_digest(conn, &tcp_ctask->headbuf,
-					 (u8*)tcp_ctask->hdrext);
+					 iscsi_next_hdr(ctask));
 		tcp_ctask->xmstate &= ~XMSTATE_CMD_HDR_INIT;
 		tcp_ctask->xmstate |= XMSTATE_CMD_HDR_XMIT;
 	}
@@ -2176,7 +2175,8 @@ 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;
+		ctask->hdr = &tcp_ctask->hdr.cmd_hdr;
+		ctask->hdr_max = sizeof(tcp_ctask->hdr) - ISCSI_DIGEST_SIZE;
 	}
 
 	for (cmd_i = 0; cmd_i < session->mgmtpool_max; cmd_i++) {
drivers/scsi/iscsi_tcp.h +7 -6
index f1c5411..eb3784f 100644
@@ -41,7 +41,6 @@
 #define XMSTATE_IMM_HDR_INIT		0x1000
 #define XMSTATE_SOL_HDR_INIT		0x2000
 
-#define ISCSI_PAD_LEN			4
 #define ISCSI_SG_TABLESIZE		SG_ALL
 #define ISCSI_TCP_MAX_CMD_LEN		16
 
@@ -130,14 +129,14 @@ struct iscsi_buf {
 
 struct iscsi_data_task {
 	struct iscsi_data	hdr;			/* PDU */
-	char			hdrext[sizeof(__u32)];	/* Header-Digest */
+	char			hdrext[ISCSI_DIGEST_SIZE];/* Header-Digest */
 	struct iscsi_buf	digestbuf;		/* digest buffer */
 	uint32_t		digest;			/* data digest */
 };
 
 struct iscsi_tcp_mgmt_task {
 	struct iscsi_hdr	hdr;
-	char			hdrext[sizeof(__u32)]; /* Header-Digest */
+	char			hdrext[ISCSI_DIGEST_SIZE]; /* Header-Digest */
 	int			xmstate;	/* mgmt xmit progress */
 	struct iscsi_buf	headbuf;	/* header buffer */
 	struct iscsi_buf	sendbuf;	/* in progress buffer */
@@ -159,9 +158,11 @@ struct iscsi_r2t_info {
 };
 
 struct iscsi_tcp_cmd_task {
-	struct iscsi_cmd	hdr;
-	char			hdrext[4*sizeof(__u16)+	/* AHS */
-				    sizeof(__u32)];	/* HeaderDigest */
+	struct iscsi_hdr_buff {
+		struct iscsi_cmd	cmd_hdr;
+		char			hdrextbuf[ISCSI_MAX_AHS_SIZE +
+		                                  ISCSI_DIGEST_SIZE];
+	} hdr;
 	char			pad[ISCSI_PAD_LEN];
 	int			pad_count;		/* padded bytes */
 	struct iscsi_buf	headbuf;		/* header buf (xmit) */
drivers/scsi/libiscsi.c +39 -2

89 lines not on this page · raw message

include/scsi/iscsi_proto.h +9 -1
index 751c81e..6947082 100644
@@ -27,7 +27,7 @@
 #define ISCSI_LISTEN_PORT	3260
 
 /* Padding word length */
-#define PAD_WORD_LEN		4
+#define ISCSI_PAD_LEN		4
 
 /*
  * useful common(control and data pathes) macro
@@ -147,6 +147,14 @@ 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;
+	uint8_t ecdb[260 - 16];	/* 4-byte aligned extended CDB spillover */
+};
+
 /* SCSI Response Header */
 struct iscsi_cmd_rsp {
 	uint8_t opcode;
include/scsi/libiscsi.h +31 -2
index e1fb3d0..a9a9e86 100644
@@ -78,6 +78,9 @@ enum {
 #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),
 };
 
@@ -102,10 +105,13 @@ enum {
 
 struct iscsi_cmd_task {
 	/*
-	 * Becuae LLDs allocate their hdr differently, this is a pointer to
-	 * that storage. It must be setup at session creation time.
+	 * Because LLDs allocate their hdr differently, this is a pointer
+	 * and length 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 */
 
 	uint32_t		unsol_datasn;
@@ -124,6 +130,11 @@ 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;
+}
+
 struct iscsi_conn {
 	struct iscsi_cls_conn	*cls_conn;	/* ptr to class connection */
 	void			*dd_data;	/* iscsi_transport data */
@@ -342,4 +353,22 @@ extern void iscsi_requeue_ctask(struct iscsi_cmd_task *ctask);
 extern void iscsi_pool_free(struct iscsi_queue *, void **);
 extern int iscsi_pool_init(struct iscsi_queue *, 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;
+}
+
 #endif
Signature · 1 line
1.5.1.2
#5 lore raw

PATCH 5/24 iser patching for AHS support

From: Mike Christie <[email protected]>

from Boaz Harrosh <[email protected]>

- The default initialization of hdr_max is the minimum - sizeof(struct iscsi_cmd) - Once this patch goes into iser the default initialization at libiscsi can be removed.
- This is not yet full support for AHSs at iser end. But it should be easy. Just allocate more space at iser_desc right after iscsi_hdr. Than at transmission time use ctask->hdr_len to retrieve the total size of all iscsi pdu headers. See previous patch at iscsi_tcp.[ch]

---
 drivers/infiniband/ulp/iser/iscsi_iser.c |    1 +
 drivers/scsi/libiscsi.c                  |    1 -
 2 files changed, 1 insertions(+), 1 deletions(-)
drivers/infiniband/ulp/iser/iscsi_iser.c +1 -0
index 2eadb6d..a2622f4 100644
@@ -400,6 +400,7 @@ 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++) {
drivers/scsi/libiscsi.c +0 -1
index 0d7914f..5936586 100644
@@ -1570,7 +1570,6 @@ iscsi_session_setup(struct iscsi_transport *iscsit,
 		if (cmd_task_size)
 			ctask->dd_data = &ctask[1];
 		ctask->itt = cmd_i;
-		ctask->hdr_max = sizeof(struct iscsi_cmd);
 		INIT_LIST_HEAD(&ctask->running);
 	}
 
Signature · 1 line
1.5.1.2
#6 lore raw

PATCH 6/24 libiscsi, iscsi_tcp: iscsi pool cleanup

From: Mike Christie <[email protected]>

from [email protected]

iscsi_pool_init simplified

iscsi_pool_init currently has a lot of duplicate kfree() calls it does when some allocation fails. This patch simplifies the code a little by using iscsi_pool_free to tear down the pool in case of an error.

iscsi_pool_init also returns a copy of the item array to the caller. Not all callers use this array, so we make it optional.

Instead of allocating a second array and return that, allocate just one array, of twice the size.

Update users of iscsi_pool_{init,free}

This patch drops the (now useless) second argument to iscsi_pool_free, and updates all callers.

It also removes the ctask->r2ts array, which was never used anyway. Since the items argument to iscsi_pool_init is now optional, we can pass NULL instead.

---
 drivers/scsi/iscsi_tcp.c |   12 ++-----
 drivers/scsi/iscsi_tcp.h |    3 +-
 drivers/scsi/libiscsi.c  |   75 ++++++++++++++++++++++++---------------------
 include/scsi/libiscsi.h  |   10 +++---
 4 files changed, 50 insertions(+), 50 deletions(-)
drivers/scsi/iscsi_tcp.c +4 -8
index 491845f..f79a457 100644
@@ -1998,8 +1998,7 @@ iscsi_r2tpool_alloc(struct iscsi_session *session)
 		 */
 
 		/* R2T pool */
-		if (iscsi_pool_init(&tcp_ctask->r2tpool, session->max_r2t * 4,
-				    (void***)&tcp_ctask->r2ts,
+		if (iscsi_pool_init(&tcp_ctask->r2tpool, session->max_r2t * 4, NULL,
 				    sizeof(struct iscsi_r2t_info))) {
 			goto r2t_alloc_fail;
 		}
@@ -2008,8 +2007,7 @@ 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,
-					(void**)tcp_ctask->r2ts);
+			iscsi_pool_free(&tcp_ctask->r2tpool);
 			goto r2t_alloc_fail;
 		}
 	}
@@ -2022,8 +2020,7 @@ r2t_alloc_fail:
 		struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
 
 		kfifo_free(tcp_ctask->r2tqueue);
-		iscsi_pool_free(&tcp_ctask->r2tpool,
-				(void**)tcp_ctask->r2ts);
+		iscsi_pool_free(&tcp_ctask->r2tpool);
 	}
 	return -ENOMEM;
 }
@@ -2038,8 +2035,7 @@ 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,
-				(void**)tcp_ctask->r2ts);
+		iscsi_pool_free(&tcp_ctask->r2tpool);
 	}
 }
 
drivers/scsi/iscsi_tcp.h +1 -2
index eb3784f..d49d876 100644
@@ -175,9 +175,8 @@ struct iscsi_tcp_cmd_task {
 	uint32_t		exp_datasn;		/* expected target's R2TSN/DataSN */
 	int			data_offset;
 	struct iscsi_r2t_info	*r2t;			/* in progress R2T    */
-	struct iscsi_queue	r2tpool;
+	struct iscsi_pool	r2tpool;
 	struct kfifo		*r2tqueue;
-	struct iscsi_r2t_info	**r2ts;
 	int			digest_count;
 	uint32_t		immdigest;		/* for imm data */
 	struct iscsi_buf	immbuf;			/* for imm data digest */
drivers/scsi/libiscsi.c +40 -35

116 lines not on this page · raw message

include/scsi/libiscsi.h +5 -5
index a9a9e86..4b3e3c1 100644
@@ -215,7 +215,7 @@ struct iscsi_conn {
 	uint32_t		eh_abort_cnt;
 };
 
-struct iscsi_queue {
+struct iscsi_pool {
 	struct kfifo		*queue;		/* FIFO Queue */
 	void			**pool;		/* Pool of elements */
 	int			max;		/* Max number of elements */
@@ -274,10 +274,10 @@ struct iscsi_session {
 
 	int			cmds_max;	/* size of cmds array */
 	struct iscsi_cmd_task	**cmds;		/* Original Cmds arr */
-	struct iscsi_queue	cmdpool;	/* PDU's pool */
+	struct iscsi_pool	cmdpool;	/* PDU's pool */
 	int			mgmtpool_max;	/* size of mgmt array */
 	struct iscsi_mgmt_task	**mgmt_cmds;	/* Original mgmt arr */
-	struct iscsi_queue	mgmtpool;	/* Mgmt PDU's pool */
+	struct iscsi_pool	mgmtpool;	/* Mgmt PDU's pool */
 };
 
 /*
@@ -350,8 +350,8 @@ extern void iscsi_requeue_ctask(struct iscsi_cmd_task *ctask);
 /*
  * generic helpers
  */
-extern void iscsi_pool_free(struct iscsi_queue *, void **);
-extern int iscsi_pool_init(struct iscsi_queue *, int, void ***, int);
+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.
Signature · 1 line
1.5.1.2
#7 lore raw

PATCH 7/24 libiscsi: do not block session during logout

From: Mike Christie <[email protected]>

There is not need to block the session during logout. Since we are going to fail the commands that were blocked just fail them immediately instead.

---
 drivers/infiniband/ulp/iser/iser_initiator.c |    4 +-
 drivers/scsi/iscsi_tcp.c                     |    4 +-
 drivers/scsi/libiscsi.c                      |  153 ++++++++++++++------------
 include/scsi/libiscsi.h                      |    2 +
 include/scsi/scsi_transport_iscsi.h          |    1 +
 5 files changed, 88 insertions(+), 76 deletions(-)
drivers/infiniband/ulp/iser/iser_initiator.c +1 -3
index a6f2303..47f716c 100644
@@ -621,9 +621,7 @@ void iser_snd_completion(struct iser_desc *tx_desc)
 			struct iscsi_session *session = conn->session;
 
 			spin_lock(&conn->session->lock);
-			list_del(&mtask->running);
-			__kfifo_put(session->mgmtpool.queue, (void*)&mtask,
-				    sizeof(void*));
+			iscsi_free_mgmt_task(conn, mtask);
 			spin_unlock(&session->lock);
 		}
 	}
drivers/scsi/iscsi_tcp.c +1 -3
index f79a457..90eae8e 100644
@@ -1349,9 +1349,7 @@ iscsi_tcp_mtask_xmit(struct iscsi_conn *conn, struct iscsi_mgmt_task *mtask)
 		struct iscsi_session *session = conn->session;
 
 		spin_lock_bh(&session->lock);
-		list_del(&conn->mtask->running);
-		__kfifo_put(session->mgmtpool.queue, (void*)&conn->mtask,
-			    sizeof(void*));
+		iscsi_free_mgmt_task(conn, mtask);
 		spin_unlock_bh(&session->lock);
 	}
 	return 0;
drivers/scsi/libiscsi.c +83 -70

251 lines not on this page · raw message

include/scsi/libiscsi.h +2 -0
index 4b3e3c1..d68f745 100644
@@ -346,6 +346,8 @@ extern int __iscsi_complete_pdu(struct iscsi_conn *, struct iscsi_hdr *,
 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
include/scsi/scsi_transport_iscsi.h +1 -0
index 7ff6199..b8d97bd 100644
@@ -176,6 +176,7 @@ struct iscsi_cls_conn {
 #define ISCSI_STATE_TERMINATE		4
 #define ISCSI_STATE_IN_RECOVERY		5
 #define ISCSI_STATE_RECOVERY_FAILED	6
+#define ISCSI_STATE_LOGGING_OUT		7
 
 struct iscsi_cls_session {
 	struct list_head sess_list;		/* item in session_list */
Signature · 1 line
1.5.1.2
#8 lore raw

PATCH 8/24 iscsi class: Use our own workq instead of common system one.

From: Mike Christie <[email protected]>

There is just too much going on through the common workq and something like a scsi device removal through sysfs affects how long it will take to recover the transport, mark it as failed, or shut it down gracefully.

---
 drivers/scsi/scsi_transport_iscsi.c |   16 ++++++++++++----
 1 files changed, 12 insertions(+), 4 deletions(-)
drivers/scsi/scsi_transport_iscsi.c +12 -4
index 75d3069..9cc2cc8 100644
@@ -50,6 +50,7 @@ struct iscsi_internal {
 };
 
 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
@@ -252,7 +253,7 @@ static void session_recovery_timedout(struct work_struct *work)
 void iscsi_unblock_session(struct iscsi_cls_session *session)
 {
 	if (!cancel_delayed_work(&session->recovery_work))
-		flush_scheduled_work();
+		flush_workqueue(iscsi_eh_timer_workq);
 	scsi_target_unblock(&session->dev);
 }
 EXPORT_SYMBOL_GPL(iscsi_unblock_session);
@@ -260,8 +261,8 @@ EXPORT_SYMBOL_GPL(iscsi_unblock_session);
 void iscsi_block_session(struct iscsi_cls_session *session)
 {
 	scsi_target_block(&session->dev);
-	schedule_delayed_work(&session->recovery_work,
-			     session->recovery_tmo * HZ);
+	queue_delayed_work(iscsi_eh_timer_workq, &session->recovery_work,
+			   session->recovery_tmo * HZ);
 }
 EXPORT_SYMBOL_GPL(iscsi_block_session);
 
@@ -357,7 +358,7 @@ void iscsi_remove_session(struct iscsi_cls_session *session)
 	struct iscsi_host *ihost = shost->shost_data;
 
 	if (!cancel_delayed_work(&session->recovery_work))
-		flush_scheduled_work();
+		flush_workqueue(iscsi_eh_timer_workq);
 
 	mutex_lock(&ihost->mutex);
 	list_del(&session->host_list);
@@ -1521,8 +1522,14 @@ static __init int iscsi_transport_init(void)
 		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:
+	sock_release(nls->sk_socket);
 unregister_session_class:
 	transport_class_unregister(&iscsi_session_class);
 unregister_conn_class:
@@ -1536,6 +1543,7 @@ unregister_transport_class:
 
 static void __exit iscsi_transport_exit(void)
 {
+	destroy_workqueue(iscsi_eh_timer_workq);
 	sock_release(nls->sk_socket);
 	transport_class_unregister(&iscsi_connection_class);
 	transport_class_unregister(&iscsi_session_class);
Signature · 1 line
1.5.1.2
#9 lore raw

PATCH 9/24 libiscsi: grab eh_mutex during host reset

From: Mike Christie <[email protected]>

I thought we may not need the eh mutex during host reset, but that is wrong with the new shutdown code. When start_session_recovery sets the state to terminate then drops the session lock. The scsi eh thread could then grab the session lock see that we are terminating and then return failed to scsi-ml. scsi-ml's eh then owns the command and will do whatever it wants with it. But then the iscsi eh thread could grab the session lock and want to complete the scsi commands that we in the LLD, but
it no longer owns them and kaboom.

---
 drivers/scsi/libiscsi.c |    7 +++++--
 1 files changed, 5 insertions(+), 2 deletions(-)
drivers/scsi/libiscsi.c +5 -2
index b7a2b9a..441e351 100644
@@ -1079,17 +1079,19 @@ int iscsi_eh_host_reset(struct scsi_cmnd *sc)
 	struct iscsi_session *session = iscsi_hostdata(host->hostdata);
 	struct iscsi_conn *conn = session->leadconn;
 
+	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;
 	}
 
 	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
@@ -1104,13 +1106,14 @@ 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)
 		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);
Signature · 1 line
1.5.1.2
#10 lore raw

PATCH 10/24 libiscsi: fix shutdown

From: Mike Christie <[email protected]>

We were using the device delete sysfs file to remove each device then logout. Now in 2.6.21 this will not work because
the sysfs delete file returns immediately and does not wait for the device removal to complete. This causes a hang if a cache sync is needed during shutdown. Before .21, that approach had other problems, so this patch fixes the shutdown code so that we remove the target and unbind the session before logging out and shut down the session

---
 drivers/scsi/libiscsi.c             |    4 +-
 drivers/scsi/qla4xxx/ql4_init.c     |    4 +-
 drivers/scsi/qla4xxx/ql4_os.c       |    7 +-
 drivers/scsi/scsi_transport_iscsi.c |  289 +++++++++++++++++++----------------
 include/scsi/iscsi_if.h             |    7 +
 include/scsi/iscsi_proto.h          |    2 +
 include/scsi/scsi_transport_iscsi.h |    7 +-
 7 files changed, 176 insertions(+), 144 deletions(-)
drivers/scsi/libiscsi.c +2 -2
index 441e351..5205ef2 100644
@@ -1662,7 +1662,7 @@ 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_unblock_session(cls_session);
+	iscsi_remove_session(cls_session);
 	scsi_remove_host(shost);
 
 	iscsi_pool_free(&session->mgmtpool);
@@ -1677,7 +1677,7 @@ void iscsi_session_teardown(struct iscsi_cls_session *cls_session)
 	kfree(session->hwaddress);
 	kfree(session->initiatorname);
 
-	iscsi_destroy_session(cls_session);
+	iscsi_free_session(cls_session);
 	scsi_host_put(shost);
 	module_put(owner);
 }
drivers/scsi/qla4xxx/ql4_init.c +3 -1
index d692c71..cbe0a17 100644
@@ -5,6 +5,7 @@
  * See LICENSE.qla4xxx for copyright and licensing details.
  */
 
+#include <scsi/iscsi_if.h>
 #include "ql4_def.h"
 #include "ql4_glbl.h"
 #include "ql4_dbg.h"
@@ -1305,7 +1306,8 @@ int qla4xxx_process_ddb_changed(struct scsi_qla_host *ha,
 		atomic_set(&ddb_entry->relogin_timer, 0);
 		clear_bit(DF_RELOGIN, &ddb_entry->flags);
 		clear_bit(DF_NO_RELOGIN, &ddb_entry->flags);
-		iscsi_if_create_session_done(ddb_entry->conn);
+		iscsi_session_event(ddb_entry->sess,
+				    ISCSI_KEVENT_CREATE_SESSION);
 		/*
 		 * Change the lun state to READY in case the lun TIMEOUT before
 		 * the device came back.
drivers/scsi/qla4xxx/ql4_os.c +2 -5
index 89460d2..f55b9f7 100644
@@ -298,8 +298,7 @@ void qla4xxx_destroy_sess(struct ddb_entry *ddb_entry)
 		return;
 
 	if (ddb_entry->conn) {
-		iscsi_if_destroy_session_done(ddb_entry->conn);
-		iscsi_destroy_conn(ddb_entry->conn);
+		atomic_set(&ddb_entry->state, DDB_STATE_DEAD);
 		iscsi_remove_session(ddb_entry->sess);
 	}
 	iscsi_free_session(ddb_entry->sess);
@@ -309,6 +308,7 @@ int qla4xxx_add_sess(struct ddb_entry *ddb_entry)
 {
 	int err;
 
+	ddb_entry->sess->recovery_tmo = ddb_entry->ha->port_down_retry_count;
 	err = iscsi_add_session(ddb_entry->sess, ddb_entry->fw_ddb_index);
 	if (err) {
 		DEBUG2(printk(KERN_ERR "Could not add session.\n"));
@@ -321,9 +321,6 @@ int qla4xxx_add_sess(struct ddb_entry *ddb_entry)
 		DEBUG2(printk(KERN_ERR "Could not add connection.\n"));
 		return -ENOMEM;
 	}
-
-	ddb_entry->sess->recovery_tmo = ddb_entry->ha->port_down_retry_count;
-	iscsi_if_create_session_done(ddb_entry->conn);
 	return 0;
 }
 
drivers/scsi/scsi_transport_iscsi.c +155 -134

473 lines not on this page · raw message

include/scsi/iscsi_if.h +7 -0
index bff0b1f..8a4426d 100644
@@ -49,12 +49,15 @@ enum iscsi_uevent_e {
 
 	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 {
@@ -156,6 +159,10 @@ 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;
include/scsi/iscsi_proto.h +2 -0
index 6947082..318a909 100644
@@ -21,6 +21,8 @@
 #ifndef ISCSI_PROTO_H
 #define ISCSI_PROTO_H
 
+#include <linux/types.h>
+
 #define ISCSI_DRAFT20_VERSION	0x00
 
 /* default iSCSI listen port for incoming connections */
include/scsi/scsi_transport_iscsi.h +5 -2
index b8d97bd..093b403 100644
@@ -186,6 +186,7 @@ struct iscsi_cls_session {
 	/* recovery fields */
 	int recovery_tmo;
 	struct delayed_work recovery_work;
+	struct work_struct unbind_work;
 
 	int target_id;
 
@@ -206,6 +207,8 @@ struct iscsi_cls_session {
 struct iscsi_host {
 	struct list_head sessions;
 	struct mutex mutex;
+	struct workqueue_struct *unbind_workq;
+	char unbind_workq_name[KOBJ_NAME_LEN];
 };
 
 /*
@@ -215,8 +218,8 @@ extern struct iscsi_cls_session *iscsi_alloc_session(struct Scsi_Host *shost,
 					struct iscsi_transport *transport);
 extern int iscsi_add_session(struct iscsi_cls_session *session,
 			     unsigned int target_id);
-extern int iscsi_if_create_session_done(struct iscsi_cls_conn *conn);
-extern int iscsi_if_destroy_session_done(struct iscsi_cls_conn *conn);
+extern int iscsi_session_event(struct iscsi_cls_session *session,
+			       enum iscsi_uevent_e event);
 extern struct iscsi_cls_session *iscsi_create_session(struct Scsi_Host *shost,
 						struct iscsi_transport *t,
 						unsigned int target_id);
Signature · 1 line
1.5.1.2
#11 lore raw

PATCH 11/24 libiscsi: fix nop handling

From: Mike Christie <[email protected]>

During root boot and shutdown the target could send us nops. At this time iscsid cannot be running, so the target will drop the session and the boot or shutdown will hang.

To handle this and allow us to better control when to check the network this patch moves the nop handling to the kernel.

---
 drivers/infiniband/ulp/iser/iscsi_iser.c |    4 +-
 drivers/scsi/iscsi_tcp.c                 |    4 +-
 drivers/scsi/libiscsi.c                  |  331 ++++++++++++++++++++++++------
 drivers/scsi/scsi_transport_iscsi.c      |    4 +
 include/scsi/iscsi_if.h                  |   11 +
 include/scsi/libiscsi.h                  |    8 +
 6 files changed, 294 insertions(+), 68 deletions(-)
drivers/infiniband/ulp/iser/iscsi_iser.c +3 -1
index a2622f4..2656064 100644
@@ -577,7 +577,9 @@ static struct iscsi_transport iscsi_iser_transport = {
 				  ISCSI_PERSISTENT_ADDRESS |
 				  ISCSI_TARGET_NAME | ISCSI_TPGT |
 				  ISCSI_USERNAME | ISCSI_PASSWORD |
-				  ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN,
+				  ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN |
+				  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,
drivers/scsi/iscsi_tcp.c +3 -1
index 90eae8e..9b41852 100644
@@ -2246,7 +2246,9 @@ static struct iscsi_transport iscsi_tcp_transport = {
 				  ISCSI_TARGET_NAME | ISCSI_TPGT |
 				  ISCSI_USERNAME | ISCSI_PASSWORD |
 				  ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN |
-				  ISCSI_FAST_ABORT,
+				  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,
drivers/scsi/libiscsi.c +265 -66

472 lines not on this page · raw message

drivers/scsi/scsi_transport_iscsi.c +4 -0
index b82139d..36aa50e 100644
@@ -1202,6 +1202,8 @@ 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)
@@ -1437,6 +1439,8 @@ 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;
include/scsi/iscsi_if.h +11 -0
index 8a4426d..e19e584 100644
@@ -244,6 +244,12 @@ enum iscsi_param {
 	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,
 };
@@ -275,6 +281,11 @@ enum iscsi_param {
 #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 {
include/scsi/libiscsi.h +8 -0
index d68f745..889f51f 100644
@@ -148,6 +148,12 @@ 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;
@@ -238,6 +244,8 @@ struct iscsi_session {
 	uint32_t		queued_cmdsn;
 
 	/* configuration */
+	int			abort_timeout;
+	int			lu_reset_timeout;
 	int			initial_r2t_en;
 	unsigned		max_r2t;
 	int			imm_data_en;
Signature · 1 line
1.5.1.2
#12 lore raw

PATCH 12/24 iscsi_tcp: update the website URL

From: FUJITA Tomonori <[email protected]>

Use open-iscsi.org instead of linux-iscsi.sf.net, which hasn't been updated for ages.

---
 drivers/scsi/Kconfig |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)
drivers/scsi/Kconfig +1 -1
index becbb09..4c7e99f 100644
@@ -341,7 +341,7 @@ config ISCSI_TCP
 	 The userspace component needed to initialize the driver, documentation,
 	 and sample configuration files can be found here:
 
-	 http://linux-iscsi.sf.net
+	 http://open-iscsi.org
 
 config SGIWD93_SCSI
 	tristate "SGI WD93C93 SCSI Driver"
Signature · 1 line
1.5.1.2
#13 lore raw

PATCH 13/24 Do not fail commands immediately during logout

From: Mike Christie <[email protected]>

If the target requests a logout, then we do not want
to fail commands to scsi-ml right away. This patch just fails in pending commands for a requeue immediately, and then lets iscsid handle running commands like normal recovery.

---
 drivers/scsi/libiscsi.c |   14 ++++++--------
 1 files changed, 6 insertions(+), 8 deletions(-)
drivers/scsi/libiscsi.c +6 -8
index 9688361..b17081b 100644
@@ -917,7 +917,7 @@ check_mgmt:
 		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_NO_CONNECT << 16);
+			fail_command(conn, conn->ctask, DID_IMM_RETRY << 16);
 			continue;
 		}
 		if (iscsi_prep_scsi_cmd_pdu(conn->ctask)) {
@@ -1024,21 +1024,19 @@ 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)
 		 */
-		if (session->state == ISCSI_STATE_IN_RECOVERY) {
+		switch (session->state) {
+		case ISCSI_STATE_IN_RECOVERY:
 			reason = FAILURE_SESSION_IN_RECOVERY;
 			goto reject;
-		}
-
-		switch (session->state) {
+		case ISCSI_STATE_LOGGING_OUT:
+			reason = FAILURE_SESSION_LOGGING_OUT;
+			goto reject;
 		case ISCSI_STATE_RECOVERY_FAILED:
 			reason = FAILURE_SESSION_RECOVERY_TIMEOUT;
 			break;
 		case ISCSI_STATE_TERMINATE:
 			reason = FAILURE_SESSION_TERMINATE;
 			break;
-		case ISCSI_STATE_LOGGING_OUT:
-			reason = FAILURE_SESSION_LOGGING_OUT;
-			break;
 		default:
 			reason = FAILURE_SESSION_FREED;
 		}
Signature · 1 line
1.5.1.2
#14 lore raw

PATCH 14/24 clear conn->ctask when task is completed early

From: Mike Christie <[email protected]>

If the current ctask is failed early, we legt the conn->ctask pointer pointing to a invalid task. When the xmit thread would send data for it, we would then oops.

---
 drivers/scsi/libiscsi.c |    5 ++++-
 1 files changed, 4 insertions(+), 1 deletions(-)
drivers/scsi/libiscsi.c +4 -1
index b17081b..4461317 100644
@@ -248,13 +248,16 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)
  */
 static void iscsi_complete_command(struct iscsi_cmd_task *ctask)
 {
-	struct iscsi_session *session = ctask->conn->session;
+	struct iscsi_conn *conn = ctask->conn;
+	struct iscsi_session *session = 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);
Signature · 1 line
1.5.1.2
#15 lore raw

PATCH 15/24 Drop host lock in queuecommand

From: Mike Christie <[email protected]>

The driver does not need the host lock in queuecommand so drop it.

---
 drivers/scsi/libiscsi.c |    6 +++++-
 1 files changed, 5 insertions(+), 1 deletions(-)
drivers/scsi/libiscsi.c +5 -1
index 4461317..b0bc8c3 100644
@@ -1010,8 +1010,9 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
 	sc->SCp.ptr = NULL;
 
 	host = sc->device->host;
-	session = iscsi_hostdata(host->hostdata);
+	spin_unlock(host->host_lock);
 
+	session = iscsi_hostdata(host->hostdata);
 	spin_lock(&session->lock);
 
 	/*
@@ -1077,11 +1078,13 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
 	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:
@@ -1091,6 +1094,7 @@ fault:
 	sc->result = (DID_NO_CONNECT << 16);
 	scsi_set_resid(sc, scsi_bufflen(sc));
 	sc->scsi_done(sc);
+	spin_lock(host->host_lock);
 	return 0;
 }
 EXPORT_SYMBOL_GPL(iscsi_queuecommand);
Signature · 1 line
1.5.1.2
#16 lore raw

PATCH 16/24 convert xmit path to iscsi chunks

From: Mike Christie <[email protected]>

from [email protected]:

Convert xmit to iscsi chunks.

from [email protected]:

Bug fixes, more digest integration, sg chaining conversion and other sg wrapper changes, coding style sync up, and removal of io fields, like pdu_sent, that are not needed.

---
 drivers/infiniband/ulp/iser/iscsi_iser.c |    3 +-
 drivers/scsi/iscsi_tcp.c                 | 1333 ++++++++++++------------------
 drivers/scsi/iscsi_tcp.h                 |   75 +--
 drivers/scsi/libiscsi.c                  |   37 +-
 include/scsi/scsi_transport_iscsi.h      |    2 +-
 5 files changed, 563 insertions(+), 887 deletions(-)
drivers/infiniband/ulp/iser/iscsi_iser.c +2 -1
index 2656064..fd69fb3 100644
@@ -129,7 +129,7 @@ error:
  * iscsi_iser_cmd_init - Initialize iSCSI SCSI_READ or SCSI_WRITE commands
  *
  **/
-static void
+static int
 iscsi_iser_cmd_init(struct iscsi_cmd_task *ctask)
 {
 	struct iscsi_iser_conn     *iser_conn  = ctask->conn->dd_data;
@@ -138,6 +138,7 @@ iscsi_iser_cmd_init(struct iscsi_cmd_task *ctask)
 	iser_ctask->command_sent = 0;
 	iser_ctask->iser_conn    = iser_conn;
 	iser_ctask_rdma_init(iser_ctask);
+	return 0;
 }
 
 /**
drivers/scsi/iscsi_tcp.c +526 -807

1,741 lines not on this page · raw message

drivers/scsi/iscsi_tcp.h +15 -60

143 lines not on this page · raw message

drivers/scsi/libiscsi.c +19 -18
index b0bc8c3..f15df8d 100644
@@ -156,20 +156,19 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)
 	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, conn->id, session->age);
-        hdr->data_length = cpu_to_be32(scsi_bufflen(sc));
-        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);
+	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(scsi_bufflen(sc));
+	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);
 	if (sc->cmd_len < MAX_COMMAND_SIZE)
 		memset(&hdr->cdb[sc->cmd_len], 0,
 			MAX_COMMAND_SIZE - sc->cmd_len);
 
-	ctask->data_count = 0;
 	ctask->imm_count = 0;
 	if (sc->sc_data_direction == DMA_TO_DEVICE) {
 		hdr->flags |= ISCSI_FLAG_CMD_WRITE;
@@ -198,9 +197,9 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)
 			else
 				ctask->imm_count = min(scsi_bufflen(sc),
 							conn->max_xmit_dlength);
-			hton24(ctask->hdr->dlength, ctask->imm_count);
+			hton24(hdr->dlength, ctask->imm_count);
 		} else
-			zero_data(ctask->hdr->dlength);
+			zero_data(hdr->dlength);
 
 		if (!session->initial_r2t_en) {
 			ctask->unsol_count = min((session->first_burst),
@@ -210,7 +209,7 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)
 
 		if (!ctask->unsol_count)
 			/* No unsolicit Data-Out's */
-			ctask->hdr->flags |= ISCSI_FLAG_CMD_FINAL;
+			hdr->flags |= ISCSI_FLAG_CMD_FINAL;
 	} else {
 		hdr->flags |= ISCSI_FLAG_CMD_FINAL;
 		zero_data(hdr->dlength);
@@ -228,13 +227,15 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)
 	WARN_ON(hdrlength >= 256);
 	hdr->hlength = hdrlength & 0xFF;
 
-	conn->scsicmd_pdus_cnt++;
+	if (conn->session->tt->init_cmd_task(conn->ctask))
+		return EIO;
 
-        debug_scsi("iscsi prep [%s cid %d sc %p cdb 0x%x itt 0x%x len %d "
+	conn->scsicmd_pdus_cnt++;
+	debug_scsi("iscsi prep [%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",
+		sc->sc_data_direction == DMA_TO_DEVICE ? "write" : "read",
 		conn->id, sc, sc->cmnd[0], ctask->itt, scsi_bufflen(sc),
-                session->cmdsn, session->max_cmdsn - session->exp_cmdsn + 1);
+		session->cmdsn, session->max_cmdsn - session->exp_cmdsn + 1);
 	return 0;
 }
 
@@ -927,7 +928,7 @@ check_mgmt:
 			fail_command(conn, conn->ctask, DID_ABORT << 16);
 			continue;
 		}
-		conn->session->tt->init_cmd_task(conn->ctask);
+
 		conn->ctask->state = ISCSI_TASK_RUNNING;
 		list_move_tail(conn->xmitqueue.next, &conn->run_list);
 		rc = iscsi_xmit_ctask(conn);
include/scsi/scsi_transport_iscsi.h +1 -1
index 093b403..404f11d 100644
@@ -118,7 +118,7 @@ struct iscsi_transport {
 			 char *data, uint32_t data_size);
 	void (*get_stats) (struct iscsi_cls_conn *conn,
 			   struct iscsi_stats *stats);
-	void (*init_cmd_task) (struct iscsi_cmd_task *ctask);
+	int (*init_cmd_task) (struct iscsi_cmd_task *ctask);
 	void (*init_mgmt_task) (struct iscsi_conn *conn,
 				struct iscsi_mgmt_task *mtask);
 	int (*xmit_cmd_task) (struct iscsi_conn *conn,
Signature · 1 line
1.5.1.2
#17 lore raw

PATCH 17/24 iscsi_tcp: stop leaking r2t_info's when the incoming R2T is bad

From: Mike Christie <[email protected]>

from [email protected]:

iscsi_r2t_rsp checks the incoming R2T for sanity, and if it thinks it's fishy, it will drop it silently. In this case, we leaked an r2t_info object. If we do this often enough, we run into a BUG_ON some time later.

Removed r2t wrappers and update patch by Mike Christie

---
 drivers/scsi/iscsi_tcp.c |    6 +++++-
 1 files changed, 5 insertions(+), 1 deletions(-)
drivers/scsi/iscsi_tcp.c +5 -1
index 7212fe9..ecba606 100644
@@ -658,6 +658,8 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
 	r2t->data_length = be32_to_cpu(rhdr->data_length);
 	if (r2t->data_length == 0) {
 		printk(KERN_ERR "iscsi_tcp: invalid R2T with zero data len\n");
+		__kfifo_put(tcp_ctask->r2tpool.queue, (void*)&r2t,
+			    sizeof(void*));
 		spin_unlock(&session->lock);
 		return ISCSI_ERR_DATALEN;
 	}
@@ -669,10 +671,12 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
 
 	r2t->data_offset = be32_to_cpu(rhdr->data_offset);
 	if (r2t->data_offset + r2t->data_length > scsi_bufflen(ctask->sc)) {
-		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, scsi_bufflen(ctask->sc));
+		__kfifo_put(tcp_ctask->r2tpool.queue, (void*)&r2t,
+			    sizeof(void*));
+		spin_unlock(&session->lock);
 		return ISCSI_ERR_DATALEN;
 	}
 
Signature · 1 line
1.5.1.2
#18 lore raw

PATCH 18/24 iscsi_tcp: drop session when itt does not match any command

From: Mike Christie <[email protected]>

A target should never send us a itt that does not match a running task. If it does we do not really know what is coming down after the header, unless we evaluate the hdr and do some guessing sometimes. However, even if we know what is coming we probably do not have buffers for it or we cannot respond (if it is a r2t for example), so just drop the session.

---
 drivers/scsi/iscsi_tcp.c |    6 +-----
 1 files changed, 1 insertions(+), 5 deletions(-)
drivers/scsi/iscsi_tcp.c +1 -5
index ecba606..65df908 100644
@@ -755,11 +755,7 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
 	opcode = hdr->opcode & ISCSI_OPCODE_MASK;
 	/* verify itt (itt encoding: age+cid+itt) */
 	rc = iscsi_verify_itt(conn, hdr, &itt);
-	if (rc == ISCSI_ERR_NO_SCSI_CMD) {
-		/* XXX: what does this do? */
-		tcp_conn->in.datalen = 0; /* force drop */
-		return 0;
-	} else if (rc)
+	if (rc)
 		return rc;
 
 	debug_tcp("opcode 0x%x ahslen %d datalen %d\n",
Signature · 1 line
1.5.1.2
#19 lore raw

PATCH 19/24 libiscsi, iscsi class: set tmf to a safe default and export in sysfs

From: Mike Christie <[email protected]>

Older tools will not be setting the tmf time outs since they did not exists, so set them to a safe default.

And export abort and lu reset timeout values in sysfs.

---
 drivers/scsi/libiscsi.c             |    2 ++
 drivers/scsi/scsi_transport_iscsi.c |    8 ++++++--
 2 files changed, 8 insertions(+), 2 deletions(-)
drivers/scsi/libiscsi.c +2 -0
index f15df8d..6573223 100644
@@ -1732,6 +1732,8 @@ iscsi_session_setup(struct iscsi_transport *iscsit,
 	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;
drivers/scsi/scsi_transport_iscsi.c +6 -2
index 36aa50e..3585599 100644
@@ -30,7 +30,7 @@
 #include <scsi/scsi_transport_iscsi.h>
 #include <scsi/iscsi_if.h>
 
-#define ISCSI_SESSION_ATTRS 16
+#define ISCSI_SESSION_ATTRS 18
 #define ISCSI_CONN_ATTRS 11
 #define ISCSI_HOST_ATTRS 4
 #define ISCSI_TRANSPORT_VERSION "2.0-724"
@@ -1242,7 +1242,9 @@ 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, 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);
 
 #define iscsi_priv_session_attr_show(field, format)			\
 static ssize_t								\
@@ -1467,6 +1469,8 @@ iscsi_register_transport(struct iscsi_transport *tt)
 	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);
 
 	BUG_ON(count > ISCSI_SESSION_ATTRS);
Signature · 1 line
1.5.1.2
#20 lore raw

PATCH 20/24 iscsi_tcp: enable sg chaining

From: Mike Christie <[email protected]>

The previous patches converted iscsi_tcp to support sg chaining. This patch sets the proper flags and sets sg_table size to 4096. This allows fs io to be capped at max_sectors, but passthrough IO to be limited by some other part of the kernel.

---
 drivers/scsi/iscsi_tcp.c |    5 +++--
 drivers/scsi/iscsi_tcp.h |    3 ---
 2 files changed, 3 insertions(+), 5 deletions(-)
drivers/scsi/iscsi_tcp.c +3 -2
index 65df908..84c4a50 100644
@@ -1928,13 +1928,14 @@ static struct scsi_host_template iscsi_sht = {
 	.queuecommand           = iscsi_queuecommand,
 	.change_queue_depth	= iscsi_change_queue_depth,
 	.can_queue		= ISCSI_DEF_XMIT_CMDS_MAX - 1,
-	.sg_tablesize		= ISCSI_SG_TABLESIZE,
+	.sg_tablesize		= 4096,
 	.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,
+	.use_sg_chaining	= ENABLE_SG_CHAINING,
 	.slave_configure        = iscsi_tcp_slave_configure,
 	.proc_name		= "iscsi_tcp",
 	.this_id		= -1,
@@ -1974,7 +1975,7 @@ static struct iscsi_transport iscsi_tcp_transport = {
 	.host_template		= &iscsi_sht,
 	.conndata_size		= sizeof(struct iscsi_conn),
 	.max_conn		= 1,
-	.max_cmd_len		= ISCSI_TCP_MAX_CMD_LEN,
+	.max_cmd_len		= 16,
 	/* session management */
 	.create_session		= iscsi_tcp_session_create,
 	.destroy_session	= iscsi_tcp_session_destroy,
drivers/scsi/iscsi_tcp.h +0 -3
index 893cd2e..ed0b991 100644
@@ -24,9 +24,6 @@
 
 #include <scsi/libiscsi.h>
 
-#define ISCSI_SG_TABLESIZE		SG_ALL
-#define ISCSI_TCP_MAX_CMD_LEN		16
-
 struct crypto_hash;
 struct socket;
 struct iscsi_tcp_conn;
Signature · 1 line
1.5.1.2
#21 lore raw

PATCH 21/24 iscsi_tcp: hold lock during data rsp processing

From: Mike Christie <[email protected]>

iscsi_data_rsp needs to hold the sesison lock when it calls iscsi_update_cmdsn.

---
 drivers/scsi/iscsi_tcp.c |   14 ++++++--------
 1 files changed, 6 insertions(+), 8 deletions(-)
drivers/scsi/iscsi_tcp.c +6 -8
index 84c4a50..edebdf2 100644
@@ -641,13 +641,11 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
 	}
 
 	/* fill-in new R2T associated with the task */
-	spin_lock(&session->lock);
 	iscsi_update_cmdsn(session, (struct iscsi_nopin*)rhdr);
 
 	if (!ctask->sc || 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;
 	}
 
@@ -660,7 +658,6 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
 		printk(KERN_ERR "iscsi_tcp: invalid R2T with zero data len\n");
 		__kfifo_put(tcp_ctask->r2tpool.queue, (void*)&r2t,
 			    sizeof(void*));
-		spin_unlock(&session->lock);
 		return ISCSI_ERR_DATALEN;
 	}
 
@@ -676,7 +673,6 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
 		       r2t->data_offset, scsi_bufflen(ctask->sc));
 		__kfifo_put(tcp_ctask->r2tpool.queue, (void*)&r2t,
 			    sizeof(void*));
-		spin_unlock(&session->lock);
 		return ISCSI_ERR_DATALEN;
 	}
 
@@ -690,8 +686,6 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
 	conn->r2t_pdus_cnt++;
 
 	iscsi_requeue_ctask(ctask);
-	spin_unlock(&session->lock);
-
 	return 0;
 }
 
@@ -764,7 +758,9 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
 	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);
 		if (rc)
 			return rc;
 		if (tcp_conn->in.datalen) {
@@ -806,9 +802,11 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
 		ctask = session->cmds[itt];
 		if (ahslen)
 			rc = ISCSI_ERR_AHSLEN;
-		else if (ctask->sc->sc_data_direction == DMA_TO_DEVICE)
+		else if (ctask->sc->sc_data_direction == DMA_TO_DEVICE) {
+			spin_lock(&session->lock);
 			rc = iscsi_r2t_rsp(conn, ctask);
-		else
+			spin_unlock(&session->lock);
+		} else
 			rc = ISCSI_ERR_PROTO;
 		break;
 	case ISCSI_OP_LOGIN_RSP:
Signature · 1 line
1.5.1.2
#22 lore raw

PATCH 22/24 libiscsi: use is_power_of_2

From: Mike Christie <[email protected]>

Patch from vignesh babu <[email protected]>:

Replacing n & (n - 1) for power of 2 check by is_power_of_2(n)

---
 drivers/scsi/libiscsi.c |    3 ++-
 1 files changed, 2 insertions(+), 1 deletions(-)
drivers/scsi/libiscsi.c +2 -1
index 6573223..553168a 100644
@@ -24,6 +24,7 @@
 #include <linux/types.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>
@@ -1700,7 +1701,7 @@ iscsi_session_setup(struct iscsi_transport *iscsit,
 		qdepth = ISCSI_DEF_CMD_PER_LUN;
 	}
 
-	if (cmds_max < 2 || (cmds_max & (cmds_max - 1)) ||
+	if (!is_power_of_2(cmds_max) ||
 	    cmds_max >= ISCSI_MGMT_ITT_OFFSET) {
 		if (cmds_max != 0)
 			printk(KERN_ERR "iscsi: invalid can_queue of %d. "
Signature · 1 line
1.5.1.2
#23 lore raw

PATCH 23/24 iscsi_tcp: fix setting of r2t

From: Mike Christie <[email protected]>

If we negotiate for X r2ts we have to use only X r2ts. We cannot round up (we could send less though). It is ok to fail if it is not something the driver can handle, so this patch just does that.

---
 drivers/scsi/iscsi_tcp.c |    6 +++---
 1 files changed, 3 insertions(+), 3 deletions(-)
drivers/scsi/iscsi_tcp.c +3 -3
index edebdf2..e5be5fd 100644
@@ -1774,12 +1774,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 (session->max_r2t == roundup_pow_of_two(value))
+		if (value <= 0 || !is_power_of_2(value))
+			return -EINVAL;
+		if (session->max_r2t == 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;
Signature · 1 line
1.5.1.2
#24 lore raw

PATCH 24/24 iscsi class: bump version

From: Mike Christie <[email protected]>

Update version.

---
 drivers/scsi/scsi_transport_iscsi.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)
drivers/scsi/scsi_transport_iscsi.c +1 -1
index 3585599..ef0e742 100644
@@ -33,7 +33,7 @@
 #define ISCSI_SESSION_ATTRS 18
 #define ISCSI_CONN_ATTRS 11
 #define ISCSI_HOST_ATTRS 4
-#define ISCSI_TRANSPORT_VERSION "2.0-724"
+#define ISCSI_TRANSPORT_VERSION "2.0-867"
 
 struct iscsi_internal {
 	int daemon_pid;
Signature · 1 line
1.5.1.2
#25 lore raw

Re: [PATCH 20/24] iscsi_tcp: enable sg chaining

In reply to [email protected]
Quoted 18 lines · On Thu, Dec 13 2007 at 20:43 +0200, [email protected] wrote:

From: Mike Christie <[email protected]>

The previous patches converted iscsi_tcp to support sg chaining. This patch sets the proper flags and sets sg_table size to 4096. This allows fs io to be capped at max_sectors, but passthrough IO to be limited by some other part of the kernel.

---
 drivers/scsi/iscsi_tcp.c |    5 +++--
 drivers/scsi/iscsi_tcp.h |    3 ---
 2 files changed, 3 insertions(+), 5 deletions(-)
drivers/scsi/iscsi_tcp.c +0 -0
index 65df908..84c4a50 100644

drivers/scsi/iscsi_tcp.c static struct scsi_host_template iscsi_sht = { · line 1931

Quoted 5 lines
drivers/scsi/iscsi_tcp.c +1 -1
@@ -1928,13 +1928,14 @@ static struct scsi_host_template iscsi_sht = {
 	.queuecommand           = iscsi_queuecommand,
 	.change_queue_depth	= iscsi_change_queue_depth,
 	.can_queue		= ISCSI_DEF_XMIT_CMDS_MAX - 1,
-	.sg_tablesize		= ISCSI_SG_TABLESIZE,
+	.sg_tablesize		= 4096,

+ .sg_tablesize = ~0,
I use ~0 in my tree. The 4096 is a mechanical limitation here without any real reasons. ~0 means that the driver does not have any limitations and the minimum will be at some higher level.

Quoted 24 lines
drivers/scsi/iscsi_tcp.c +2 -1
 	.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,
+	.use_sg_chaining	= ENABLE_SG_CHAINING,
 	.slave_configure        = iscsi_tcp_slave_configure,
 	.proc_name		= "iscsi_tcp",
 	.this_id		= -1,
@@ -1974,7 +1975,7 @@ static struct iscsi_transport iscsi_tcp_transport = {
 	.host_template		= &iscsi_sht,
 	.conndata_size		= sizeof(struct iscsi_conn),
 	.max_conn		= 1,
-	.max_cmd_len		= ISCSI_TCP_MAX_CMD_LEN,
+	.max_cmd_len		= 16,
 	/* session management */
 	.create_session		= iscsi_tcp_session_create,
 	.destroy_session	= iscsi_tcp_session_destroy,
drivers/scsi/iscsi_tcp.h +0 -0
index 893cd2e..ed0b991 100644

drivers/scsi/iscsi_tcp.h line 29

Quoted 9 lines
drivers/scsi/iscsi_tcp.h +0 -3
@@ -24,9 +24,6 @@
 
 #include <scsi/libiscsi.h>
 
-#define ISCSI_SG_TABLESIZE		SG_ALL
-#define ISCSI_TCP_MAX_CMD_LEN		16
-
 struct crypto_hash;
 struct socket;
 struct iscsi_tcp_conn;

Don't fix it just yet. I will send a patch much later, when I will actually need it. I just wanted to make a note of it.

Boaz