mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 12:28:41 +08:00
scsi: iscsi: Stop queueing during ep_disconnect
During ep_disconnect we have been doing iscsi_suspend_tx/queue to block new I/O but every driver except cxgbi and iscsi_tcp can still get I/O from __iscsi_conn_send_pdu() if we haven't called iscsi_conn_failure() before ep_disconnect. This could happen if we were terminating the session, and the logout timed out before it was even sent to libiscsi. Fix the issue by adding a helper which reverses the bind_conn call that allows new I/O to be queued. Drivers implementing ep_disconnect can use this to make sure new I/O is not queued to them when handling the disconnect. Link: https://lore.kernel.org/r/20210525181821.7617-3-michael.christie@oracle.com Reviewed-by: Lee Duncan <lduncan@suse.com> Signed-off-by: Mike Christie <michael.christie@oracle.com> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
parent
1486a4f5c2
commit
891e2639de
@ -1002,6 +1002,7 @@ static struct iscsi_transport iscsi_iser_transport = {
|
||||
/* connection management */
|
||||
.create_conn = iscsi_iser_conn_create,
|
||||
.bind_conn = iscsi_iser_conn_bind,
|
||||
.unbind_conn = iscsi_conn_unbind,
|
||||
.destroy_conn = iscsi_conn_teardown,
|
||||
.attr_is_visible = iser_attr_is_visible,
|
||||
.set_param = iscsi_iser_set_param,
|
||||
|
@ -5807,6 +5807,7 @@ struct iscsi_transport beiscsi_iscsi_transport = {
|
||||
.destroy_session = beiscsi_session_destroy,
|
||||
.create_conn = beiscsi_conn_create,
|
||||
.bind_conn = beiscsi_conn_bind,
|
||||
.unbind_conn = iscsi_conn_unbind,
|
||||
.destroy_conn = iscsi_conn_teardown,
|
||||
.attr_is_visible = beiscsi_attr_is_visible,
|
||||
.set_iface_param = beiscsi_iface_set_param,
|
||||
|
@ -2276,6 +2276,7 @@ struct iscsi_transport bnx2i_iscsi_transport = {
|
||||
.destroy_session = bnx2i_session_destroy,
|
||||
.create_conn = bnx2i_conn_create,
|
||||
.bind_conn = bnx2i_conn_bind,
|
||||
.unbind_conn = iscsi_conn_unbind,
|
||||
.destroy_conn = bnx2i_conn_destroy,
|
||||
.attr_is_visible = bnx2i_attr_is_visible,
|
||||
.set_param = iscsi_set_param,
|
||||
|
@ -117,6 +117,7 @@ static struct iscsi_transport cxgb3i_iscsi_transport = {
|
||||
/* connection management */
|
||||
.create_conn = cxgbi_create_conn,
|
||||
.bind_conn = cxgbi_bind_conn,
|
||||
.unbind_conn = iscsi_conn_unbind,
|
||||
.destroy_conn = iscsi_tcp_conn_teardown,
|
||||
.start_conn = iscsi_conn_start,
|
||||
.stop_conn = iscsi_conn_stop,
|
||||
|
@ -134,6 +134,7 @@ static struct iscsi_transport cxgb4i_iscsi_transport = {
|
||||
/* connection management */
|
||||
.create_conn = cxgbi_create_conn,
|
||||
.bind_conn = cxgbi_bind_conn,
|
||||
.unbind_conn = iscsi_conn_unbind,
|
||||
.destroy_conn = iscsi_tcp_conn_teardown,
|
||||
.start_conn = iscsi_conn_start,
|
||||
.stop_conn = iscsi_conn_stop,
|
||||
|
@ -1387,23 +1387,32 @@ void iscsi_session_failure(struct iscsi_session *session,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iscsi_session_failure);
|
||||
|
||||
void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err)
|
||||
static bool iscsi_set_conn_failed(struct iscsi_conn *conn)
|
||||
{
|
||||
struct iscsi_session *session = conn->session;
|
||||
|
||||
spin_lock_bh(&session->frwd_lock);
|
||||
if (session->state == ISCSI_STATE_FAILED) {
|
||||
spin_unlock_bh(&session->frwd_lock);
|
||||
return;
|
||||
}
|
||||
if (session->state == ISCSI_STATE_FAILED)
|
||||
return false;
|
||||
|
||||
if (conn->stop_stage == 0)
|
||||
session->state = ISCSI_STATE_FAILED;
|
||||
spin_unlock_bh(&session->frwd_lock);
|
||||
|
||||
set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
|
||||
set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx);
|
||||
iscsi_conn_error_event(conn->cls_conn, err);
|
||||
return true;
|
||||
}
|
||||
|
||||
void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err)
|
||||
{
|
||||
struct iscsi_session *session = conn->session;
|
||||
bool needs_evt;
|
||||
|
||||
spin_lock_bh(&session->frwd_lock);
|
||||
needs_evt = iscsi_set_conn_failed(conn);
|
||||
spin_unlock_bh(&session->frwd_lock);
|
||||
|
||||
if (needs_evt)
|
||||
iscsi_conn_error_event(conn->cls_conn, err);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iscsi_conn_failure);
|
||||
|
||||
@ -2180,6 +2189,51 @@ done:
|
||||
spin_unlock(&session->frwd_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* iscsi_conn_unbind - prevent queueing to conn.
|
||||
* @cls_conn: iscsi conn ep is bound to.
|
||||
* @is_active: is the conn in use for boot or is this for EH/termination
|
||||
*
|
||||
* This must be called by drivers implementing the ep_disconnect callout.
|
||||
* It disables queueing to the connection from libiscsi in preparation for
|
||||
* an ep_disconnect call.
|
||||
*/
|
||||
void iscsi_conn_unbind(struct iscsi_cls_conn *cls_conn, bool is_active)
|
||||
{
|
||||
struct iscsi_session *session;
|
||||
struct iscsi_conn *conn;
|
||||
|
||||
if (!cls_conn)
|
||||
return;
|
||||
|
||||
conn = cls_conn->dd_data;
|
||||
session = conn->session;
|
||||
/*
|
||||
* Wait for iscsi_eh calls to exit. We don't wait for the tmf to
|
||||
* complete or timeout. The caller just wants to know what's running
|
||||
* is everything that needs to be cleaned up, and no cmds will be
|
||||
* queued.
|
||||
*/
|
||||
mutex_lock(&session->eh_mutex);
|
||||
|
||||
iscsi_suspend_queue(conn);
|
||||
iscsi_suspend_tx(conn);
|
||||
|
||||
spin_lock_bh(&session->frwd_lock);
|
||||
if (!is_active) {
|
||||
/*
|
||||
* if logout timed out before userspace could even send a PDU
|
||||
* the state might still be in ISCSI_STATE_LOGGED_IN and
|
||||
* allowing new cmds and TMFs.
|
||||
*/
|
||||
if (session->state == ISCSI_STATE_LOGGED_IN)
|
||||
iscsi_set_conn_failed(conn);
|
||||
}
|
||||
spin_unlock_bh(&session->frwd_lock);
|
||||
mutex_unlock(&session->eh_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iscsi_conn_unbind);
|
||||
|
||||
static void iscsi_prep_abort_task_pdu(struct iscsi_task *task,
|
||||
struct iscsi_tm *hdr)
|
||||
{
|
||||
|
@ -1401,6 +1401,7 @@ struct iscsi_transport qedi_iscsi_transport = {
|
||||
.destroy_session = qedi_session_destroy,
|
||||
.create_conn = qedi_conn_create,
|
||||
.bind_conn = qedi_conn_bind,
|
||||
.unbind_conn = iscsi_conn_unbind,
|
||||
.start_conn = qedi_conn_start,
|
||||
.stop_conn = iscsi_conn_stop,
|
||||
.destroy_conn = qedi_conn_destroy,
|
||||
|
@ -259,6 +259,7 @@ static struct iscsi_transport qla4xxx_iscsi_transport = {
|
||||
.start_conn = qla4xxx_conn_start,
|
||||
.create_conn = qla4xxx_conn_create,
|
||||
.bind_conn = qla4xxx_conn_bind,
|
||||
.unbind_conn = iscsi_conn_unbind,
|
||||
.stop_conn = iscsi_conn_stop,
|
||||
.destroy_conn = qla4xxx_conn_destroy,
|
||||
.set_param = iscsi_set_param,
|
||||
|
@ -2964,7 +2964,7 @@ release_host:
|
||||
}
|
||||
|
||||
static int iscsi_if_ep_disconnect(struct iscsi_transport *transport,
|
||||
u64 ep_handle)
|
||||
u64 ep_handle, bool is_active)
|
||||
{
|
||||
struct iscsi_cls_conn *conn;
|
||||
struct iscsi_endpoint *ep;
|
||||
@ -2981,6 +2981,8 @@ static int iscsi_if_ep_disconnect(struct iscsi_transport *transport,
|
||||
conn->ep = NULL;
|
||||
mutex_unlock(&conn->ep_mutex);
|
||||
conn->state = ISCSI_CONN_FAILED;
|
||||
|
||||
transport->unbind_conn(conn, is_active);
|
||||
}
|
||||
|
||||
transport->ep_disconnect(ep);
|
||||
@ -3012,7 +3014,8 @@ iscsi_if_transport_ep(struct iscsi_transport *transport,
|
||||
break;
|
||||
case ISCSI_UEVENT_TRANSPORT_EP_DISCONNECT:
|
||||
rc = iscsi_if_ep_disconnect(transport,
|
||||
ev->u.ep_disconnect.ep_handle);
|
||||
ev->u.ep_disconnect.ep_handle,
|
||||
false);
|
||||
break;
|
||||
}
|
||||
return rc;
|
||||
@ -3737,7 +3740,7 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group)
|
||||
conn = iscsi_conn_lookup(ev->u.b_conn.sid, ev->u.b_conn.cid);
|
||||
|
||||
if (conn && conn->ep)
|
||||
iscsi_if_ep_disconnect(transport, conn->ep->id);
|
||||
iscsi_if_ep_disconnect(transport, conn->ep->id, true);
|
||||
|
||||
if (!session || !conn) {
|
||||
err = -EINVAL;
|
||||
@ -4656,6 +4659,7 @@ iscsi_register_transport(struct iscsi_transport *tt)
|
||||
int err;
|
||||
|
||||
BUG_ON(!tt);
|
||||
WARN_ON(tt->ep_disconnect && !tt->unbind_conn);
|
||||
|
||||
priv = iscsi_if_transport_lookup(tt);
|
||||
if (priv)
|
||||
|
@ -431,6 +431,7 @@ extern int iscsi_conn_start(struct iscsi_cls_conn *);
|
||||
extern void iscsi_conn_stop(struct iscsi_cls_conn *, int);
|
||||
extern int iscsi_conn_bind(struct iscsi_cls_session *, struct iscsi_cls_conn *,
|
||||
int);
|
||||
extern void iscsi_conn_unbind(struct iscsi_cls_conn *cls_conn, bool is_active);
|
||||
extern void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err);
|
||||
extern void iscsi_session_failure(struct iscsi_session *session,
|
||||
enum iscsi_err err);
|
||||
|
@ -82,6 +82,7 @@ struct iscsi_transport {
|
||||
void (*destroy_session) (struct iscsi_cls_session *session);
|
||||
struct iscsi_cls_conn *(*create_conn) (struct iscsi_cls_session *sess,
|
||||
uint32_t cid);
|
||||
void (*unbind_conn) (struct iscsi_cls_conn *conn, bool is_active);
|
||||
int (*bind_conn) (struct iscsi_cls_session *session,
|
||||
struct iscsi_cls_conn *cls_conn,
|
||||
uint64_t transport_eph, int is_leading);
|
||||
|
Loading…
Reference in New Issue
Block a user