summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Christie <michaelc@cs.wisc.edu>2009-05-13 17:57:43 -0500
committerJames Bottomley <James.Bottomley@HansenPartnership.com>2009-05-23 15:44:11 -0500
commit26013ad4c43f49a038a6489c35e9b901491339fe (patch)
treec5f9b4a8c73de58b08ac9c072b81d27de3e1dd3b
parentedbc9aa0580c0aca96ac8d11bfb2defa81d91bb3 (diff)
[SCSI] libiscsi: fix nop response/reply and session cleanup race
If we are responding to a nop from the target by sending our nop, and the session is getting torn down, then iscsi_start_session_recovery could set the conn stop bits while the recv path is sending the nop response and we will hit the bug ons in __iscsi_conn_send_pdu. This has us check the state in __iscsi_conn_send_pdu and fail all incoming mgmt IO if we are not logged in and if the pdu is not login related. It also changes the ordering of the setting of conn stop state bits so they are set after the session state is set (both are set under the session lock). Signed-off-by: Mike Christie <michaelc@cs.wisc.edu> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
-rw-r--r--drivers/scsi/libiscsi.c11
1 files changed, 8 insertions, 3 deletions
diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
index 047543cd3fc1..212fe2027a8c 100644
--- a/drivers/scsi/libiscsi.c
+++ b/drivers/scsi/libiscsi.c
@@ -546,6 +546,9 @@ __iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
*/
task = conn->login_task;
else {
+ if (session->state != ISCSI_STATE_LOGGED_IN)
+ return NULL;
+
BUG_ON(conn->c_stage == ISCSI_CONN_INITIAL_STAGE);
BUG_ON(conn->c_stage == ISCSI_CONN_STOPPED);
@@ -2566,8 +2569,6 @@ static void iscsi_start_session_recovery(struct iscsi_session *session,
{
int old_stop_stage;
- del_timer_sync(&conn->transport_timer);
-
mutex_lock(&session->eh_mutex);
spin_lock_bh(&session->lock);
if (conn->stop_stage == STOP_CONN_TERM) {
@@ -2585,13 +2586,17 @@ static void iscsi_start_session_recovery(struct iscsi_session *session,
session->state = ISCSI_STATE_TERMINATE;
else if (conn->stop_stage != STOP_CONN_RECOVER)
session->state = ISCSI_STATE_IN_RECOVERY;
+ spin_unlock_bh(&session->lock);
+ del_timer_sync(&conn->transport_timer);
+ iscsi_suspend_tx(conn);
+
+ spin_lock_bh(&session->lock);
old_stop_stage = conn->stop_stage;
conn->stop_stage = flag;
conn->c_stage = ISCSI_CONN_STOPPED;
spin_unlock_bh(&session->lock);
- iscsi_suspend_tx(conn);
/*
* for connection level recovery we should not calculate
* header digest. conn->hdr_size used for optimization