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);