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 */
|
/* connection management */
|
||||||
.create_conn = iscsi_iser_conn_create,
|
.create_conn = iscsi_iser_conn_create,
|
||||||
.bind_conn = iscsi_iser_conn_bind,
|
.bind_conn = iscsi_iser_conn_bind,
|
||||||
|
.unbind_conn = iscsi_conn_unbind,
|
||||||
.destroy_conn = iscsi_conn_teardown,
|
.destroy_conn = iscsi_conn_teardown,
|
||||||
.attr_is_visible = iser_attr_is_visible,
|
.attr_is_visible = iser_attr_is_visible,
|
||||||
.set_param = iscsi_iser_set_param,
|
.set_param = iscsi_iser_set_param,
|
||||||
|
@ -5807,6 +5807,7 @@ struct iscsi_transport beiscsi_iscsi_transport = {
|
|||||||
.destroy_session = beiscsi_session_destroy,
|
.destroy_session = beiscsi_session_destroy,
|
||||||
.create_conn = beiscsi_conn_create,
|
.create_conn = beiscsi_conn_create,
|
||||||
.bind_conn = beiscsi_conn_bind,
|
.bind_conn = beiscsi_conn_bind,
|
||||||
|
.unbind_conn = iscsi_conn_unbind,
|
||||||
.destroy_conn = iscsi_conn_teardown,
|
.destroy_conn = iscsi_conn_teardown,
|
||||||
.attr_is_visible = beiscsi_attr_is_visible,
|
.attr_is_visible = beiscsi_attr_is_visible,
|
||||||
.set_iface_param = beiscsi_iface_set_param,
|
.set_iface_param = beiscsi_iface_set_param,
|
||||||
|
@ -2276,6 +2276,7 @@ struct iscsi_transport bnx2i_iscsi_transport = {
|
|||||||
.destroy_session = bnx2i_session_destroy,
|
.destroy_session = bnx2i_session_destroy,
|
||||||
.create_conn = bnx2i_conn_create,
|
.create_conn = bnx2i_conn_create,
|
||||||
.bind_conn = bnx2i_conn_bind,
|
.bind_conn = bnx2i_conn_bind,
|
||||||
|
.unbind_conn = iscsi_conn_unbind,
|
||||||
.destroy_conn = bnx2i_conn_destroy,
|
.destroy_conn = bnx2i_conn_destroy,
|
||||||
.attr_is_visible = bnx2i_attr_is_visible,
|
.attr_is_visible = bnx2i_attr_is_visible,
|
||||||
.set_param = iscsi_set_param,
|
.set_param = iscsi_set_param,
|
||||||
|
@ -117,6 +117,7 @@ static struct iscsi_transport cxgb3i_iscsi_transport = {
|
|||||||
/* connection management */
|
/* connection management */
|
||||||
.create_conn = cxgbi_create_conn,
|
.create_conn = cxgbi_create_conn,
|
||||||
.bind_conn = cxgbi_bind_conn,
|
.bind_conn = cxgbi_bind_conn,
|
||||||
|
.unbind_conn = iscsi_conn_unbind,
|
||||||
.destroy_conn = iscsi_tcp_conn_teardown,
|
.destroy_conn = iscsi_tcp_conn_teardown,
|
||||||
.start_conn = iscsi_conn_start,
|
.start_conn = iscsi_conn_start,
|
||||||
.stop_conn = iscsi_conn_stop,
|
.stop_conn = iscsi_conn_stop,
|
||||||
|
@ -134,6 +134,7 @@ static struct iscsi_transport cxgb4i_iscsi_transport = {
|
|||||||
/* connection management */
|
/* connection management */
|
||||||
.create_conn = cxgbi_create_conn,
|
.create_conn = cxgbi_create_conn,
|
||||||
.bind_conn = cxgbi_bind_conn,
|
.bind_conn = cxgbi_bind_conn,
|
||||||
|
.unbind_conn = iscsi_conn_unbind,
|
||||||
.destroy_conn = iscsi_tcp_conn_teardown,
|
.destroy_conn = iscsi_tcp_conn_teardown,
|
||||||
.start_conn = iscsi_conn_start,
|
.start_conn = iscsi_conn_start,
|
||||||
.stop_conn = iscsi_conn_stop,
|
.stop_conn = iscsi_conn_stop,
|
||||||
|
@ -1387,22 +1387,31 @@ void iscsi_session_failure(struct iscsi_session *session,
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(iscsi_session_failure);
|
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;
|
struct iscsi_session *session = conn->session;
|
||||||
|
|
||||||
spin_lock_bh(&session->frwd_lock);
|
if (session->state == ISCSI_STATE_FAILED)
|
||||||
if (session->state == ISCSI_STATE_FAILED) {
|
return false;
|
||||||
spin_unlock_bh(&session->frwd_lock);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (conn->stop_stage == 0)
|
if (conn->stop_stage == 0)
|
||||||
session->state = ISCSI_STATE_FAILED;
|
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_tx);
|
||||||
set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx);
|
set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx);
|
||||||
|
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);
|
iscsi_conn_error_event(conn->cls_conn, err);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(iscsi_conn_failure);
|
EXPORT_SYMBOL_GPL(iscsi_conn_failure);
|
||||||
@ -2180,6 +2189,51 @@ done:
|
|||||||
spin_unlock(&session->frwd_lock);
|
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,
|
static void iscsi_prep_abort_task_pdu(struct iscsi_task *task,
|
||||||
struct iscsi_tm *hdr)
|
struct iscsi_tm *hdr)
|
||||||
{
|
{
|
||||||
|
@ -1401,6 +1401,7 @@ struct iscsi_transport qedi_iscsi_transport = {
|
|||||||
.destroy_session = qedi_session_destroy,
|
.destroy_session = qedi_session_destroy,
|
||||||
.create_conn = qedi_conn_create,
|
.create_conn = qedi_conn_create,
|
||||||
.bind_conn = qedi_conn_bind,
|
.bind_conn = qedi_conn_bind,
|
||||||
|
.unbind_conn = iscsi_conn_unbind,
|
||||||
.start_conn = qedi_conn_start,
|
.start_conn = qedi_conn_start,
|
||||||
.stop_conn = iscsi_conn_stop,
|
.stop_conn = iscsi_conn_stop,
|
||||||
.destroy_conn = qedi_conn_destroy,
|
.destroy_conn = qedi_conn_destroy,
|
||||||
|
@ -259,6 +259,7 @@ static struct iscsi_transport qla4xxx_iscsi_transport = {
|
|||||||
.start_conn = qla4xxx_conn_start,
|
.start_conn = qla4xxx_conn_start,
|
||||||
.create_conn = qla4xxx_conn_create,
|
.create_conn = qla4xxx_conn_create,
|
||||||
.bind_conn = qla4xxx_conn_bind,
|
.bind_conn = qla4xxx_conn_bind,
|
||||||
|
.unbind_conn = iscsi_conn_unbind,
|
||||||
.stop_conn = iscsi_conn_stop,
|
.stop_conn = iscsi_conn_stop,
|
||||||
.destroy_conn = qla4xxx_conn_destroy,
|
.destroy_conn = qla4xxx_conn_destroy,
|
||||||
.set_param = iscsi_set_param,
|
.set_param = iscsi_set_param,
|
||||||
|
@ -2964,7 +2964,7 @@ release_host:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int iscsi_if_ep_disconnect(struct iscsi_transport *transport,
|
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_cls_conn *conn;
|
||||||
struct iscsi_endpoint *ep;
|
struct iscsi_endpoint *ep;
|
||||||
@ -2981,6 +2981,8 @@ static int iscsi_if_ep_disconnect(struct iscsi_transport *transport,
|
|||||||
conn->ep = NULL;
|
conn->ep = NULL;
|
||||||
mutex_unlock(&conn->ep_mutex);
|
mutex_unlock(&conn->ep_mutex);
|
||||||
conn->state = ISCSI_CONN_FAILED;
|
conn->state = ISCSI_CONN_FAILED;
|
||||||
|
|
||||||
|
transport->unbind_conn(conn, is_active);
|
||||||
}
|
}
|
||||||
|
|
||||||
transport->ep_disconnect(ep);
|
transport->ep_disconnect(ep);
|
||||||
@ -3012,7 +3014,8 @@ iscsi_if_transport_ep(struct iscsi_transport *transport,
|
|||||||
break;
|
break;
|
||||||
case ISCSI_UEVENT_TRANSPORT_EP_DISCONNECT:
|
case ISCSI_UEVENT_TRANSPORT_EP_DISCONNECT:
|
||||||
rc = iscsi_if_ep_disconnect(transport,
|
rc = iscsi_if_ep_disconnect(transport,
|
||||||
ev->u.ep_disconnect.ep_handle);
|
ev->u.ep_disconnect.ep_handle,
|
||||||
|
false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return rc;
|
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);
|
conn = iscsi_conn_lookup(ev->u.b_conn.sid, ev->u.b_conn.cid);
|
||||||
|
|
||||||
if (conn && conn->ep)
|
if (conn && conn->ep)
|
||||||
iscsi_if_ep_disconnect(transport, conn->ep->id);
|
iscsi_if_ep_disconnect(transport, conn->ep->id, true);
|
||||||
|
|
||||||
if (!session || !conn) {
|
if (!session || !conn) {
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
@ -4656,6 +4659,7 @@ iscsi_register_transport(struct iscsi_transport *tt)
|
|||||||
int err;
|
int err;
|
||||||
|
|
||||||
BUG_ON(!tt);
|
BUG_ON(!tt);
|
||||||
|
WARN_ON(tt->ep_disconnect && !tt->unbind_conn);
|
||||||
|
|
||||||
priv = iscsi_if_transport_lookup(tt);
|
priv = iscsi_if_transport_lookup(tt);
|
||||||
if (priv)
|
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 void iscsi_conn_stop(struct iscsi_cls_conn *, int);
|
||||||
extern int iscsi_conn_bind(struct iscsi_cls_session *, struct iscsi_cls_conn *,
|
extern int iscsi_conn_bind(struct iscsi_cls_session *, struct iscsi_cls_conn *,
|
||||||
int);
|
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_conn_failure(struct iscsi_conn *conn, enum iscsi_err err);
|
||||||
extern void iscsi_session_failure(struct iscsi_session *session,
|
extern void iscsi_session_failure(struct iscsi_session *session,
|
||||||
enum iscsi_err err);
|
enum iscsi_err err);
|
||||||
|
@ -82,6 +82,7 @@ struct iscsi_transport {
|
|||||||
void (*destroy_session) (struct iscsi_cls_session *session);
|
void (*destroy_session) (struct iscsi_cls_session *session);
|
||||||
struct iscsi_cls_conn *(*create_conn) (struct iscsi_cls_session *sess,
|
struct iscsi_cls_conn *(*create_conn) (struct iscsi_cls_session *sess,
|
||||||
uint32_t cid);
|
uint32_t cid);
|
||||||
|
void (*unbind_conn) (struct iscsi_cls_conn *conn, bool is_active);
|
||||||
int (*bind_conn) (struct iscsi_cls_session *session,
|
int (*bind_conn) (struct iscsi_cls_session *session,
|
||||||
struct iscsi_cls_conn *cls_conn,
|
struct iscsi_cls_conn *cls_conn,
|
||||||
uint64_t transport_eph, int is_leading);
|
uint64_t transport_eph, int is_leading);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user