Commit 656cffc9 authored by Mike Christie's avatar Mike Christie Committed by James Bottomley

[SCSI] iscsi: fix command requeues during iscsi recovery

Do not flush queues then block session. This will cause commands
to needlessly swing around on us and remove goofy
recovery_failed field and replace with state value.

And do not start recovery from within the host reset function.
This causeis too many problems becuase open-iscsi was desinged to
call out to userspace then have userpscae decide if we should
go into recovery or kill the session.
Signed-off-by: default avatarMike Christie <michaelc@cs.wisc.edu>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@SteelEye.com>
parent 790f39a2
...@@ -487,6 +487,11 @@ void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err) ...@@ -487,6 +487,11 @@ void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err)
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&session->lock, flags); spin_lock_irqsave(&session->lock, flags);
if (session->state == ISCSI_STATE_FAILED) {
spin_unlock_irqrestore(&session->lock, flags);
return;
}
if (session->conn_cnt == 1 || session->leadconn == conn) if (session->conn_cnt == 1 || session->leadconn == conn)
session->state = ISCSI_STATE_FAILED; session->state = ISCSI_STATE_FAILED;
spin_unlock_irqrestore(&session->lock, flags); spin_unlock_irqrestore(&session->lock, flags);
...@@ -612,6 +617,7 @@ enum { ...@@ -612,6 +617,7 @@ enum {
FAILURE_SESSION_FREED, FAILURE_SESSION_FREED,
FAILURE_WINDOW_CLOSED, FAILURE_WINDOW_CLOSED,
FAILURE_SESSION_TERMINATE, FAILURE_SESSION_TERMINATE,
FAILURE_SESSION_IN_RECOVERY,
FAILURE_SESSION_RECOVERY_TIMEOUT, FAILURE_SESSION_RECOVERY_TIMEOUT,
}; };
...@@ -631,17 +637,29 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *)) ...@@ -631,17 +637,29 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
spin_lock(&session->lock); spin_lock(&session->lock);
if (session->state != ISCSI_STATE_LOGGED_IN) { /*
if (session->recovery_failed) { * ISCSI_STATE_FAILED is a temp. state. The recovery
reason = FAILURE_SESSION_RECOVERY_TIMEOUT; * code will decide what is best to do with command queued
goto fault; * during this time
} else if (session->state == ISCSI_STATE_FAILED) { */
reason = FAILURE_SESSION_FAILED; if (session->state != ISCSI_STATE_LOGGED_IN &&
goto reject; session->state != ISCSI_STATE_FAILED) {
} else if (session->state == ISCSI_STATE_TERMINATE) { /*
reason = FAILURE_SESSION_TERMINATE; * to handle the race between when we set the recovery state
* and block the session we requeue here (commands could
* be entering our queuecommand while a block is starting
* up because the block code is not locked)
*/
if (session->state == ISCSI_STATE_IN_RECOVERY) {
reason = FAILURE_SESSION_IN_RECOVERY;
goto fault; goto fault;
} }
if (session->state == ISCSI_STATE_RECOVERY_FAILED)
reason = FAILURE_SESSION_RECOVERY_TIMEOUT;
else if (session->state == ISCSI_STATE_TERMINATE)
reason = FAILURE_SESSION_TERMINATE;
else
reason = FAILURE_SESSION_FREED; reason = FAILURE_SESSION_FREED;
goto fault; goto fault;
} }
...@@ -803,7 +821,7 @@ void iscsi_session_recovery_timedout(struct iscsi_cls_session *cls_session) ...@@ -803,7 +821,7 @@ void iscsi_session_recovery_timedout(struct iscsi_cls_session *cls_session)
spin_lock_bh(&session->lock); spin_lock_bh(&session->lock);
if (session->state != ISCSI_STATE_LOGGED_IN) { if (session->state != ISCSI_STATE_LOGGED_IN) {
session->recovery_failed = 1; session->state = ISCSI_STATE_RECOVERY_FAILED;
if (conn) if (conn)
wake_up(&conn->ehwait); wake_up(&conn->ehwait);
} }
...@@ -838,20 +856,14 @@ int iscsi_eh_host_reset(struct scsi_cmnd *sc) ...@@ -838,20 +856,14 @@ int iscsi_eh_host_reset(struct scsi_cmnd *sc)
* we drop the lock here but the leadconn cannot be destoyed while * we drop the lock here but the leadconn cannot be destoyed while
* we are in the scsi eh * we are in the scsi eh
*/ */
if (fail_session) { if (fail_session)
iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
/*
* if userspace cannot respond then we must kick this off
* here for it
*/
iscsi_start_session_recovery(session, conn, STOP_CONN_RECOVER);
}
debug_scsi("iscsi_eh_host_reset wait for relogin\n"); debug_scsi("iscsi_eh_host_reset wait for relogin\n");
wait_event_interruptible(conn->ehwait, wait_event_interruptible(conn->ehwait,
session->state == ISCSI_STATE_TERMINATE || session->state == ISCSI_STATE_TERMINATE ||
session->state == ISCSI_STATE_LOGGED_IN || session->state == ISCSI_STATE_LOGGED_IN ||
session->recovery_failed); session->state == ISCSI_STATE_RECOVERY_FAILED);
if (signal_pending(current)) if (signal_pending(current))
flush_signals(current); flush_signals(current);
...@@ -940,8 +952,7 @@ static int iscsi_exec_abort_task(struct scsi_cmnd *sc, ...@@ -940,8 +952,7 @@ static int iscsi_exec_abort_task(struct scsi_cmnd *sc,
wait_event_interruptible(conn->ehwait, wait_event_interruptible(conn->ehwait,
sc->SCp.phase != session->age || sc->SCp.phase != session->age ||
session->state != ISCSI_STATE_LOGGED_IN || session->state != ISCSI_STATE_LOGGED_IN ||
conn->tmabort_state != TMABORT_INITIAL || conn->tmabort_state != TMABORT_INITIAL);
session->recovery_failed);
if (signal_pending(current)) if (signal_pending(current))
flush_signals(current); flush_signals(current);
del_timer_sync(&conn->tmabort_timer); del_timer_sync(&conn->tmabort_timer);
...@@ -1491,7 +1502,6 @@ int iscsi_conn_start(struct iscsi_cls_conn *cls_conn) ...@@ -1491,7 +1502,6 @@ int iscsi_conn_start(struct iscsi_cls_conn *cls_conn)
conn->stop_stage = 0; conn->stop_stage = 0;
conn->tmabort_state = TMABORT_INITIAL; conn->tmabort_state = TMABORT_INITIAL;
session->age++; session->age++;
session->recovery_failed = 0;
spin_unlock_bh(&session->lock); spin_unlock_bh(&session->lock);
iscsi_unblock_session(session_to_cls(session)); iscsi_unblock_session(session_to_cls(session));
...@@ -1566,7 +1576,7 @@ static void fail_all_commands(struct iscsi_conn *conn) ...@@ -1566,7 +1576,7 @@ static void fail_all_commands(struct iscsi_conn *conn)
conn->ctask = NULL; conn->ctask = NULL;
} }
void iscsi_start_session_recovery(struct iscsi_session *session, static void iscsi_start_session_recovery(struct iscsi_session *session,
struct iscsi_conn *conn, int flag) struct iscsi_conn *conn, int flag)
{ {
int old_stop_stage; int old_stop_stage;
...@@ -1597,19 +1607,10 @@ void iscsi_start_session_recovery(struct iscsi_session *session, ...@@ -1597,19 +1607,10 @@ void iscsi_start_session_recovery(struct iscsi_session *session,
set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx); set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
if (session->conn_cnt == 0 || session->leadconn == conn) if (session->conn_cnt == 0 || session->leadconn == conn)
session->state = ISCSI_STATE_FAILED; session->state = ISCSI_STATE_IN_RECOVERY;
spin_unlock_bh(&session->lock); spin_unlock_bh(&session->lock);
session->tt->terminate_conn(conn);
/*
* flush queues.
*/
spin_lock_bh(&session->lock);
fail_all_commands(conn);
flush_control_queues(session, conn);
spin_unlock_bh(&session->lock);
/* /*
* for connection level recovery we should not calculate * for connection level recovery we should not calculate
* header digest. conn->hdr_size used for optimization * header digest. conn->hdr_size used for optimization
...@@ -1619,18 +1620,26 @@ void iscsi_start_session_recovery(struct iscsi_session *session, ...@@ -1619,18 +1620,26 @@ void iscsi_start_session_recovery(struct iscsi_session *session,
if (flag == STOP_CONN_RECOVER) { if (flag == STOP_CONN_RECOVER) {
conn->hdrdgst_en = 0; conn->hdrdgst_en = 0;
conn->datadgst_en = 0; conn->datadgst_en = 0;
/* /*
* if this is called from the eh and and from userspace * if this is called from the eh and and from userspace
* then we only need to block once. * then we only need to block once.
*/ */
if (session->state == ISCSI_STATE_FAILED && if (session->state == ISCSI_STATE_IN_RECOVERY &&
old_stop_stage != STOP_CONN_RECOVER) old_stop_stage != STOP_CONN_RECOVER)
iscsi_block_session(session_to_cls(session)); iscsi_block_session(session_to_cls(session));
} }
session->tt->terminate_conn(conn);
/*
* flush queues.
*/
spin_lock_bh(&session->lock);
fail_all_commands(conn);
flush_control_queues(session, conn);
spin_unlock_bh(&session->lock);
mutex_unlock(&conn->xmitmutex); mutex_unlock(&conn->xmitmutex);
} }
EXPORT_SYMBOL_GPL(iscsi_start_session_recovery);
void iscsi_conn_stop(struct iscsi_cls_conn *cls_conn, int flag) void iscsi_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
{ {
......
...@@ -210,7 +210,6 @@ struct iscsi_session { ...@@ -210,7 +210,6 @@ struct iscsi_session {
* - mgmtpool, * * - mgmtpool, *
* - r2tpool */ * - r2tpool */
int state; /* session state */ int state; /* session state */
int recovery_failed;
struct list_head item; struct list_head item;
int conn_cnt; int conn_cnt;
int age; /* counts session re-opens */ int age; /* counts session re-opens */
...@@ -241,8 +240,6 @@ iscsi_session_setup(struct iscsi_transport *, struct scsi_transport_template *, ...@@ -241,8 +240,6 @@ iscsi_session_setup(struct iscsi_transport *, struct scsi_transport_template *,
int, int, uint32_t, uint32_t *); int, int, uint32_t, uint32_t *);
extern void iscsi_session_teardown(struct iscsi_cls_session *); extern void iscsi_session_teardown(struct iscsi_cls_session *);
extern struct iscsi_session *class_to_transport_session(struct iscsi_cls_session *); extern struct iscsi_session *class_to_transport_session(struct iscsi_cls_session *);
extern void iscsi_start_session_recovery(struct iscsi_session *,
struct iscsi_conn *, int);
extern void iscsi_session_recovery_timedout(struct iscsi_cls_session *); extern void iscsi_session_recovery_timedout(struct iscsi_cls_session *);
#define session_to_cls(_sess) \ #define session_to_cls(_sess) \
......
...@@ -173,6 +173,8 @@ struct iscsi_cls_conn { ...@@ -173,6 +173,8 @@ struct iscsi_cls_conn {
#define ISCSI_STATE_LOGGED_IN 2 #define ISCSI_STATE_LOGGED_IN 2
#define ISCSI_STATE_FAILED 3 #define ISCSI_STATE_FAILED 3
#define ISCSI_STATE_TERMINATE 4 #define ISCSI_STATE_TERMINATE 4
#define ISCSI_STATE_IN_RECOVERY 5
#define ISCSI_STATE_RECOVERY_FAILED 6
struct iscsi_cls_session { struct iscsi_cls_session {
struct list_head sess_list; /* item in session_list */ struct list_head sess_list; /* item in session_list */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment