Commit 861a9633 authored by John Soni Jose's avatar John Soni Jose Committed by Ben Hutchings

libiscsi: Fix host busy blocking during connection teardown

commit 660d0831 upstream.

In case of hw iscsi offload, an host can have N-number of active
connections. There can be IO's running on some connections which
make host->host_busy always TRUE. Now if logout from a connection
is tried then the code gets into an infinite loop as host->host_busy
is always TRUE.

 iscsi_conn_teardown(....)
 {
   .........
    /*
     * Block until all in-progress commands for this connection
     * time out or fail.
     */
     for (;;) {
      spin_lock_irqsave(session->host->host_lock, flags);
      if (!atomic_read(&session->host->host_busy)) { /* OK for ERL == 0 */
	      spin_unlock_irqrestore(session->host->host_lock, flags);
              break;
      }
     spin_unlock_irqrestore(session->host->host_lock, flags);
     msleep_interruptible(500);
     iscsi_conn_printk(KERN_INFO, conn, "iscsi conn_destroy(): "
                 "host_busy %d host_failed %d\n",
	          atomic_read(&session->host->host_busy),
	          session->host->host_failed);

	................
	...............
     }
  }

This is not an issue with software-iscsi/iser as each cxn is a separate
host.

Fix:
Acquiring eh_mutex in iscsi_conn_teardown() before setting
session->state = ISCSI_STATE_TERMINATE.
Signed-off-by: default avatarJohn Soni Jose <sony.john@avagotech.com>
Reviewed-by: default avatarMike Christie <michaelc@cs.wisc.edu>
Reviewed-by: default avatarChris Leech <cleech@redhat.com>
Signed-off-by: default avatarJames Bottomley <JBottomley@Odin.com>
[bwh: Backported to 3.2: adjust context]
Signed-off-by: default avatarBen Hutchings <ben@decadent.org.uk>
parent a11117c7
...@@ -2906,10 +2906,10 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn) ...@@ -2906,10 +2906,10 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn)
{ {
struct iscsi_conn *conn = cls_conn->dd_data; struct iscsi_conn *conn = cls_conn->dd_data;
struct iscsi_session *session = conn->session; struct iscsi_session *session = conn->session;
unsigned long flags;
del_timer_sync(&conn->transport_timer); del_timer_sync(&conn->transport_timer);
mutex_lock(&session->eh_mutex);
spin_lock_bh(&session->lock); spin_lock_bh(&session->lock);
conn->c_stage = ISCSI_CONN_CLEANUP_WAIT; conn->c_stage = ISCSI_CONN_CLEANUP_WAIT;
if (session->leadconn == conn) { if (session->leadconn == conn) {
...@@ -2921,28 +2921,6 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn) ...@@ -2921,28 +2921,6 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn)
} }
spin_unlock_bh(&session->lock); spin_unlock_bh(&session->lock);
/*
* Block until all in-progress commands for this connection
* time out or fail.
*/
for (;;) {
spin_lock_irqsave(session->host->host_lock, flags);
if (!session->host->host_busy) { /* OK for ERL == 0 */
spin_unlock_irqrestore(session->host->host_lock, flags);
break;
}
spin_unlock_irqrestore(session->host->host_lock, flags);
msleep_interruptible(500);
iscsi_conn_printk(KERN_INFO, conn, "iscsi conn_destroy(): "
"host_busy %d host_failed %d\n",
session->host->host_busy,
session->host->host_failed);
/*
* force eh_abort() to unblock
*/
wake_up(&conn->ehwait);
}
/* flush queued up work because we free the connection below */ /* flush queued up work because we free the connection below */
iscsi_suspend_tx(conn); iscsi_suspend_tx(conn);
...@@ -2955,6 +2933,7 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn) ...@@ -2955,6 +2933,7 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn)
if (session->leadconn == conn) if (session->leadconn == conn)
session->leadconn = NULL; session->leadconn = NULL;
spin_unlock_bh(&session->lock); spin_unlock_bh(&session->lock);
mutex_unlock(&session->eh_mutex);
iscsi_destroy_conn(cls_conn); iscsi_destroy_conn(cls_conn);
} }
......
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