Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > fc11cd6e1c513a17304da94a5390f3cd > files > 3534

kernel-2.6.18-194.11.1.el5.src.rpm

From: Mike Christie <mchristi@redhat.com>
Subject: [PATCH RHEL5] fix oops in iscsi packet transfer path
Date: Mon, 13 Nov 2006 13:50:51 -0600
Bugzilla: 215381
Message-Id: <4558CC9B.9030204@redhat.com>
Changelog: scsi: fix oops in iscsi packet transfer path


This is for BZ 215381.

When writing out data we can oops because the receive code, which gets
run from the network softirq, will set the r2t xmit bit then add the
task to the r2tqueue and if the transfer thread happens to be checking
the r2t xmit bit at the same time it would remove a r2t from the
r2tqueue before the recv thread has added it.

This patch is upstream here
http://kernel.org/git/?p=linux/kernel/git/jejb/scsi-rc-fixes-2.6.git;a=commitdiff;h=db37c505e5dfc1a26d6c82f1ce0c3ae06641c3e0

The attached patch was made against the current RHEL5 kernel and
includes the other upstream bugfixes RHEL5 is missing that were
discovered in recent upstream testing, and was tested by me (ran
bonnie++ and disktest and rebooted targets and unplugged cables) and by
upstream testers. I could not reproduce the r2t race, but the upstream
reporters tested the patch and verified it worked.

diff -aurp linux-2.6.18.noarch/drivers/scsi/iscsi_tcp.c linux-2.6.18.noarch.allbugs/drivers/scsi/iscsi_tcp.c
--- linux-2.6.18.noarch/drivers/scsi/iscsi_tcp.c	2006-11-13 12:54:42.000000000 -0600
+++ linux-2.6.18.noarch.allbugs/drivers/scsi/iscsi_tcp.c	2006-11-13 13:34:56.000000000 -0600
@@ -415,8 +415,8 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, s
 	iscsi_solicit_data_init(conn, ctask, r2t);
 
 	tcp_ctask->exp_r2tsn = r2tsn + 1;
-	tcp_ctask->xmstate |= XMSTATE_SOL_HDR;
 	__kfifo_put(tcp_ctask->r2tqueue, (void*)&r2t, sizeof(void*));
+	tcp_ctask->xmstate |= XMSTATE_SOL_HDR;
 	list_move_tail(&ctask->running, &conn->xmitqueue);
 
 	scsi_queue_work(session->host, &conn->xmitwork);
@@ -1624,9 +1624,12 @@ static int iscsi_send_sol_pdu(struct isc
 	if (tcp_ctask->xmstate & XMSTATE_SOL_HDR) {
 		tcp_ctask->xmstate &= ~XMSTATE_SOL_HDR;
 		tcp_ctask->xmstate |= XMSTATE_SOL_DATA;
-		if (!tcp_ctask->r2t)
+		if (!tcp_ctask->r2t) {
+			spin_lock_bh(&session->lock);
 			__kfifo_get(tcp_ctask->r2tqueue, (void*)&tcp_ctask->r2t,
 				    sizeof(void*));
+			spin_unlock_bh(&session->lock);
+		}
 send_hdr:
 		r2t = tcp_ctask->r2t;
 		dtask = &r2t->dtask;
@@ -1809,21 +1812,14 @@ iscsi_tcp_conn_destroy(struct iscsi_cls_
 {
 	struct iscsi_conn *conn = cls_conn->dd_data;
 	struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
-	int digest = 0;
-
-	if (conn->hdrdgst_en || conn->datadgst_en)
-		digest = 1;
 
 	iscsi_tcp_release_conn(conn);
 	iscsi_conn_teardown(cls_conn);
 
-	/* now free tcp_conn */
-	if (digest) {
-		if (tcp_conn->tx_tfm)
-			crypto_free_tfm(tcp_conn->tx_tfm);
-		if (tcp_conn->rx_tfm)
-			crypto_free_tfm(tcp_conn->rx_tfm);
-	}
+	if (tcp_conn->tx_tfm)
+		crypto_free_tfm(tcp_conn->tx_tfm);
+	if (tcp_conn->rx_tfm)
+		crypto_free_tfm(tcp_conn->rx_tfm);
 
 	kfree(tcp_conn);
 }
diff -aurp linux-2.6.18.noarch/drivers/scsi/libiscsi.c linux-2.6.18.noarch.allbugs/drivers/scsi/libiscsi.c
--- linux-2.6.18.noarch/drivers/scsi/libiscsi.c	2006-11-13 12:54:42.000000000 -0600
+++ linux-2.6.18.noarch.allbugs/drivers/scsi/libiscsi.c	2006-11-13 13:34:56.000000000 -0600
@@ -481,8 +481,8 @@ int __iscsi_complete_pdu(struct iscsi_co
 			break;
 		case ISCSI_OP_ASYNC_EVENT:
 			conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1;
-			/* we need sth like iscsi_async_event_rsp() */
-			rc = ISCSI_ERR_BAD_OPCODE;
+			if (iscsi_recv_pdu(conn->cls_conn, hdr, data, datalen))
+				rc = ISCSI_ERR_CONN_FAILED;
 			break;
 		default:
 			rc = ISCSI_ERR_BAD_OPCODE;
@@ -578,6 +578,27 @@ void iscsi_conn_failure(struct iscsi_con
 }
 EXPORT_SYMBOL_GPL(iscsi_conn_failure);
 
+static int iscsi_xmit_imm_task(struct iscsi_conn *conn)
+{
+	struct iscsi_hdr *hdr = conn->mtask->hdr;
+	int rc, was_logout = 0;
+
+	if ((hdr->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_LOGOUT) {
+		conn->session->state = ISCSI_STATE_IN_RECOVERY;
+		iscsi_block_session(session_to_cls(conn->session));
+		was_logout = 1;
+	}
+	rc = conn->session->tt->xmit_mgmt_task(conn, conn->mtask);
+	if (rc)
+		return rc;
+
+	if (was_logout) {
+		set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
+		return -ENODATA;
+	}
+	return 0;
+}
+
 /**
  * iscsi_data_xmit - xmit any command into the scheduled connection
  * @conn: iscsi connection
@@ -623,7 +644,7 @@ static int iscsi_data_xmit(struct iscsi_
 		conn->ctask = NULL;
 	}
 	if (conn->mtask) {
-		rc = tt->xmit_mgmt_task(conn, conn->mtask);
+		rc = iscsi_xmit_imm_task(conn);
 	        if (rc)
 		        goto again;
 		/* done with this in-progress mtask */
@@ -638,7 +659,7 @@ static int iscsi_data_xmit(struct iscsi_
 			list_add_tail(&conn->mtask->running,
 				      &conn->mgmt_run_list);
 			spin_unlock_bh(&conn->session->lock);
-			rc = tt->xmit_mgmt_task(conn, conn->mtask);
+			rc = iscsi_xmit_imm_task(conn);
 		        if (rc)
 			        goto again;
 	        }
@@ -661,8 +682,6 @@ static int iscsi_data_xmit(struct iscsi_
 		spin_unlock_bh(&conn->session->lock);
 
 		rc = tt->xmit_cmd_task(conn, conn->ctask);
-		if (rc)
-			goto again;
 
 		spin_lock_bh(&conn->session->lock);
 		__iscsi_put_ctask(conn->ctask);