summaryrefslogtreecommitdiff
path: root/drivers/target/iscsi/iscsi_target_nego.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/target/iscsi/iscsi_target_nego.c')
-rw-r--r--drivers/target/iscsi/iscsi_target_nego.c405
1 files changed, 227 insertions, 178 deletions
diff --git a/drivers/target/iscsi/iscsi_target_nego.c b/drivers/target/iscsi/iscsi_target_nego.c
index 8a5e8d17a942..832588f21f91 100644
--- a/drivers/target/iscsi/iscsi_target_nego.c
+++ b/drivers/target/iscsi/iscsi_target_nego.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*******************************************************************************
* This file contains main functions related to iSCSI Parameter negotiation.
*
@@ -5,15 +6,6 @@
*
* Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
******************************************************************************/
#include <linux/ctype.h>
@@ -21,6 +13,7 @@
#include <linux/slab.h>
#include <linux/sched/signal.h>
#include <net/sock.h>
+#include <trace/events/sock.h>
#include <scsi/iscsi_proto.h>
#include <target/target_core_base.h>
#include <target/target_core_fabric.h>
@@ -36,7 +29,6 @@
#include "iscsi_target_auth.h"
#define MAX_LOGIN_PDUS 7
-#define TEXT_LEN 4096
void convert_null_to_semi(char *buf, int len)
{
@@ -71,31 +63,34 @@ int extract_param(
int len;
if (!in_buf || !pattern || !out_buf || !type)
- return -1;
+ return -EINVAL;
ptr = strstr(in_buf, pattern);
if (!ptr)
- return -1;
+ return -ENOENT;
ptr = strstr(ptr, "=");
if (!ptr)
- return -1;
+ return -EINVAL;
ptr += 1;
if (*ptr == '0' && (*(ptr+1) == 'x' || *(ptr+1) == 'X')) {
ptr += 2; /* skip 0x */
*type = HEX;
+ } else if (*ptr == '0' && (*(ptr+1) == 'b' || *(ptr+1) == 'B')) {
+ ptr += 2; /* skip 0b */
+ *type = BASE64;
} else
*type = DECIMAL;
len = strlen_semi(ptr);
if (len < 0)
- return -1;
+ return -EINVAL;
if (len >= max_length) {
pr_err("Length of input: %d exceeds max_length:"
" %d\n", len, max_length);
- return -1;
+ return -EINVAL;
}
memcpy(out_buf, ptr, len);
out_buf[len] = '\0';
@@ -103,55 +98,44 @@ int extract_param(
return 0;
}
+static struct iscsi_node_auth *iscsi_get_node_auth(struct iscsit_conn *conn)
+{
+ struct iscsi_portal_group *tpg;
+ struct iscsi_node_acl *nacl;
+ struct se_node_acl *se_nacl;
+
+ if (conn->sess->sess_ops->SessionType)
+ return &iscsit_global->discovery_acl.node_auth;
+
+ se_nacl = conn->sess->se_sess->se_node_acl;
+ if (!se_nacl) {
+ pr_err("Unable to locate struct se_node_acl for CHAP auth\n");
+ return NULL;
+ }
+
+ if (se_nacl->dynamic_node_acl) {
+ tpg = to_iscsi_tpg(se_nacl->se_tpg);
+ return &tpg->tpg_demo_auth;
+ }
+
+ nacl = to_iscsi_nacl(se_nacl);
+
+ return &nacl->node_auth;
+}
+
static u32 iscsi_handle_authentication(
- struct iscsi_conn *conn,
+ struct iscsit_conn *conn,
char *in_buf,
char *out_buf,
int in_length,
int *out_length,
unsigned char *authtype)
{
- struct iscsi_session *sess = conn->sess;
struct iscsi_node_auth *auth;
- struct iscsi_node_acl *iscsi_nacl;
- struct iscsi_portal_group *iscsi_tpg;
- struct se_node_acl *se_nacl;
-
- if (!sess->sess_ops->SessionType) {
- /*
- * For SessionType=Normal
- */
- se_nacl = conn->sess->se_sess->se_node_acl;
- if (!se_nacl) {
- pr_err("Unable to locate struct se_node_acl for"
- " CHAP auth\n");
- return -1;
- }
- iscsi_nacl = container_of(se_nacl, struct iscsi_node_acl,
- se_node_acl);
- if (!iscsi_nacl) {
- pr_err("Unable to locate struct iscsi_node_acl for"
- " CHAP auth\n");
- return -1;
- }
- if (se_nacl->dynamic_node_acl) {
- iscsi_tpg = container_of(se_nacl->se_tpg,
- struct iscsi_portal_group, tpg_se_tpg);
-
- auth = &iscsi_tpg->tpg_demo_auth;
- } else {
- iscsi_nacl = container_of(se_nacl, struct iscsi_node_acl,
- se_node_acl);
-
- auth = &iscsi_nacl->node_auth;
- }
- } else {
- /*
- * For SessionType=Discovery
- */
- auth = &iscsit_global->discovery_acl.node_auth;
- }
+ auth = iscsi_get_node_auth(conn);
+ if (!auth)
+ return -1;
if (strstr("CHAP", authtype))
strcpy(conn->sess->auth_type, "CHAP");
@@ -160,31 +144,20 @@ static u32 iscsi_handle_authentication(
if (strstr("None", authtype))
return 1;
-#ifdef CANSRP
- else if (strstr("SRP", authtype))
- return srp_main_loop(conn, auth, in_buf, out_buf,
- &in_length, out_length);
-#endif
else if (strstr("CHAP", authtype))
return chap_main_loop(conn, auth, in_buf, out_buf,
&in_length, out_length);
- else if (strstr("SPKM1", authtype))
- return 2;
- else if (strstr("SPKM2", authtype))
- return 2;
- else if (strstr("KRB5", authtype))
- return 2;
- else
- return 2;
+ /* SRP, SPKM1, SPKM2 and KRB5 are unsupported */
+ return 2;
}
-static void iscsi_remove_failed_auth_entry(struct iscsi_conn *conn)
+static void iscsi_remove_failed_auth_entry(struct iscsit_conn *conn)
{
kfree(conn->auth_protocol);
}
int iscsi_target_check_login_request(
- struct iscsi_conn *conn,
+ struct iscsit_conn *conn,
struct iscsi_login *login)
{
int req_csg, req_nsg;
@@ -239,7 +212,7 @@ int iscsi_target_check_login_request(
if ((login_req->max_version != login->version_max) ||
(login_req->min_version != login->version_min)) {
- pr_err("Login request changed Version Max/Nin"
+ pr_err("Login request changed Version Max/Min"
" unexpectedly to 0x%02x/0x%02x, protocol error\n",
login_req->max_version, login_req->min_version);
iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
@@ -275,7 +248,7 @@ int iscsi_target_check_login_request(
EXPORT_SYMBOL(iscsi_target_check_login_request);
static int iscsi_target_check_first_request(
- struct iscsi_conn *conn,
+ struct iscsit_conn *conn,
struct iscsi_login *login)
{
struct iscsi_param *param = NULL;
@@ -342,7 +315,7 @@ static int iscsi_target_check_first_request(
return 0;
}
-static int iscsi_target_do_tx_login_io(struct iscsi_conn *conn, struct iscsi_login *login)
+static int iscsi_target_do_tx_login_io(struct iscsit_conn *conn, struct iscsi_login *login)
{
u32 padding = 0;
struct iscsi_login_rsp *login_rsp;
@@ -409,9 +382,10 @@ err:
static void iscsi_target_sk_data_ready(struct sock *sk)
{
- struct iscsi_conn *conn = sk->sk_user_data;
+ struct iscsit_conn *conn = sk->sk_user_data;
bool rc;
+ trace_sk_data_ready(sk);
pr_debug("Entering iscsi_target_sk_data_ready: conn: %p\n", conn);
write_lock_bh(&sk->sk_callback_lock);
@@ -448,7 +422,7 @@ static void iscsi_target_sk_data_ready(struct sock *sk)
static void iscsi_target_sk_state_change(struct sock *);
-static void iscsi_target_set_sock_callbacks(struct iscsi_conn *conn)
+static void iscsi_target_set_sock_callbacks(struct iscsit_conn *conn)
{
struct sock *sk;
@@ -470,7 +444,7 @@ static void iscsi_target_set_sock_callbacks(struct iscsi_conn *conn)
sk->sk_rcvtimeo = TA_LOGIN_TIMEOUT * HZ;
}
-static void iscsi_target_restore_sock_callbacks(struct iscsi_conn *conn)
+static void iscsi_target_restore_sock_callbacks(struct iscsit_conn *conn)
{
struct sock *sk;
@@ -494,19 +468,19 @@ static void iscsi_target_restore_sock_callbacks(struct iscsi_conn *conn)
sk->sk_rcvtimeo = MAX_SCHEDULE_TIMEOUT;
}
-static int iscsi_target_do_login(struct iscsi_conn *, struct iscsi_login *);
+static int iscsi_target_do_login(struct iscsit_conn *, struct iscsi_login *);
static bool __iscsi_target_sk_check_close(struct sock *sk)
{
if (sk->sk_state == TCP_CLOSE_WAIT || sk->sk_state == TCP_CLOSE) {
pr_debug("__iscsi_target_sk_check_close: TCP_CLOSE_WAIT|TCP_CLOSE,"
- "returning FALSE\n");
+ "returning TRUE\n");
return true;
}
return false;
}
-static bool iscsi_target_sk_check_close(struct iscsi_conn *conn)
+static bool iscsi_target_sk_check_close(struct iscsit_conn *conn)
{
bool state = false;
@@ -521,7 +495,7 @@ static bool iscsi_target_sk_check_close(struct iscsi_conn *conn)
return state;
}
-static bool iscsi_target_sk_check_flag(struct iscsi_conn *conn, unsigned int flag)
+static bool iscsi_target_sk_check_flag(struct iscsit_conn *conn, unsigned int flag)
{
bool state = false;
@@ -535,7 +509,7 @@ static bool iscsi_target_sk_check_flag(struct iscsi_conn *conn, unsigned int fla
return state;
}
-static bool iscsi_target_sk_check_and_clear(struct iscsi_conn *conn, unsigned int flag)
+static bool iscsi_target_sk_check_and_clear(struct iscsit_conn *conn, unsigned int flag)
{
bool state = false;
@@ -552,55 +526,38 @@ static bool iscsi_target_sk_check_and_clear(struct iscsi_conn *conn, unsigned in
return state;
}
-static void iscsi_target_login_drop(struct iscsi_conn *conn, struct iscsi_login *login)
+static void iscsi_target_login_drop(struct iscsit_conn *conn, struct iscsi_login *login)
{
- struct iscsi_np *np = login->np;
bool zero_tsih = login->zero_tsih;
iscsi_remove_failed_auth_entry(conn);
iscsi_target_nego_release(conn);
- iscsi_target_login_sess_out(conn, np, zero_tsih, true);
-}
-
-struct conn_timeout {
- struct timer_list timer;
- struct iscsi_conn *conn;
-};
-
-static void iscsi_target_login_timeout(struct timer_list *t)
-{
- struct conn_timeout *timeout = from_timer(timeout, t, timer);
- struct iscsi_conn *conn = timeout->conn;
-
- pr_debug("Entering iscsi_target_login_timeout >>>>>>>>>>>>>>>>>>>\n");
-
- if (conn->login_kworker) {
- pr_debug("Sending SIGINT to conn->login_kworker %s/%d\n",
- conn->login_kworker->comm, conn->login_kworker->pid);
- send_sig(SIGINT, conn->login_kworker, 1);
- }
+ iscsi_target_login_sess_out(conn, zero_tsih, true);
}
static void iscsi_target_do_login_rx(struct work_struct *work)
{
- struct iscsi_conn *conn = container_of(work,
- struct iscsi_conn, login_work.work);
+ struct iscsit_conn *conn = container_of(work,
+ struct iscsit_conn, login_work.work);
struct iscsi_login *login = conn->login;
struct iscsi_np *np = login->np;
struct iscsi_portal_group *tpg = conn->tpg;
struct iscsi_tpg_np *tpg_np = conn->tpg_np;
- struct conn_timeout timeout;
int rc, zero_tsih = login->zero_tsih;
bool state;
pr_debug("entering iscsi_target_do_login_rx, conn: %p, %s:%d\n",
conn, current->comm, current->pid);
+
+ spin_lock(&conn->login_worker_lock);
+ set_bit(LOGIN_FLAGS_WORKER_RUNNING, &conn->login_flags);
+ spin_unlock(&conn->login_worker_lock);
/*
* If iscsi_target_do_login_rx() has been invoked by ->sk_data_ready()
* before initial PDU processing in iscsi_target_start_negotiation()
* has completed, go ahead and retry until it's cleared.
*
- * Otherwise if the TCP connection drops while this is occuring,
+ * Otherwise if the TCP connection drops while this is occurring,
* iscsi_target_start_negotiation() will detect the failure, call
* cancel_delayed_work_sync(&conn->login_work), and cleanup the
* remaining iscsi connection resources from iscsi_np process context.
@@ -624,19 +581,16 @@ static void iscsi_target_do_login_rx(struct work_struct *work)
goto err;
}
- conn->login_kworker = current;
allow_signal(SIGINT);
-
- timeout.conn = conn;
- timer_setup_on_stack(&timeout.timer, iscsi_target_login_timeout, 0);
- mod_timer(&timeout.timer, jiffies + TA_LOGIN_TIMEOUT * HZ);
- pr_debug("Starting login timer for %s/%d\n", current->comm, current->pid);
+ rc = iscsit_set_login_timer_kworker(conn, current);
+ if (rc < 0) {
+ /* The login timer has already expired */
+ pr_debug("iscsi_target_do_login_rx, login failed\n");
+ goto err;
+ }
rc = conn->conn_transport->iscsit_get_login_rx(conn, login);
- del_timer_sync(&timeout.timer);
- destroy_timer_on_stack(&timeout.timer);
flush_signals(current);
- conn->login_kworker = NULL;
if (rc < 0)
goto err;
@@ -644,13 +598,47 @@ static void iscsi_target_do_login_rx(struct work_struct *work)
pr_debug("iscsi_target_do_login_rx after rx_login_io, %p, %s:%d\n",
conn, current->comm, current->pid);
+ /*
+ * LOGIN_FLAGS_READ_ACTIVE is cleared so that sk_data_ready
+ * could be triggered again after this.
+ *
+ * LOGIN_FLAGS_WRITE_ACTIVE is cleared after we successfully
+ * process a login PDU, so that sk_state_chage can do login
+ * cleanup as needed if the socket is closed. If a delayed work is
+ * ongoing (LOGIN_FLAGS_WRITE_ACTIVE or LOGIN_FLAGS_READ_ACTIVE),
+ * sk_state_change will leave the cleanup to the delayed work or
+ * it will schedule a delayed work to do cleanup.
+ */
+ if (conn->sock) {
+ struct sock *sk = conn->sock->sk;
+
+ write_lock_bh(&sk->sk_callback_lock);
+ if (!test_bit(LOGIN_FLAGS_INITIAL_PDU, &conn->login_flags)) {
+ clear_bit(LOGIN_FLAGS_READ_ACTIVE, &conn->login_flags);
+ set_bit(LOGIN_FLAGS_WRITE_ACTIVE, &conn->login_flags);
+ }
+ write_unlock_bh(&sk->sk_callback_lock);
+ }
+
rc = iscsi_target_do_login(conn, login);
if (rc < 0) {
goto err;
} else if (!rc) {
- if (iscsi_target_sk_check_and_clear(conn, LOGIN_FLAGS_READ_ACTIVE))
+ if (iscsi_target_sk_check_and_clear(conn,
+ LOGIN_FLAGS_WRITE_ACTIVE))
+ goto err;
+
+ /*
+ * Set the login timer thread pointer to NULL to prevent the
+ * login process from getting stuck if the initiator
+ * stops sending data.
+ */
+ rc = iscsit_set_login_timer_kworker(conn, NULL);
+ if (rc < 0)
goto err;
} else if (rc == 1) {
+ iscsit_stop_login_timer(conn);
+ cancel_delayed_work(&conn->login_work);
iscsi_target_nego_release(conn);
iscsi_post_login_handler(np, conn, zero_tsih);
iscsit_deaccess_np(np, tpg, tpg_np);
@@ -659,13 +647,15 @@ static void iscsi_target_do_login_rx(struct work_struct *work)
err:
iscsi_target_restore_sock_callbacks(conn);
+ iscsit_stop_login_timer(conn);
+ cancel_delayed_work(&conn->login_work);
iscsi_target_login_drop(conn, login);
iscsit_deaccess_np(np, tpg, tpg_np);
}
static void iscsi_target_sk_state_change(struct sock *sk)
{
- struct iscsi_conn *conn;
+ struct iscsit_conn *conn;
void (*orig_state_change)(struct sock *);
bool state;
@@ -689,9 +679,10 @@ static void iscsi_target_sk_state_change(struct sock *sk)
state = __iscsi_target_sk_check_close(sk);
pr_debug("__iscsi_target_sk_close_change: state: %d\n", state);
- if (test_bit(LOGIN_FLAGS_READ_ACTIVE, &conn->login_flags)) {
- pr_debug("Got LOGIN_FLAGS_READ_ACTIVE=1 sk_state_change"
- " conn: %p\n", conn);
+ if (test_bit(LOGIN_FLAGS_READ_ACTIVE, &conn->login_flags) ||
+ test_bit(LOGIN_FLAGS_WRITE_ACTIVE, &conn->login_flags)) {
+ pr_debug("Got LOGIN_FLAGS_{READ|WRITE}_ACTIVE=1"
+ " sk_state_change conn: %p\n", conn);
if (state)
set_bit(LOGIN_FLAGS_CLOSED, &conn->login_flags);
write_unlock_bh(&sk->sk_callback_lock);
@@ -743,7 +734,7 @@ static void iscsi_target_sk_state_change(struct sock *sk)
* ISID/TSIH combinations.
*/
static int iscsi_target_check_for_existing_instances(
- struct iscsi_conn *conn,
+ struct iscsit_conn *conn,
struct iscsi_login *login)
{
if (login->checked_for_existing)
@@ -759,7 +750,7 @@ static int iscsi_target_check_for_existing_instances(
}
static int iscsi_target_do_authentication(
- struct iscsi_conn *conn,
+ struct iscsit_conn *conn,
struct iscsi_login *login)
{
int authret;
@@ -817,8 +808,44 @@ static int iscsi_target_do_authentication(
return 0;
}
+bool iscsi_conn_auth_required(struct iscsit_conn *conn)
+{
+ struct iscsi_node_acl *nacl;
+ struct se_node_acl *se_nacl;
+
+ if (conn->sess->sess_ops->SessionType) {
+ /*
+ * For SessionType=Discovery
+ */
+ return conn->tpg->tpg_attrib.authentication;
+ }
+ /*
+ * For SessionType=Normal
+ */
+ se_nacl = conn->sess->se_sess->se_node_acl;
+ if (!se_nacl) {
+ pr_debug("Unknown ACL is trying to connect\n");
+ return true;
+ }
+
+ if (se_nacl->dynamic_node_acl) {
+ pr_debug("Dynamic ACL %s is trying to connect\n",
+ se_nacl->initiatorname);
+ return conn->tpg->tpg_attrib.authentication;
+ }
+
+ pr_debug("Known ACL %s is trying to connect\n",
+ se_nacl->initiatorname);
+
+ nacl = to_iscsi_nacl(se_nacl);
+ if (nacl->node_attrib.authentication == NA_AUTHENTICATION_INHERITED)
+ return conn->tpg->tpg_attrib.authentication;
+
+ return nacl->node_attrib.authentication;
+}
+
static int iscsi_target_handle_csg_zero(
- struct iscsi_conn *conn,
+ struct iscsit_conn *conn,
struct iscsi_login *login)
{
int ret;
@@ -878,22 +905,26 @@ static int iscsi_target_handle_csg_zero(
return -1;
if (!iscsi_check_negotiated_keys(conn->param_list)) {
- if (conn->tpg->tpg_attrib.authentication &&
- !strncmp(param->value, NONE, 4)) {
- pr_err("Initiator sent AuthMethod=None but"
- " Target is enforcing iSCSI Authentication,"
- " login failed.\n");
- iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
- ISCSI_LOGIN_STATUS_AUTH_FAILED);
- return -1;
- }
+ bool auth_required = iscsi_conn_auth_required(conn);
+
+ if (auth_required) {
+ if (!strncmp(param->value, NONE, 4)) {
+ pr_err("Initiator sent AuthMethod=None but"
+ " Target is enforcing iSCSI Authentication,"
+ " login failed.\n");
+ iscsit_tx_login_rsp(conn,
+ ISCSI_STATUS_CLS_INITIATOR_ERR,
+ ISCSI_LOGIN_STATUS_AUTH_FAILED);
+ return -1;
+ }
- if (conn->tpg->tpg_attrib.authentication &&
- !login->auth_complete)
- return 0;
+ if (!login->auth_complete)
+ return 0;
- if (strncmp(param->value, NONE, 4) && !login->auth_complete)
- return 0;
+ if (strncmp(param->value, NONE, 4) &&
+ !login->auth_complete)
+ return 0;
+ }
if ((login_req->flags & ISCSI_FLAG_LOGIN_NEXT_STAGE1) &&
(login_req->flags & ISCSI_FLAG_LOGIN_TRANSIT)) {
@@ -908,7 +939,19 @@ do_auth:
return iscsi_target_do_authentication(conn, login);
}
-static int iscsi_target_handle_csg_one(struct iscsi_conn *conn, struct iscsi_login *login)
+static bool iscsi_conn_authenticated(struct iscsit_conn *conn,
+ struct iscsi_login *login)
+{
+ if (!iscsi_conn_auth_required(conn))
+ return true;
+
+ if (login->auth_complete)
+ return true;
+
+ return false;
+}
+
+static int iscsi_target_handle_csg_one(struct iscsit_conn *conn, struct iscsi_login *login)
{
int ret;
u32 payload_length;
@@ -951,11 +994,10 @@ static int iscsi_target_handle_csg_one(struct iscsi_conn *conn, struct iscsi_log
return -1;
}
- if (!login->auth_complete &&
- conn->tpg->tpg_attrib.authentication) {
+ if (!iscsi_conn_authenticated(conn, login)) {
pr_err("Initiator is requesting CSG: 1, has not been"
- " successfully authenticated, and the Target is"
- " enforcing iSCSI Authentication, login failed.\n");
+ " successfully authenticated, and the Target is"
+ " enforcing iSCSI Authentication, login failed.\n");
iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
ISCSI_LOGIN_STATUS_AUTH_FAILED);
return -1;
@@ -970,7 +1012,14 @@ static int iscsi_target_handle_csg_one(struct iscsi_conn *conn, struct iscsi_log
return 0;
}
-static int iscsi_target_do_login(struct iscsi_conn *conn, struct iscsi_login *login)
+/*
+ * RETURN VALUE:
+ *
+ * 1 = Login successful
+ * -1 = Login failed
+ * 0 = More PDU exchanges required
+ */
+static int iscsi_target_do_login(struct iscsit_conn *conn, struct iscsi_login *login)
{
int pdu_count = 0;
struct iscsi_login_req *login_req;
@@ -1001,7 +1050,7 @@ static int iscsi_target_do_login(struct iscsi_conn *conn, struct iscsi_login *lo
/*
* Check to make sure the TCP connection has not
* dropped asynchronously while session reinstatement
- * was occuring in this kthread context, before
+ * was occurring in this kthread context, before
* transitioning to full feature phase operation.
*/
if (iscsi_target_sk_check_close(conn))
@@ -1056,12 +1105,12 @@ static void iscsi_initiatorname_tolower(
*/
int iscsi_target_locate_portal(
struct iscsi_np *np,
- struct iscsi_conn *conn,
+ struct iscsit_conn *conn,
struct iscsi_login *login)
{
char *i_buf = NULL, *s_buf = NULL, *t_buf = NULL;
char *tmpbuf, *start = NULL, *end = NULL, *key, *value;
- struct iscsi_session *sess = conn->sess;
+ struct iscsit_session *sess = conn->sess;
struct iscsi_tiqn *tiqn;
struct iscsi_tpg_np *tpg_np = NULL;
struct iscsi_login_req *login_req;
@@ -1073,18 +1122,17 @@ int iscsi_target_locate_portal(
iscsi_target_set_sock_callbacks(conn);
login->np = np;
+ conn->tpg = NULL;
login_req = (struct iscsi_login_req *) login->req;
payload_length = ntoh24(login_req->dlength);
- tmpbuf = kzalloc(payload_length + 1, GFP_KERNEL);
+ tmpbuf = kmemdup_nul(login->req_buf, payload_length, GFP_KERNEL);
if (!tmpbuf) {
pr_err("Unable to allocate memory for tmpbuf.\n");
return -1;
}
- memcpy(tmpbuf, login->req_buf, payload_length);
- tmpbuf[payload_length] = '\0';
start = tmpbuf;
end = (start + payload_length);
@@ -1142,26 +1190,20 @@ int iscsi_target_locate_portal(
*/
sessiontype = strncmp(s_buf, DISCOVERY, 9);
if (!sessiontype) {
- conn->tpg = iscsit_global->discovery_tpg;
if (!login->leading_connection)
goto get_target;
sess->sess_ops->SessionType = 1;
- /*
- * Setup crc32c modules from libcrypto
- */
- if (iscsi_login_setup_crypto(conn) < 0) {
- pr_err("iscsi_login_setup_crypto() failed\n");
- ret = -1;
- goto out;
- }
+
/*
* Serialize access across the discovery struct iscsi_portal_group to
* process login attempt.
*/
+ conn->tpg = iscsit_global->discovery_tpg;
if (iscsit_access_np(np, conn->tpg) < 0) {
iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
ISCSI_LOGIN_STATUS_SVC_UNAVAILABLE);
+ conn->tpg = NULL;
ret = -1;
goto out;
}
@@ -1209,17 +1251,7 @@ get_target:
}
conn->tpg_np = tpg_np;
pr_debug("Located Portal Group Object: %hu\n", conn->tpg->tpgt);
- /*
- * Setup crc32c modules from libcrypto
- */
- if (iscsi_login_setup_crypto(conn) < 0) {
- pr_err("iscsi_login_setup_crypto() failed\n");
- kref_put(&tpg_np->tpg_np_kref, iscsit_login_kref_put);
- iscsit_put_tiqn_for_login(tiqn);
- conn->tpg = NULL;
- ret = -1;
- goto out;
- }
+
/*
* Serialize access across the struct iscsi_portal_group to
* process login attempt.
@@ -1236,7 +1268,7 @@ get_target:
/*
* conn->sess->node_acl will be set when the referenced
- * struct iscsi_session is located from received ISID+TSIH in
+ * struct iscsit_session is located from received ISID+TSIH in
* iscsi_login_non_zero_tsih_s2().
*/
if (!login->leading_connection) {
@@ -1276,7 +1308,7 @@ get_target:
alloc_tags:
tag_num = max_t(u32, ISCSIT_MIN_TAGS, queue_depth);
tag_num = (tag_num * 2) + ISCSIT_EXTRA_TAGS;
- tag_size = sizeof(struct iscsi_cmd) + conn->conn_transport->priv_size;
+ tag_size = sizeof(struct iscsit_cmd) + conn->conn_transport->priv_size;
ret = transport_alloc_session_tags(sess->se_sess, tag_num, tag_size);
if (ret < 0) {
@@ -1291,7 +1323,7 @@ out:
int iscsi_target_start_negotiation(
struct iscsi_login *login,
- struct iscsi_conn *conn)
+ struct iscsit_conn *conn)
{
int ret;
@@ -1313,21 +1345,38 @@ int iscsi_target_start_negotiation(
* and perform connection cleanup now.
*/
ret = iscsi_target_do_login(conn, login);
- if (!ret && iscsi_target_sk_check_and_clear(conn, LOGIN_FLAGS_INITIAL_PDU))
- ret = -1;
+ if (!ret) {
+ spin_lock(&conn->login_worker_lock);
+
+ if (iscsi_target_sk_check_and_clear(conn, LOGIN_FLAGS_INITIAL_PDU))
+ ret = -1;
+ else if (!test_bit(LOGIN_FLAGS_WORKER_RUNNING, &conn->login_flags)) {
+ if (iscsit_set_login_timer_kworker(conn, NULL) < 0) {
+ /*
+ * The timeout has expired already.
+ * Schedule login_work to perform the cleanup.
+ */
+ schedule_delayed_work(&conn->login_work, 0);
+ }
+ }
+
+ spin_unlock(&conn->login_worker_lock);
+ }
if (ret < 0) {
- cancel_delayed_work_sync(&conn->login_work);
iscsi_target_restore_sock_callbacks(conn);
iscsi_remove_failed_auth_entry(conn);
}
- if (ret != 0)
+ if (ret != 0) {
+ iscsit_stop_login_timer(conn);
+ cancel_delayed_work_sync(&conn->login_work);
iscsi_target_nego_release(conn);
+ }
return ret;
}
-void iscsi_target_nego_release(struct iscsi_conn *conn)
+void iscsi_target_nego_release(struct iscsit_conn *conn)
{
struct iscsi_login *login = conn->conn_login;