Commit 9e67600e authored by Gulam Mohamed's avatar Gulam Mohamed Committed by Martin K. Petersen

scsi: iscsi: Fix race condition between login and sync thread

A kernel panic was observed due to a timing issue between the sync thread
and the initiator processing a login response from the target. The session
reopen can be invoked both from the session sync thread when iscsid
restarts and from iscsid through the error handler. Before the initiator
receives the response to a login, another reopen request can be sent from
the error handler/sync session. When the initial login response is
subsequently processed, the connection has been closed and the socket has
been released.

To fix this a new connection state, ISCSI_CONN_BOUND, is added:

 - Set the connection state value to ISCSI_CONN_DOWN upon
   iscsi_if_ep_disconnect() and iscsi_if_stop_conn()

 - Set the connection state to the newly created value ISCSI_CONN_BOUND
   after bind connection (transport->bind_conn())

 - In iscsi_set_param(), return -ENOTCONN if the connection state is not
   either ISCSI_CONN_BOUND or ISCSI_CONN_UP

Link: https://lore.kernel.org/r/20210325093248.284678-1-gulam.mohamed@oracle.comReviewed-by: default avatarMike Christie <michael.christie@oracle.com>
Signed-off-by: default avatarGulam Mohamed <gulam.mohamed@oracle.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>

index 91074fd97f64..f4bf62b007a0 100644
parent 36fa766f
...@@ -2471,6 +2471,7 @@ static void iscsi_if_stop_conn(struct iscsi_cls_conn *conn, int flag) ...@@ -2471,6 +2471,7 @@ static void iscsi_if_stop_conn(struct iscsi_cls_conn *conn, int flag)
*/ */
mutex_lock(&conn_mutex); mutex_lock(&conn_mutex);
conn->transport->stop_conn(conn, flag); conn->transport->stop_conn(conn, flag);
conn->state = ISCSI_CONN_DOWN;
mutex_unlock(&conn_mutex); mutex_unlock(&conn_mutex);
} }
...@@ -2894,6 +2895,13 @@ iscsi_set_param(struct iscsi_transport *transport, struct iscsi_uevent *ev) ...@@ -2894,6 +2895,13 @@ iscsi_set_param(struct iscsi_transport *transport, struct iscsi_uevent *ev)
default: default:
err = transport->set_param(conn, ev->u.set_param.param, err = transport->set_param(conn, ev->u.set_param.param,
data, ev->u.set_param.len); data, ev->u.set_param.len);
if ((conn->state == ISCSI_CONN_BOUND) ||
(conn->state == ISCSI_CONN_UP)) {
err = transport->set_param(conn, ev->u.set_param.param,
data, ev->u.set_param.len);
} else {
return -ENOTCONN;
}
} }
return err; return err;
...@@ -2953,6 +2961,7 @@ static int iscsi_if_ep_disconnect(struct iscsi_transport *transport, ...@@ -2953,6 +2961,7 @@ static int iscsi_if_ep_disconnect(struct iscsi_transport *transport,
mutex_lock(&conn->ep_mutex); mutex_lock(&conn->ep_mutex);
conn->ep = NULL; conn->ep = NULL;
mutex_unlock(&conn->ep_mutex); mutex_unlock(&conn->ep_mutex);
conn->state = ISCSI_CONN_DOWN;
} }
transport->ep_disconnect(ep); transport->ep_disconnect(ep);
...@@ -3713,6 +3722,8 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group) ...@@ -3713,6 +3722,8 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group)
ev->r.retcode = transport->bind_conn(session, conn, ev->r.retcode = transport->bind_conn(session, conn,
ev->u.b_conn.transport_eph, ev->u.b_conn.transport_eph,
ev->u.b_conn.is_leading); ev->u.b_conn.is_leading);
if (!ev->r.retcode)
conn->state = ISCSI_CONN_BOUND;
mutex_unlock(&conn_mutex); mutex_unlock(&conn_mutex);
if (ev->r.retcode || !transport->ep_connect) if (ev->r.retcode || !transport->ep_connect)
...@@ -3944,7 +3955,8 @@ iscsi_conn_attr(local_ipaddr, ISCSI_PARAM_LOCAL_IPADDR); ...@@ -3944,7 +3955,8 @@ iscsi_conn_attr(local_ipaddr, ISCSI_PARAM_LOCAL_IPADDR);
static const char *const connection_state_names[] = { static const char *const connection_state_names[] = {
[ISCSI_CONN_UP] = "up", [ISCSI_CONN_UP] = "up",
[ISCSI_CONN_DOWN] = "down", [ISCSI_CONN_DOWN] = "down",
[ISCSI_CONN_FAILED] = "failed" [ISCSI_CONN_FAILED] = "failed",
[ISCSI_CONN_BOUND] = "bound"
}; };
static ssize_t show_conn_state(struct device *dev, static ssize_t show_conn_state(struct device *dev,
......
...@@ -193,6 +193,7 @@ enum iscsi_connection_state { ...@@ -193,6 +193,7 @@ enum iscsi_connection_state {
ISCSI_CONN_UP = 0, ISCSI_CONN_UP = 0,
ISCSI_CONN_DOWN, ISCSI_CONN_DOWN,
ISCSI_CONN_FAILED, ISCSI_CONN_FAILED,
ISCSI_CONN_BOUND,
}; };
struct iscsi_cls_conn { struct iscsi_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