Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 89877e42827f16fa5f86b1df0c2860b1 > files > 2162

kernel-2.6.18-128.1.10.el5.src.rpm

From: mchristi@redhat.com <mchristi@redhat.com>
Date: Mon, 15 Sep 2008 18:54:14 -0500
Subject: [scsi] libiscsi: data corruption when resending packets
Message-id: 1221522854-2234-1-git-send-email-mchristi@redhat.com
O-Subject: [PATCH] RHEL 5.3: libiscsi: fix data corruption when target has to resend packets
Bugzilla: 460158

From: Mike Christie <mchristi@redhat.com>

This is for BZ 460158.

iscsi_tcp was updating the exp_statsn (exp_statsn acknowledges
status and tells the target it is ok to let the resources for
a iscsi pdu (iscsi pdu is basically a iscsi level packet)
to be reused) before it got all the data for iscsi
pdu read into OS buffers. This lead to data corruption if something happens
to a network packet and the network layer requests a retransmit, because
when the targets gets the updated exp_statsn then
it might reallocate buffers and for the retransmit request
it may be sending data from a buffer it has reused
for a new iscsi pdu. This fixes the problem by having the LLD,
iscsi_tcp in this case, transfer data then update the status bits.

I have tested with a couple targets, iet, stgt, and istor by doing
reads to the target so it hits the node paths below to check
for regrssions. I haven't been able to cause the curruption bug to
occur, so I have not veriried that. The bz reporter did test a
similar patch for RHEL4's iscsi initiator which works like
the RHEL5 one and had the same issue. They did not have RHEL5
to test though.

I am just sending this to the lists now, so it is not yet upstream.

diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c
index 86a7aea..e7a7978 100644
--- a/drivers/scsi/iscsi_tcp.c
+++ b/drivers/scsi/iscsi_tcp.c
@@ -205,18 +205,17 @@ iscsi_tcp_cleanup_ctask(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
 }
 
 /**
- * iscsi_data_rsp - SCSI Data-In Response processing
+ * iscsi_data_in - SCSI Data-In Response processing
  * @conn: iscsi connection
  * @ctask: scsi command task
  **/
 static int
-iscsi_data_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
+iscsi_data_in(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
 {
 	struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
 	struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
 	struct iscsi_data_rsp *rhdr = (struct iscsi_data_rsp *)tcp_conn->in.hdr;
 	struct iscsi_session *session = conn->session;
-	struct scsi_cmnd *sc = ctask->sc;
 	int datasn = be32_to_cpu(rhdr->datasn);
 
 	iscsi_update_cmdsn(session, (struct iscsi_nopin*)rhdr);
@@ -240,25 +239,6 @@ iscsi_data_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
 	if (tcp_ctask->data_offset + tcp_conn->in.datalen > ctask->total_length)
 		return ISCSI_ERR_DATA_OFFSET;
 
-	if (rhdr->flags & ISCSI_FLAG_DATA_STATUS) {
-		conn->exp_statsn = be32_to_cpu(rhdr->statsn) + 1;
-		if (rhdr->flags & ISCSI_FLAG_DATA_UNDERFLOW) {
-			int res_count = be32_to_cpu(rhdr->residual_count);
-
-			if (res_count > 0 &&
-			    res_count <= sc->request_bufflen) {
-				sc->resid = res_count;
-				sc->result = (DID_OK << 16) | rhdr->cmd_status;
-			} else
-				sc->result = (DID_BAD_TARGET << 16) |
-					rhdr->cmd_status;
-		} else if (rhdr->flags & ISCSI_FLAG_DATA_OVERFLOW) {
-			sc->resid = be32_to_cpu(rhdr->residual_count);
-			sc->result = (DID_OK << 16) | rhdr->cmd_status;
-		} else
-			sc->result = (DID_OK << 16) | rhdr->cmd_status;
-	}
-
 	conn->datain_pdus_cnt++;
 	return 0;
 }
@@ -493,7 +473,7 @@ iscsi_tcp_hdr_recv(struct iscsi_conn *conn)
 	switch(opcode) {
 	case ISCSI_OP_SCSI_DATA_IN:
 		tcp_conn->in.ctask = session->cmds[itt];
-		rc = iscsi_data_rsp(conn, tcp_conn->in.ctask);
+		rc = iscsi_data_in(conn, tcp_conn->in.ctask);
 		if (rc)
 			return rc;
 		/* fall through */
diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
index 603fc50..9e7b9fe 100644
--- a/drivers/scsi/libiscsi.c
+++ b/drivers/scsi/libiscsi.c
@@ -407,6 +407,41 @@ out:
 	return 0;
 }
 
+/**
+ * iscsi_data_in_rsp - SCSI Data-In Response processing
+ * @conn: iscsi connection
+ * @hdr:  iscsi pdu
+ * @ctask: scsi command task
+ */
+static void
+iscsi_data_in_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+		  struct iscsi_cmd_task *ctask)
+{
+	struct iscsi_data_rsp *rhdr = (struct iscsi_data_rsp *)hdr;
+	struct scsi_cmnd *sc = ctask->sc;
+
+	if (!(rhdr->flags & ISCSI_FLAG_DATA_STATUS))
+		return;
+
+	sc->result = (DID_OK << 16) | rhdr->cmd_status;
+	conn->exp_statsn = be32_to_cpu(rhdr->statsn) + 1;
+	if (rhdr->flags & (ISCSI_FLAG_DATA_UNDERFLOW |
+	                   ISCSI_FLAG_DATA_OVERFLOW)) {
+		int res_count = be32_to_cpu(rhdr->residual_count);
+
+		if (res_count > 0 &&
+		    (rhdr->flags & ISCSI_FLAG_CMD_OVERFLOW ||
+		     res_count <= sc->request_bufflen))
+			sc->resid = res_count;
+		else
+			sc->result = (DID_BAD_TARGET << 16) |
+				rhdr->cmd_status;
+	}
+
+	conn->scsirsp_pdus_cnt++;
+	__iscsi_put_ctask(ctask);
+}
+
 static void iscsi_tmf_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
 {
 	struct iscsi_tm_rsp *tmf = (struct iscsi_tm_rsp *)hdr;
@@ -523,10 +558,7 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
 			break;
 		case ISCSI_OP_SCSI_DATA_IN:
 			BUG_ON((void*)ctask != ctask->sc->SCp.ptr);
-			if (hdr->flags & ISCSI_FLAG_DATA_STATUS) {
-				conn->scsirsp_pdus_cnt++;
-				__iscsi_put_ctask(ctask);
-			}
+			iscsi_data_in_rsp(conn, hdr, ctask);
 			break;
 		case ISCSI_OP_R2T:
 			/* LLD handles this for now */