162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*******************************************************************************
362306a36Sopenharmony_ci * This file contains main functions related to iSCSI Parameter negotiation.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * (c) Copyright 2007-2013 Datera, Inc.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci ******************************************************************************/
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/ctype.h>
1262306a36Sopenharmony_ci#include <linux/kthread.h>
1362306a36Sopenharmony_ci#include <linux/slab.h>
1462306a36Sopenharmony_ci#include <linux/sched/signal.h>
1562306a36Sopenharmony_ci#include <net/sock.h>
1662306a36Sopenharmony_ci#include <trace/events/sock.h>
1762306a36Sopenharmony_ci#include <scsi/iscsi_proto.h>
1862306a36Sopenharmony_ci#include <target/target_core_base.h>
1962306a36Sopenharmony_ci#include <target/target_core_fabric.h>
2062306a36Sopenharmony_ci#include <target/iscsi/iscsi_transport.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include <target/iscsi/iscsi_target_core.h>
2362306a36Sopenharmony_ci#include "iscsi_target_parameters.h"
2462306a36Sopenharmony_ci#include "iscsi_target_login.h"
2562306a36Sopenharmony_ci#include "iscsi_target_nego.h"
2662306a36Sopenharmony_ci#include "iscsi_target_tpg.h"
2762306a36Sopenharmony_ci#include "iscsi_target_util.h"
2862306a36Sopenharmony_ci#include "iscsi_target.h"
2962306a36Sopenharmony_ci#include "iscsi_target_auth.h"
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define MAX_LOGIN_PDUS  7
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_civoid convert_null_to_semi(char *buf, int len)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	int i;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	for (i = 0; i < len; i++)
3862306a36Sopenharmony_ci		if (buf[i] == '\0')
3962306a36Sopenharmony_ci			buf[i] = ';';
4062306a36Sopenharmony_ci}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic int strlen_semi(char *buf)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	int i = 0;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	while (buf[i] != '\0') {
4762306a36Sopenharmony_ci		if (buf[i] == ';')
4862306a36Sopenharmony_ci			return i;
4962306a36Sopenharmony_ci		i++;
5062306a36Sopenharmony_ci	}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	return -1;
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ciint extract_param(
5662306a36Sopenharmony_ci	const char *in_buf,
5762306a36Sopenharmony_ci	const char *pattern,
5862306a36Sopenharmony_ci	unsigned int max_length,
5962306a36Sopenharmony_ci	char *out_buf,
6062306a36Sopenharmony_ci	unsigned char *type)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	char *ptr;
6362306a36Sopenharmony_ci	int len;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	if (!in_buf || !pattern || !out_buf || !type)
6662306a36Sopenharmony_ci		return -EINVAL;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	ptr = strstr(in_buf, pattern);
6962306a36Sopenharmony_ci	if (!ptr)
7062306a36Sopenharmony_ci		return -ENOENT;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	ptr = strstr(ptr, "=");
7362306a36Sopenharmony_ci	if (!ptr)
7462306a36Sopenharmony_ci		return -EINVAL;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	ptr += 1;
7762306a36Sopenharmony_ci	if (*ptr == '0' && (*(ptr+1) == 'x' || *(ptr+1) == 'X')) {
7862306a36Sopenharmony_ci		ptr += 2; /* skip 0x */
7962306a36Sopenharmony_ci		*type = HEX;
8062306a36Sopenharmony_ci	} else if (*ptr == '0' && (*(ptr+1) == 'b' || *(ptr+1) == 'B')) {
8162306a36Sopenharmony_ci		ptr += 2; /* skip 0b */
8262306a36Sopenharmony_ci		*type = BASE64;
8362306a36Sopenharmony_ci	} else
8462306a36Sopenharmony_ci		*type = DECIMAL;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	len = strlen_semi(ptr);
8762306a36Sopenharmony_ci	if (len < 0)
8862306a36Sopenharmony_ci		return -EINVAL;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	if (len >= max_length) {
9162306a36Sopenharmony_ci		pr_err("Length of input: %d exceeds max_length:"
9262306a36Sopenharmony_ci			" %d\n", len, max_length);
9362306a36Sopenharmony_ci		return -EINVAL;
9462306a36Sopenharmony_ci	}
9562306a36Sopenharmony_ci	memcpy(out_buf, ptr, len);
9662306a36Sopenharmony_ci	out_buf[len] = '\0';
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	return 0;
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic struct iscsi_node_auth *iscsi_get_node_auth(struct iscsit_conn *conn)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	struct iscsi_portal_group *tpg;
10462306a36Sopenharmony_ci	struct iscsi_node_acl *nacl;
10562306a36Sopenharmony_ci	struct se_node_acl *se_nacl;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	if (conn->sess->sess_ops->SessionType)
10862306a36Sopenharmony_ci		return &iscsit_global->discovery_acl.node_auth;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	se_nacl = conn->sess->se_sess->se_node_acl;
11162306a36Sopenharmony_ci	if (!se_nacl) {
11262306a36Sopenharmony_ci		pr_err("Unable to locate struct se_node_acl for CHAP auth\n");
11362306a36Sopenharmony_ci		return NULL;
11462306a36Sopenharmony_ci	}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	if (se_nacl->dynamic_node_acl) {
11762306a36Sopenharmony_ci		tpg = to_iscsi_tpg(se_nacl->se_tpg);
11862306a36Sopenharmony_ci		return &tpg->tpg_demo_auth;
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	nacl = to_iscsi_nacl(se_nacl);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	return &nacl->node_auth;
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic u32 iscsi_handle_authentication(
12762306a36Sopenharmony_ci	struct iscsit_conn *conn,
12862306a36Sopenharmony_ci	char *in_buf,
12962306a36Sopenharmony_ci	char *out_buf,
13062306a36Sopenharmony_ci	int in_length,
13162306a36Sopenharmony_ci	int *out_length,
13262306a36Sopenharmony_ci	unsigned char *authtype)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	struct iscsi_node_auth *auth;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	auth = iscsi_get_node_auth(conn);
13762306a36Sopenharmony_ci	if (!auth)
13862306a36Sopenharmony_ci		return -1;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	if (strstr("CHAP", authtype))
14162306a36Sopenharmony_ci		strcpy(conn->sess->auth_type, "CHAP");
14262306a36Sopenharmony_ci	else
14362306a36Sopenharmony_ci		strcpy(conn->sess->auth_type, NONE);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	if (strstr("None", authtype))
14662306a36Sopenharmony_ci		return 1;
14762306a36Sopenharmony_ci	else if (strstr("CHAP", authtype))
14862306a36Sopenharmony_ci		return chap_main_loop(conn, auth, in_buf, out_buf,
14962306a36Sopenharmony_ci				&in_length, out_length);
15062306a36Sopenharmony_ci	/* SRP, SPKM1, SPKM2 and KRB5 are unsupported */
15162306a36Sopenharmony_ci	return 2;
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cistatic void iscsi_remove_failed_auth_entry(struct iscsit_conn *conn)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	kfree(conn->auth_protocol);
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ciint iscsi_target_check_login_request(
16062306a36Sopenharmony_ci	struct iscsit_conn *conn,
16162306a36Sopenharmony_ci	struct iscsi_login *login)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	int req_csg, req_nsg;
16462306a36Sopenharmony_ci	u32 payload_length;
16562306a36Sopenharmony_ci	struct iscsi_login_req *login_req;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	login_req = (struct iscsi_login_req *) login->req;
16862306a36Sopenharmony_ci	payload_length = ntoh24(login_req->dlength);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	switch (login_req->opcode & ISCSI_OPCODE_MASK) {
17162306a36Sopenharmony_ci	case ISCSI_OP_LOGIN:
17262306a36Sopenharmony_ci		break;
17362306a36Sopenharmony_ci	default:
17462306a36Sopenharmony_ci		pr_err("Received unknown opcode 0x%02x.\n",
17562306a36Sopenharmony_ci				login_req->opcode & ISCSI_OPCODE_MASK);
17662306a36Sopenharmony_ci		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
17762306a36Sopenharmony_ci				ISCSI_LOGIN_STATUS_INIT_ERR);
17862306a36Sopenharmony_ci		return -1;
17962306a36Sopenharmony_ci	}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	if ((login_req->flags & ISCSI_FLAG_LOGIN_CONTINUE) &&
18262306a36Sopenharmony_ci	    (login_req->flags & ISCSI_FLAG_LOGIN_TRANSIT)) {
18362306a36Sopenharmony_ci		pr_err("Login request has both ISCSI_FLAG_LOGIN_CONTINUE"
18462306a36Sopenharmony_ci			" and ISCSI_FLAG_LOGIN_TRANSIT set, protocol error.\n");
18562306a36Sopenharmony_ci		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
18662306a36Sopenharmony_ci				ISCSI_LOGIN_STATUS_INIT_ERR);
18762306a36Sopenharmony_ci		return -1;
18862306a36Sopenharmony_ci	}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	req_csg = ISCSI_LOGIN_CURRENT_STAGE(login_req->flags);
19162306a36Sopenharmony_ci	req_nsg = ISCSI_LOGIN_NEXT_STAGE(login_req->flags);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	if (req_csg != login->current_stage) {
19462306a36Sopenharmony_ci		pr_err("Initiator unexpectedly changed login stage"
19562306a36Sopenharmony_ci			" from %d to %d, login failed.\n", login->current_stage,
19662306a36Sopenharmony_ci			req_csg);
19762306a36Sopenharmony_ci		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
19862306a36Sopenharmony_ci				ISCSI_LOGIN_STATUS_INIT_ERR);
19962306a36Sopenharmony_ci		return -1;
20062306a36Sopenharmony_ci	}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	if ((req_nsg == 2) || (req_csg >= 2) ||
20362306a36Sopenharmony_ci	   ((login_req->flags & ISCSI_FLAG_LOGIN_TRANSIT) &&
20462306a36Sopenharmony_ci	    (req_nsg <= req_csg))) {
20562306a36Sopenharmony_ci		pr_err("Illegal login_req->flags Combination, CSG: %d,"
20662306a36Sopenharmony_ci			" NSG: %d, ISCSI_FLAG_LOGIN_TRANSIT: %d.\n", req_csg,
20762306a36Sopenharmony_ci			req_nsg, (login_req->flags & ISCSI_FLAG_LOGIN_TRANSIT));
20862306a36Sopenharmony_ci		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
20962306a36Sopenharmony_ci				ISCSI_LOGIN_STATUS_INIT_ERR);
21062306a36Sopenharmony_ci		return -1;
21162306a36Sopenharmony_ci	}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	if ((login_req->max_version != login->version_max) ||
21462306a36Sopenharmony_ci	    (login_req->min_version != login->version_min)) {
21562306a36Sopenharmony_ci		pr_err("Login request changed Version Max/Nin"
21662306a36Sopenharmony_ci			" unexpectedly to 0x%02x/0x%02x, protocol error\n",
21762306a36Sopenharmony_ci			login_req->max_version, login_req->min_version);
21862306a36Sopenharmony_ci		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
21962306a36Sopenharmony_ci				ISCSI_LOGIN_STATUS_INIT_ERR);
22062306a36Sopenharmony_ci		return -1;
22162306a36Sopenharmony_ci	}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	if (memcmp(login_req->isid, login->isid, 6) != 0) {
22462306a36Sopenharmony_ci		pr_err("Login request changed ISID unexpectedly,"
22562306a36Sopenharmony_ci				" protocol error.\n");
22662306a36Sopenharmony_ci		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
22762306a36Sopenharmony_ci				ISCSI_LOGIN_STATUS_INIT_ERR);
22862306a36Sopenharmony_ci		return -1;
22962306a36Sopenharmony_ci	}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	if (login_req->itt != login->init_task_tag) {
23262306a36Sopenharmony_ci		pr_err("Login request changed ITT unexpectedly to"
23362306a36Sopenharmony_ci			" 0x%08x, protocol error.\n", login_req->itt);
23462306a36Sopenharmony_ci		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
23562306a36Sopenharmony_ci				ISCSI_LOGIN_STATUS_INIT_ERR);
23662306a36Sopenharmony_ci		return -1;
23762306a36Sopenharmony_ci	}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	if (payload_length > MAX_KEY_VALUE_PAIRS) {
24062306a36Sopenharmony_ci		pr_err("Login request payload exceeds default"
24162306a36Sopenharmony_ci			" MaxRecvDataSegmentLength: %u, protocol error.\n",
24262306a36Sopenharmony_ci				MAX_KEY_VALUE_PAIRS);
24362306a36Sopenharmony_ci		return -1;
24462306a36Sopenharmony_ci	}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	return 0;
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ciEXPORT_SYMBOL(iscsi_target_check_login_request);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_cistatic int iscsi_target_check_first_request(
25162306a36Sopenharmony_ci	struct iscsit_conn *conn,
25262306a36Sopenharmony_ci	struct iscsi_login *login)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	struct iscsi_param *param = NULL;
25562306a36Sopenharmony_ci	struct se_node_acl *se_nacl;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	login->first_request = 0;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	list_for_each_entry(param, &conn->param_list->param_list, p_list) {
26062306a36Sopenharmony_ci		if (!strncmp(param->name, SESSIONTYPE, 11)) {
26162306a36Sopenharmony_ci			if (!IS_PSTATE_ACCEPTOR(param)) {
26262306a36Sopenharmony_ci				pr_err("SessionType key not received"
26362306a36Sopenharmony_ci					" in first login request.\n");
26462306a36Sopenharmony_ci				iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
26562306a36Sopenharmony_ci					ISCSI_LOGIN_STATUS_MISSING_FIELDS);
26662306a36Sopenharmony_ci				return -1;
26762306a36Sopenharmony_ci			}
26862306a36Sopenharmony_ci			if (!strncmp(param->value, DISCOVERY, 9))
26962306a36Sopenharmony_ci				return 0;
27062306a36Sopenharmony_ci		}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci		if (!strncmp(param->name, INITIATORNAME, 13)) {
27362306a36Sopenharmony_ci			if (!IS_PSTATE_ACCEPTOR(param)) {
27462306a36Sopenharmony_ci				if (!login->leading_connection)
27562306a36Sopenharmony_ci					continue;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci				pr_err("InitiatorName key not received"
27862306a36Sopenharmony_ci					" in first login request.\n");
27962306a36Sopenharmony_ci				iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
28062306a36Sopenharmony_ci					ISCSI_LOGIN_STATUS_MISSING_FIELDS);
28162306a36Sopenharmony_ci				return -1;
28262306a36Sopenharmony_ci			}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci			/*
28562306a36Sopenharmony_ci			 * For non-leading connections, double check that the
28662306a36Sopenharmony_ci			 * received InitiatorName matches the existing session's
28762306a36Sopenharmony_ci			 * struct iscsi_node_acl.
28862306a36Sopenharmony_ci			 */
28962306a36Sopenharmony_ci			if (!login->leading_connection) {
29062306a36Sopenharmony_ci				se_nacl = conn->sess->se_sess->se_node_acl;
29162306a36Sopenharmony_ci				if (!se_nacl) {
29262306a36Sopenharmony_ci					pr_err("Unable to locate"
29362306a36Sopenharmony_ci						" struct se_node_acl\n");
29462306a36Sopenharmony_ci					iscsit_tx_login_rsp(conn,
29562306a36Sopenharmony_ci							ISCSI_STATUS_CLS_INITIATOR_ERR,
29662306a36Sopenharmony_ci							ISCSI_LOGIN_STATUS_TGT_NOT_FOUND);
29762306a36Sopenharmony_ci					return -1;
29862306a36Sopenharmony_ci				}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci				if (strcmp(param->value,
30162306a36Sopenharmony_ci						se_nacl->initiatorname)) {
30262306a36Sopenharmony_ci					pr_err("Incorrect"
30362306a36Sopenharmony_ci						" InitiatorName: %s for this"
30462306a36Sopenharmony_ci						" iSCSI Initiator Node.\n",
30562306a36Sopenharmony_ci						param->value);
30662306a36Sopenharmony_ci					iscsit_tx_login_rsp(conn,
30762306a36Sopenharmony_ci							ISCSI_STATUS_CLS_INITIATOR_ERR,
30862306a36Sopenharmony_ci							ISCSI_LOGIN_STATUS_TGT_NOT_FOUND);
30962306a36Sopenharmony_ci					return -1;
31062306a36Sopenharmony_ci				}
31162306a36Sopenharmony_ci			}
31262306a36Sopenharmony_ci		}
31362306a36Sopenharmony_ci	}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	return 0;
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_cistatic int iscsi_target_do_tx_login_io(struct iscsit_conn *conn, struct iscsi_login *login)
31962306a36Sopenharmony_ci{
32062306a36Sopenharmony_ci	u32 padding = 0;
32162306a36Sopenharmony_ci	struct iscsi_login_rsp *login_rsp;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	login_rsp = (struct iscsi_login_rsp *) login->rsp;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	login_rsp->opcode		= ISCSI_OP_LOGIN_RSP;
32662306a36Sopenharmony_ci	hton24(login_rsp->dlength, login->rsp_length);
32762306a36Sopenharmony_ci	memcpy(login_rsp->isid, login->isid, 6);
32862306a36Sopenharmony_ci	login_rsp->tsih			= cpu_to_be16(login->tsih);
32962306a36Sopenharmony_ci	login_rsp->itt			= login->init_task_tag;
33062306a36Sopenharmony_ci	login_rsp->statsn		= cpu_to_be32(conn->stat_sn++);
33162306a36Sopenharmony_ci	login_rsp->exp_cmdsn		= cpu_to_be32(conn->sess->exp_cmd_sn);
33262306a36Sopenharmony_ci	login_rsp->max_cmdsn		= cpu_to_be32((u32) atomic_read(&conn->sess->max_cmd_sn));
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	pr_debug("Sending Login Response, Flags: 0x%02x, ITT: 0x%08x,"
33562306a36Sopenharmony_ci		" ExpCmdSN; 0x%08x, MaxCmdSN: 0x%08x, StatSN: 0x%08x, Length:"
33662306a36Sopenharmony_ci		" %u\n", login_rsp->flags, (__force u32)login_rsp->itt,
33762306a36Sopenharmony_ci		ntohl(login_rsp->exp_cmdsn), ntohl(login_rsp->max_cmdsn),
33862306a36Sopenharmony_ci		ntohl(login_rsp->statsn), login->rsp_length);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	padding = ((-login->rsp_length) & 3);
34162306a36Sopenharmony_ci	/*
34262306a36Sopenharmony_ci	 * Before sending the last login response containing the transition
34362306a36Sopenharmony_ci	 * bit for full-feature-phase, go ahead and start up TX/RX threads
34462306a36Sopenharmony_ci	 * now to avoid potential resource allocation failures after the
34562306a36Sopenharmony_ci	 * final login response has been sent.
34662306a36Sopenharmony_ci	 */
34762306a36Sopenharmony_ci	if (login->login_complete) {
34862306a36Sopenharmony_ci		int rc = iscsit_start_kthreads(conn);
34962306a36Sopenharmony_ci		if (rc) {
35062306a36Sopenharmony_ci			iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
35162306a36Sopenharmony_ci					    ISCSI_LOGIN_STATUS_NO_RESOURCES);
35262306a36Sopenharmony_ci			return -1;
35362306a36Sopenharmony_ci		}
35462306a36Sopenharmony_ci	}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	if (conn->conn_transport->iscsit_put_login_tx(conn, login,
35762306a36Sopenharmony_ci					login->rsp_length + padding) < 0)
35862306a36Sopenharmony_ci		goto err;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	login->rsp_length		= 0;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	return 0;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_cierr:
36562306a36Sopenharmony_ci	if (login->login_complete) {
36662306a36Sopenharmony_ci		if (conn->rx_thread && conn->rx_thread_active) {
36762306a36Sopenharmony_ci			send_sig(SIGINT, conn->rx_thread, 1);
36862306a36Sopenharmony_ci			complete(&conn->rx_login_comp);
36962306a36Sopenharmony_ci			kthread_stop(conn->rx_thread);
37062306a36Sopenharmony_ci		}
37162306a36Sopenharmony_ci		if (conn->tx_thread && conn->tx_thread_active) {
37262306a36Sopenharmony_ci			send_sig(SIGINT, conn->tx_thread, 1);
37362306a36Sopenharmony_ci			kthread_stop(conn->tx_thread);
37462306a36Sopenharmony_ci		}
37562306a36Sopenharmony_ci		spin_lock(&iscsit_global->ts_bitmap_lock);
37662306a36Sopenharmony_ci		bitmap_release_region(iscsit_global->ts_bitmap, conn->bitmap_id,
37762306a36Sopenharmony_ci				      get_order(1));
37862306a36Sopenharmony_ci		spin_unlock(&iscsit_global->ts_bitmap_lock);
37962306a36Sopenharmony_ci	}
38062306a36Sopenharmony_ci	return -1;
38162306a36Sopenharmony_ci}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_cistatic void iscsi_target_sk_data_ready(struct sock *sk)
38462306a36Sopenharmony_ci{
38562306a36Sopenharmony_ci	struct iscsit_conn *conn = sk->sk_user_data;
38662306a36Sopenharmony_ci	bool rc;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	trace_sk_data_ready(sk);
38962306a36Sopenharmony_ci	pr_debug("Entering iscsi_target_sk_data_ready: conn: %p\n", conn);
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	write_lock_bh(&sk->sk_callback_lock);
39262306a36Sopenharmony_ci	if (!sk->sk_user_data) {
39362306a36Sopenharmony_ci		write_unlock_bh(&sk->sk_callback_lock);
39462306a36Sopenharmony_ci		return;
39562306a36Sopenharmony_ci	}
39662306a36Sopenharmony_ci	if (!test_bit(LOGIN_FLAGS_READY, &conn->login_flags)) {
39762306a36Sopenharmony_ci		write_unlock_bh(&sk->sk_callback_lock);
39862306a36Sopenharmony_ci		pr_debug("Got LOGIN_FLAGS_READY=0, conn: %p >>>>\n", conn);
39962306a36Sopenharmony_ci		return;
40062306a36Sopenharmony_ci	}
40162306a36Sopenharmony_ci	if (test_bit(LOGIN_FLAGS_CLOSED, &conn->login_flags)) {
40262306a36Sopenharmony_ci		write_unlock_bh(&sk->sk_callback_lock);
40362306a36Sopenharmony_ci		pr_debug("Got LOGIN_FLAGS_CLOSED=1, conn: %p >>>>\n", conn);
40462306a36Sopenharmony_ci		return;
40562306a36Sopenharmony_ci	}
40662306a36Sopenharmony_ci	if (test_and_set_bit(LOGIN_FLAGS_READ_ACTIVE, &conn->login_flags)) {
40762306a36Sopenharmony_ci		write_unlock_bh(&sk->sk_callback_lock);
40862306a36Sopenharmony_ci		pr_debug("Got LOGIN_FLAGS_READ_ACTIVE=1, conn: %p >>>>\n", conn);
40962306a36Sopenharmony_ci		if (iscsi_target_sk_data_ready == conn->orig_data_ready)
41062306a36Sopenharmony_ci			return;
41162306a36Sopenharmony_ci		conn->orig_data_ready(sk);
41262306a36Sopenharmony_ci		return;
41362306a36Sopenharmony_ci	}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	rc = schedule_delayed_work(&conn->login_work, 0);
41662306a36Sopenharmony_ci	if (!rc) {
41762306a36Sopenharmony_ci		pr_debug("iscsi_target_sk_data_ready, schedule_delayed_work"
41862306a36Sopenharmony_ci			 " got false\n");
41962306a36Sopenharmony_ci	}
42062306a36Sopenharmony_ci	write_unlock_bh(&sk->sk_callback_lock);
42162306a36Sopenharmony_ci}
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_cistatic void iscsi_target_sk_state_change(struct sock *);
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_cistatic void iscsi_target_set_sock_callbacks(struct iscsit_conn *conn)
42662306a36Sopenharmony_ci{
42762306a36Sopenharmony_ci	struct sock *sk;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	if (!conn->sock)
43062306a36Sopenharmony_ci		return;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	sk = conn->sock->sk;
43362306a36Sopenharmony_ci	pr_debug("Entering iscsi_target_set_sock_callbacks: conn: %p\n", conn);
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	write_lock_bh(&sk->sk_callback_lock);
43662306a36Sopenharmony_ci	sk->sk_user_data = conn;
43762306a36Sopenharmony_ci	conn->orig_data_ready = sk->sk_data_ready;
43862306a36Sopenharmony_ci	conn->orig_state_change = sk->sk_state_change;
43962306a36Sopenharmony_ci	sk->sk_data_ready = iscsi_target_sk_data_ready;
44062306a36Sopenharmony_ci	sk->sk_state_change = iscsi_target_sk_state_change;
44162306a36Sopenharmony_ci	write_unlock_bh(&sk->sk_callback_lock);
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	sk->sk_sndtimeo = TA_LOGIN_TIMEOUT * HZ;
44462306a36Sopenharmony_ci	sk->sk_rcvtimeo = TA_LOGIN_TIMEOUT * HZ;
44562306a36Sopenharmony_ci}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_cistatic void iscsi_target_restore_sock_callbacks(struct iscsit_conn *conn)
44862306a36Sopenharmony_ci{
44962306a36Sopenharmony_ci	struct sock *sk;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	if (!conn->sock)
45262306a36Sopenharmony_ci		return;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	sk = conn->sock->sk;
45562306a36Sopenharmony_ci	pr_debug("Entering iscsi_target_restore_sock_callbacks: conn: %p\n", conn);
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	write_lock_bh(&sk->sk_callback_lock);
45862306a36Sopenharmony_ci	if (!sk->sk_user_data) {
45962306a36Sopenharmony_ci		write_unlock_bh(&sk->sk_callback_lock);
46062306a36Sopenharmony_ci		return;
46162306a36Sopenharmony_ci	}
46262306a36Sopenharmony_ci	sk->sk_user_data = NULL;
46362306a36Sopenharmony_ci	sk->sk_data_ready = conn->orig_data_ready;
46462306a36Sopenharmony_ci	sk->sk_state_change = conn->orig_state_change;
46562306a36Sopenharmony_ci	write_unlock_bh(&sk->sk_callback_lock);
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	sk->sk_sndtimeo = MAX_SCHEDULE_TIMEOUT;
46862306a36Sopenharmony_ci	sk->sk_rcvtimeo = MAX_SCHEDULE_TIMEOUT;
46962306a36Sopenharmony_ci}
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_cistatic int iscsi_target_do_login(struct iscsit_conn *, struct iscsi_login *);
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_cistatic bool __iscsi_target_sk_check_close(struct sock *sk)
47462306a36Sopenharmony_ci{
47562306a36Sopenharmony_ci	if (sk->sk_state == TCP_CLOSE_WAIT || sk->sk_state == TCP_CLOSE) {
47662306a36Sopenharmony_ci		pr_debug("__iscsi_target_sk_check_close: TCP_CLOSE_WAIT|TCP_CLOSE,"
47762306a36Sopenharmony_ci			"returning TRUE\n");
47862306a36Sopenharmony_ci		return true;
47962306a36Sopenharmony_ci	}
48062306a36Sopenharmony_ci	return false;
48162306a36Sopenharmony_ci}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_cistatic bool iscsi_target_sk_check_close(struct iscsit_conn *conn)
48462306a36Sopenharmony_ci{
48562306a36Sopenharmony_ci	bool state = false;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	if (conn->sock) {
48862306a36Sopenharmony_ci		struct sock *sk = conn->sock->sk;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci		read_lock_bh(&sk->sk_callback_lock);
49162306a36Sopenharmony_ci		state = (__iscsi_target_sk_check_close(sk) ||
49262306a36Sopenharmony_ci			 test_bit(LOGIN_FLAGS_CLOSED, &conn->login_flags));
49362306a36Sopenharmony_ci		read_unlock_bh(&sk->sk_callback_lock);
49462306a36Sopenharmony_ci	}
49562306a36Sopenharmony_ci	return state;
49662306a36Sopenharmony_ci}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_cistatic bool iscsi_target_sk_check_flag(struct iscsit_conn *conn, unsigned int flag)
49962306a36Sopenharmony_ci{
50062306a36Sopenharmony_ci	bool state = false;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	if (conn->sock) {
50362306a36Sopenharmony_ci		struct sock *sk = conn->sock->sk;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci		read_lock_bh(&sk->sk_callback_lock);
50662306a36Sopenharmony_ci		state = test_bit(flag, &conn->login_flags);
50762306a36Sopenharmony_ci		read_unlock_bh(&sk->sk_callback_lock);
50862306a36Sopenharmony_ci	}
50962306a36Sopenharmony_ci	return state;
51062306a36Sopenharmony_ci}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_cistatic bool iscsi_target_sk_check_and_clear(struct iscsit_conn *conn, unsigned int flag)
51362306a36Sopenharmony_ci{
51462306a36Sopenharmony_ci	bool state = false;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	if (conn->sock) {
51762306a36Sopenharmony_ci		struct sock *sk = conn->sock->sk;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci		write_lock_bh(&sk->sk_callback_lock);
52062306a36Sopenharmony_ci		state = (__iscsi_target_sk_check_close(sk) ||
52162306a36Sopenharmony_ci			 test_bit(LOGIN_FLAGS_CLOSED, &conn->login_flags));
52262306a36Sopenharmony_ci		if (!state)
52362306a36Sopenharmony_ci			clear_bit(flag, &conn->login_flags);
52462306a36Sopenharmony_ci		write_unlock_bh(&sk->sk_callback_lock);
52562306a36Sopenharmony_ci	}
52662306a36Sopenharmony_ci	return state;
52762306a36Sopenharmony_ci}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_cistatic void iscsi_target_login_drop(struct iscsit_conn *conn, struct iscsi_login *login)
53062306a36Sopenharmony_ci{
53162306a36Sopenharmony_ci	bool zero_tsih = login->zero_tsih;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	iscsi_remove_failed_auth_entry(conn);
53462306a36Sopenharmony_ci	iscsi_target_nego_release(conn);
53562306a36Sopenharmony_ci	iscsi_target_login_sess_out(conn, zero_tsih, true);
53662306a36Sopenharmony_ci}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_cistatic void iscsi_target_do_login_rx(struct work_struct *work)
53962306a36Sopenharmony_ci{
54062306a36Sopenharmony_ci	struct iscsit_conn *conn = container_of(work,
54162306a36Sopenharmony_ci				struct iscsit_conn, login_work.work);
54262306a36Sopenharmony_ci	struct iscsi_login *login = conn->login;
54362306a36Sopenharmony_ci	struct iscsi_np *np = login->np;
54462306a36Sopenharmony_ci	struct iscsi_portal_group *tpg = conn->tpg;
54562306a36Sopenharmony_ci	struct iscsi_tpg_np *tpg_np = conn->tpg_np;
54662306a36Sopenharmony_ci	int rc, zero_tsih = login->zero_tsih;
54762306a36Sopenharmony_ci	bool state;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	pr_debug("entering iscsi_target_do_login_rx, conn: %p, %s:%d\n",
55062306a36Sopenharmony_ci			conn, current->comm, current->pid);
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	spin_lock(&conn->login_worker_lock);
55362306a36Sopenharmony_ci	set_bit(LOGIN_FLAGS_WORKER_RUNNING, &conn->login_flags);
55462306a36Sopenharmony_ci	spin_unlock(&conn->login_worker_lock);
55562306a36Sopenharmony_ci	/*
55662306a36Sopenharmony_ci	 * If iscsi_target_do_login_rx() has been invoked by ->sk_data_ready()
55762306a36Sopenharmony_ci	 * before initial PDU processing in iscsi_target_start_negotiation()
55862306a36Sopenharmony_ci	 * has completed, go ahead and retry until it's cleared.
55962306a36Sopenharmony_ci	 *
56062306a36Sopenharmony_ci	 * Otherwise if the TCP connection drops while this is occuring,
56162306a36Sopenharmony_ci	 * iscsi_target_start_negotiation() will detect the failure, call
56262306a36Sopenharmony_ci	 * cancel_delayed_work_sync(&conn->login_work), and cleanup the
56362306a36Sopenharmony_ci	 * remaining iscsi connection resources from iscsi_np process context.
56462306a36Sopenharmony_ci	 */
56562306a36Sopenharmony_ci	if (iscsi_target_sk_check_flag(conn, LOGIN_FLAGS_INITIAL_PDU)) {
56662306a36Sopenharmony_ci		schedule_delayed_work(&conn->login_work, msecs_to_jiffies(10));
56762306a36Sopenharmony_ci		return;
56862306a36Sopenharmony_ci	}
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	spin_lock(&tpg->tpg_state_lock);
57162306a36Sopenharmony_ci	state = (tpg->tpg_state == TPG_STATE_ACTIVE);
57262306a36Sopenharmony_ci	spin_unlock(&tpg->tpg_state_lock);
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	if (!state) {
57562306a36Sopenharmony_ci		pr_debug("iscsi_target_do_login_rx: tpg_state != TPG_STATE_ACTIVE\n");
57662306a36Sopenharmony_ci		goto err;
57762306a36Sopenharmony_ci	}
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	if (iscsi_target_sk_check_close(conn)) {
58062306a36Sopenharmony_ci		pr_debug("iscsi_target_do_login_rx, TCP state CLOSE\n");
58162306a36Sopenharmony_ci		goto err;
58262306a36Sopenharmony_ci	}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	allow_signal(SIGINT);
58562306a36Sopenharmony_ci	rc = iscsit_set_login_timer_kworker(conn, current);
58662306a36Sopenharmony_ci	if (rc < 0) {
58762306a36Sopenharmony_ci		/* The login timer has already expired */
58862306a36Sopenharmony_ci		pr_debug("iscsi_target_do_login_rx, login failed\n");
58962306a36Sopenharmony_ci		goto err;
59062306a36Sopenharmony_ci	}
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	rc = conn->conn_transport->iscsit_get_login_rx(conn, login);
59362306a36Sopenharmony_ci	flush_signals(current);
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	if (rc < 0)
59662306a36Sopenharmony_ci		goto err;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	pr_debug("iscsi_target_do_login_rx after rx_login_io, %p, %s:%d\n",
59962306a36Sopenharmony_ci			conn, current->comm, current->pid);
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	/*
60262306a36Sopenharmony_ci	 * LOGIN_FLAGS_READ_ACTIVE is cleared so that sk_data_ready
60362306a36Sopenharmony_ci	 * could be triggered again after this.
60462306a36Sopenharmony_ci	 *
60562306a36Sopenharmony_ci	 * LOGIN_FLAGS_WRITE_ACTIVE is cleared after we successfully
60662306a36Sopenharmony_ci	 * process a login PDU, so that sk_state_chage can do login
60762306a36Sopenharmony_ci	 * cleanup as needed if the socket is closed. If a delayed work is
60862306a36Sopenharmony_ci	 * ongoing (LOGIN_FLAGS_WRITE_ACTIVE or LOGIN_FLAGS_READ_ACTIVE),
60962306a36Sopenharmony_ci	 * sk_state_change will leave the cleanup to the delayed work or
61062306a36Sopenharmony_ci	 * it will schedule a delayed work to do cleanup.
61162306a36Sopenharmony_ci	 */
61262306a36Sopenharmony_ci	if (conn->sock) {
61362306a36Sopenharmony_ci		struct sock *sk = conn->sock->sk;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci		write_lock_bh(&sk->sk_callback_lock);
61662306a36Sopenharmony_ci		if (!test_bit(LOGIN_FLAGS_INITIAL_PDU, &conn->login_flags)) {
61762306a36Sopenharmony_ci			clear_bit(LOGIN_FLAGS_READ_ACTIVE, &conn->login_flags);
61862306a36Sopenharmony_ci			set_bit(LOGIN_FLAGS_WRITE_ACTIVE, &conn->login_flags);
61962306a36Sopenharmony_ci		}
62062306a36Sopenharmony_ci		write_unlock_bh(&sk->sk_callback_lock);
62162306a36Sopenharmony_ci	}
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	rc = iscsi_target_do_login(conn, login);
62462306a36Sopenharmony_ci	if (rc < 0) {
62562306a36Sopenharmony_ci		goto err;
62662306a36Sopenharmony_ci	} else if (!rc) {
62762306a36Sopenharmony_ci		if (iscsi_target_sk_check_and_clear(conn,
62862306a36Sopenharmony_ci						    LOGIN_FLAGS_WRITE_ACTIVE))
62962306a36Sopenharmony_ci			goto err;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci		/*
63262306a36Sopenharmony_ci		 * Set the login timer thread pointer to NULL to prevent the
63362306a36Sopenharmony_ci		 * login process from getting stuck if the initiator
63462306a36Sopenharmony_ci		 * stops sending data.
63562306a36Sopenharmony_ci		 */
63662306a36Sopenharmony_ci		rc = iscsit_set_login_timer_kworker(conn, NULL);
63762306a36Sopenharmony_ci		if (rc < 0)
63862306a36Sopenharmony_ci			goto err;
63962306a36Sopenharmony_ci	} else if (rc == 1) {
64062306a36Sopenharmony_ci		iscsit_stop_login_timer(conn);
64162306a36Sopenharmony_ci		cancel_delayed_work(&conn->login_work);
64262306a36Sopenharmony_ci		iscsi_target_nego_release(conn);
64362306a36Sopenharmony_ci		iscsi_post_login_handler(np, conn, zero_tsih);
64462306a36Sopenharmony_ci		iscsit_deaccess_np(np, tpg, tpg_np);
64562306a36Sopenharmony_ci	}
64662306a36Sopenharmony_ci	return;
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_cierr:
64962306a36Sopenharmony_ci	iscsi_target_restore_sock_callbacks(conn);
65062306a36Sopenharmony_ci	iscsit_stop_login_timer(conn);
65162306a36Sopenharmony_ci	cancel_delayed_work(&conn->login_work);
65262306a36Sopenharmony_ci	iscsi_target_login_drop(conn, login);
65362306a36Sopenharmony_ci	iscsit_deaccess_np(np, tpg, tpg_np);
65462306a36Sopenharmony_ci}
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_cistatic void iscsi_target_sk_state_change(struct sock *sk)
65762306a36Sopenharmony_ci{
65862306a36Sopenharmony_ci	struct iscsit_conn *conn;
65962306a36Sopenharmony_ci	void (*orig_state_change)(struct sock *);
66062306a36Sopenharmony_ci	bool state;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	pr_debug("Entering iscsi_target_sk_state_change\n");
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	write_lock_bh(&sk->sk_callback_lock);
66562306a36Sopenharmony_ci	conn = sk->sk_user_data;
66662306a36Sopenharmony_ci	if (!conn) {
66762306a36Sopenharmony_ci		write_unlock_bh(&sk->sk_callback_lock);
66862306a36Sopenharmony_ci		return;
66962306a36Sopenharmony_ci	}
67062306a36Sopenharmony_ci	orig_state_change = conn->orig_state_change;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	if (!test_bit(LOGIN_FLAGS_READY, &conn->login_flags)) {
67362306a36Sopenharmony_ci		pr_debug("Got LOGIN_FLAGS_READY=0 sk_state_change conn: %p\n",
67462306a36Sopenharmony_ci			 conn);
67562306a36Sopenharmony_ci		write_unlock_bh(&sk->sk_callback_lock);
67662306a36Sopenharmony_ci		orig_state_change(sk);
67762306a36Sopenharmony_ci		return;
67862306a36Sopenharmony_ci	}
67962306a36Sopenharmony_ci	state = __iscsi_target_sk_check_close(sk);
68062306a36Sopenharmony_ci	pr_debug("__iscsi_target_sk_close_change: state: %d\n", state);
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	if (test_bit(LOGIN_FLAGS_READ_ACTIVE, &conn->login_flags) ||
68362306a36Sopenharmony_ci	    test_bit(LOGIN_FLAGS_WRITE_ACTIVE, &conn->login_flags)) {
68462306a36Sopenharmony_ci		pr_debug("Got LOGIN_FLAGS_{READ|WRITE}_ACTIVE=1"
68562306a36Sopenharmony_ci			 " sk_state_change conn: %p\n", conn);
68662306a36Sopenharmony_ci		if (state)
68762306a36Sopenharmony_ci			set_bit(LOGIN_FLAGS_CLOSED, &conn->login_flags);
68862306a36Sopenharmony_ci		write_unlock_bh(&sk->sk_callback_lock);
68962306a36Sopenharmony_ci		orig_state_change(sk);
69062306a36Sopenharmony_ci		return;
69162306a36Sopenharmony_ci	}
69262306a36Sopenharmony_ci	if (test_bit(LOGIN_FLAGS_CLOSED, &conn->login_flags)) {
69362306a36Sopenharmony_ci		pr_debug("Got LOGIN_FLAGS_CLOSED=1 sk_state_change conn: %p\n",
69462306a36Sopenharmony_ci			 conn);
69562306a36Sopenharmony_ci		write_unlock_bh(&sk->sk_callback_lock);
69662306a36Sopenharmony_ci		orig_state_change(sk);
69762306a36Sopenharmony_ci		return;
69862306a36Sopenharmony_ci	}
69962306a36Sopenharmony_ci	/*
70062306a36Sopenharmony_ci	 * If the TCP connection has dropped, go ahead and set LOGIN_FLAGS_CLOSED,
70162306a36Sopenharmony_ci	 * but only queue conn->login_work -> iscsi_target_do_login_rx()
70262306a36Sopenharmony_ci	 * processing if LOGIN_FLAGS_INITIAL_PDU has already been cleared.
70362306a36Sopenharmony_ci	 *
70462306a36Sopenharmony_ci	 * When iscsi_target_do_login_rx() runs, iscsi_target_sk_check_close()
70562306a36Sopenharmony_ci	 * will detect the dropped TCP connection from delayed workqueue context.
70662306a36Sopenharmony_ci	 *
70762306a36Sopenharmony_ci	 * If LOGIN_FLAGS_INITIAL_PDU is still set, which means the initial
70862306a36Sopenharmony_ci	 * iscsi_target_start_negotiation() is running, iscsi_target_do_login()
70962306a36Sopenharmony_ci	 * via iscsi_target_sk_check_close() or iscsi_target_start_negotiation()
71062306a36Sopenharmony_ci	 * via iscsi_target_sk_check_and_clear() is responsible for detecting the
71162306a36Sopenharmony_ci	 * dropped TCP connection in iscsi_np process context, and cleaning up
71262306a36Sopenharmony_ci	 * the remaining iscsi connection resources.
71362306a36Sopenharmony_ci	 */
71462306a36Sopenharmony_ci	if (state) {
71562306a36Sopenharmony_ci		pr_debug("iscsi_target_sk_state_change got failed state\n");
71662306a36Sopenharmony_ci		set_bit(LOGIN_FLAGS_CLOSED, &conn->login_flags);
71762306a36Sopenharmony_ci		state = test_bit(LOGIN_FLAGS_INITIAL_PDU, &conn->login_flags);
71862306a36Sopenharmony_ci		write_unlock_bh(&sk->sk_callback_lock);
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci		orig_state_change(sk);
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci		if (!state)
72362306a36Sopenharmony_ci			schedule_delayed_work(&conn->login_work, 0);
72462306a36Sopenharmony_ci		return;
72562306a36Sopenharmony_ci	}
72662306a36Sopenharmony_ci	write_unlock_bh(&sk->sk_callback_lock);
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	orig_state_change(sk);
72962306a36Sopenharmony_ci}
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci/*
73262306a36Sopenharmony_ci *	NOTE: We check for existing sessions or connections AFTER the initiator
73362306a36Sopenharmony_ci *	has been successfully authenticated in order to protect against faked
73462306a36Sopenharmony_ci *	ISID/TSIH combinations.
73562306a36Sopenharmony_ci */
73662306a36Sopenharmony_cistatic int iscsi_target_check_for_existing_instances(
73762306a36Sopenharmony_ci	struct iscsit_conn *conn,
73862306a36Sopenharmony_ci	struct iscsi_login *login)
73962306a36Sopenharmony_ci{
74062306a36Sopenharmony_ci	if (login->checked_for_existing)
74162306a36Sopenharmony_ci		return 0;
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	login->checked_for_existing = 1;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	if (!login->tsih)
74662306a36Sopenharmony_ci		return iscsi_check_for_session_reinstatement(conn);
74762306a36Sopenharmony_ci	else
74862306a36Sopenharmony_ci		return iscsi_login_post_auth_non_zero_tsih(conn, login->cid,
74962306a36Sopenharmony_ci				login->initial_exp_statsn);
75062306a36Sopenharmony_ci}
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_cistatic int iscsi_target_do_authentication(
75362306a36Sopenharmony_ci	struct iscsit_conn *conn,
75462306a36Sopenharmony_ci	struct iscsi_login *login)
75562306a36Sopenharmony_ci{
75662306a36Sopenharmony_ci	int authret;
75762306a36Sopenharmony_ci	u32 payload_length;
75862306a36Sopenharmony_ci	struct iscsi_param *param;
75962306a36Sopenharmony_ci	struct iscsi_login_req *login_req;
76062306a36Sopenharmony_ci	struct iscsi_login_rsp *login_rsp;
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	login_req = (struct iscsi_login_req *) login->req;
76362306a36Sopenharmony_ci	login_rsp = (struct iscsi_login_rsp *) login->rsp;
76462306a36Sopenharmony_ci	payload_length = ntoh24(login_req->dlength);
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	param = iscsi_find_param_from_key(AUTHMETHOD, conn->param_list);
76762306a36Sopenharmony_ci	if (!param)
76862306a36Sopenharmony_ci		return -1;
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	authret = iscsi_handle_authentication(
77162306a36Sopenharmony_ci			conn,
77262306a36Sopenharmony_ci			login->req_buf,
77362306a36Sopenharmony_ci			login->rsp_buf,
77462306a36Sopenharmony_ci			payload_length,
77562306a36Sopenharmony_ci			&login->rsp_length,
77662306a36Sopenharmony_ci			param->value);
77762306a36Sopenharmony_ci	switch (authret) {
77862306a36Sopenharmony_ci	case 0:
77962306a36Sopenharmony_ci		pr_debug("Received OK response"
78062306a36Sopenharmony_ci		" from LIO Authentication, continuing.\n");
78162306a36Sopenharmony_ci		break;
78262306a36Sopenharmony_ci	case 1:
78362306a36Sopenharmony_ci		pr_debug("iSCSI security negotiation"
78462306a36Sopenharmony_ci			" completed successfully.\n");
78562306a36Sopenharmony_ci		login->auth_complete = 1;
78662306a36Sopenharmony_ci		if ((login_req->flags & ISCSI_FLAG_LOGIN_NEXT_STAGE1) &&
78762306a36Sopenharmony_ci		    (login_req->flags & ISCSI_FLAG_LOGIN_TRANSIT)) {
78862306a36Sopenharmony_ci			login_rsp->flags |= (ISCSI_FLAG_LOGIN_NEXT_STAGE1 |
78962306a36Sopenharmony_ci					     ISCSI_FLAG_LOGIN_TRANSIT);
79062306a36Sopenharmony_ci			login->current_stage = 1;
79162306a36Sopenharmony_ci		}
79262306a36Sopenharmony_ci		return iscsi_target_check_for_existing_instances(
79362306a36Sopenharmony_ci				conn, login);
79462306a36Sopenharmony_ci	case 2:
79562306a36Sopenharmony_ci		pr_err("Security negotiation"
79662306a36Sopenharmony_ci			" failed.\n");
79762306a36Sopenharmony_ci		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
79862306a36Sopenharmony_ci				ISCSI_LOGIN_STATUS_AUTH_FAILED);
79962306a36Sopenharmony_ci		return -1;
80062306a36Sopenharmony_ci	default:
80162306a36Sopenharmony_ci		pr_err("Received unknown error %d from LIO"
80262306a36Sopenharmony_ci				" Authentication\n", authret);
80362306a36Sopenharmony_ci		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
80462306a36Sopenharmony_ci				ISCSI_LOGIN_STATUS_TARGET_ERROR);
80562306a36Sopenharmony_ci		return -1;
80662306a36Sopenharmony_ci	}
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	return 0;
80962306a36Sopenharmony_ci}
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_cibool iscsi_conn_auth_required(struct iscsit_conn *conn)
81262306a36Sopenharmony_ci{
81362306a36Sopenharmony_ci	struct iscsi_node_acl *nacl;
81462306a36Sopenharmony_ci	struct se_node_acl *se_nacl;
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	if (conn->sess->sess_ops->SessionType) {
81762306a36Sopenharmony_ci		/*
81862306a36Sopenharmony_ci		 * For SessionType=Discovery
81962306a36Sopenharmony_ci		 */
82062306a36Sopenharmony_ci		return conn->tpg->tpg_attrib.authentication;
82162306a36Sopenharmony_ci	}
82262306a36Sopenharmony_ci	/*
82362306a36Sopenharmony_ci	 * For SessionType=Normal
82462306a36Sopenharmony_ci	 */
82562306a36Sopenharmony_ci	se_nacl = conn->sess->se_sess->se_node_acl;
82662306a36Sopenharmony_ci	if (!se_nacl) {
82762306a36Sopenharmony_ci		pr_debug("Unknown ACL is trying to connect\n");
82862306a36Sopenharmony_ci		return true;
82962306a36Sopenharmony_ci	}
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	if (se_nacl->dynamic_node_acl) {
83262306a36Sopenharmony_ci		pr_debug("Dynamic ACL %s is trying to connect\n",
83362306a36Sopenharmony_ci			 se_nacl->initiatorname);
83462306a36Sopenharmony_ci		return conn->tpg->tpg_attrib.authentication;
83562306a36Sopenharmony_ci	}
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	pr_debug("Known ACL %s is trying to connect\n",
83862306a36Sopenharmony_ci		 se_nacl->initiatorname);
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	nacl = to_iscsi_nacl(se_nacl);
84162306a36Sopenharmony_ci	if (nacl->node_attrib.authentication == NA_AUTHENTICATION_INHERITED)
84262306a36Sopenharmony_ci		return conn->tpg->tpg_attrib.authentication;
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	return nacl->node_attrib.authentication;
84562306a36Sopenharmony_ci}
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_cistatic int iscsi_target_handle_csg_zero(
84862306a36Sopenharmony_ci	struct iscsit_conn *conn,
84962306a36Sopenharmony_ci	struct iscsi_login *login)
85062306a36Sopenharmony_ci{
85162306a36Sopenharmony_ci	int ret;
85262306a36Sopenharmony_ci	u32 payload_length;
85362306a36Sopenharmony_ci	struct iscsi_param *param;
85462306a36Sopenharmony_ci	struct iscsi_login_req *login_req;
85562306a36Sopenharmony_ci	struct iscsi_login_rsp *login_rsp;
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	login_req = (struct iscsi_login_req *) login->req;
85862306a36Sopenharmony_ci	login_rsp = (struct iscsi_login_rsp *) login->rsp;
85962306a36Sopenharmony_ci	payload_length = ntoh24(login_req->dlength);
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	param = iscsi_find_param_from_key(AUTHMETHOD, conn->param_list);
86262306a36Sopenharmony_ci	if (!param)
86362306a36Sopenharmony_ci		return -1;
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	ret = iscsi_decode_text_input(
86662306a36Sopenharmony_ci			PHASE_SECURITY|PHASE_DECLARATIVE,
86762306a36Sopenharmony_ci			SENDER_INITIATOR|SENDER_RECEIVER,
86862306a36Sopenharmony_ci			login->req_buf,
86962306a36Sopenharmony_ci			payload_length,
87062306a36Sopenharmony_ci			conn);
87162306a36Sopenharmony_ci	if (ret < 0)
87262306a36Sopenharmony_ci		return -1;
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	if (ret > 0) {
87562306a36Sopenharmony_ci		if (login->auth_complete) {
87662306a36Sopenharmony_ci			pr_err("Initiator has already been"
87762306a36Sopenharmony_ci				" successfully authenticated, but is still"
87862306a36Sopenharmony_ci				" sending %s keys.\n", param->value);
87962306a36Sopenharmony_ci			iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
88062306a36Sopenharmony_ci					ISCSI_LOGIN_STATUS_INIT_ERR);
88162306a36Sopenharmony_ci			return -1;
88262306a36Sopenharmony_ci		}
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci		goto do_auth;
88562306a36Sopenharmony_ci	} else if (!payload_length) {
88662306a36Sopenharmony_ci		pr_err("Initiator sent zero length security payload,"
88762306a36Sopenharmony_ci		       " login failed\n");
88862306a36Sopenharmony_ci		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
88962306a36Sopenharmony_ci				    ISCSI_LOGIN_STATUS_AUTH_FAILED);
89062306a36Sopenharmony_ci		return -1;
89162306a36Sopenharmony_ci	}
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	if (login->first_request)
89462306a36Sopenharmony_ci		if (iscsi_target_check_first_request(conn, login) < 0)
89562306a36Sopenharmony_ci			return -1;
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	ret = iscsi_encode_text_output(
89862306a36Sopenharmony_ci			PHASE_SECURITY|PHASE_DECLARATIVE,
89962306a36Sopenharmony_ci			SENDER_TARGET,
90062306a36Sopenharmony_ci			login->rsp_buf,
90162306a36Sopenharmony_ci			&login->rsp_length,
90262306a36Sopenharmony_ci			conn->param_list,
90362306a36Sopenharmony_ci			conn->tpg->tpg_attrib.login_keys_workaround);
90462306a36Sopenharmony_ci	if (ret < 0)
90562306a36Sopenharmony_ci		return -1;
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	if (!iscsi_check_negotiated_keys(conn->param_list)) {
90862306a36Sopenharmony_ci		bool auth_required = iscsi_conn_auth_required(conn);
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci		if (auth_required) {
91162306a36Sopenharmony_ci			if (!strncmp(param->value, NONE, 4)) {
91262306a36Sopenharmony_ci				pr_err("Initiator sent AuthMethod=None but"
91362306a36Sopenharmony_ci				       " Target is enforcing iSCSI Authentication,"
91462306a36Sopenharmony_ci				       " login failed.\n");
91562306a36Sopenharmony_ci				iscsit_tx_login_rsp(conn,
91662306a36Sopenharmony_ci						ISCSI_STATUS_CLS_INITIATOR_ERR,
91762306a36Sopenharmony_ci						ISCSI_LOGIN_STATUS_AUTH_FAILED);
91862306a36Sopenharmony_ci				return -1;
91962306a36Sopenharmony_ci			}
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci			if (!login->auth_complete)
92262306a36Sopenharmony_ci				return 0;
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci			if (strncmp(param->value, NONE, 4) &&
92562306a36Sopenharmony_ci			    !login->auth_complete)
92662306a36Sopenharmony_ci				return 0;
92762306a36Sopenharmony_ci		}
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci		if ((login_req->flags & ISCSI_FLAG_LOGIN_NEXT_STAGE1) &&
93062306a36Sopenharmony_ci		    (login_req->flags & ISCSI_FLAG_LOGIN_TRANSIT)) {
93162306a36Sopenharmony_ci			login_rsp->flags |= ISCSI_FLAG_LOGIN_NEXT_STAGE1 |
93262306a36Sopenharmony_ci					    ISCSI_FLAG_LOGIN_TRANSIT;
93362306a36Sopenharmony_ci			login->current_stage = 1;
93462306a36Sopenharmony_ci		}
93562306a36Sopenharmony_ci	}
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci	return 0;
93862306a36Sopenharmony_cido_auth:
93962306a36Sopenharmony_ci	return iscsi_target_do_authentication(conn, login);
94062306a36Sopenharmony_ci}
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_cistatic bool iscsi_conn_authenticated(struct iscsit_conn *conn,
94362306a36Sopenharmony_ci				     struct iscsi_login *login)
94462306a36Sopenharmony_ci{
94562306a36Sopenharmony_ci	if (!iscsi_conn_auth_required(conn))
94662306a36Sopenharmony_ci		return true;
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	if (login->auth_complete)
94962306a36Sopenharmony_ci		return true;
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	return false;
95262306a36Sopenharmony_ci}
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_cistatic int iscsi_target_handle_csg_one(struct iscsit_conn *conn, struct iscsi_login *login)
95562306a36Sopenharmony_ci{
95662306a36Sopenharmony_ci	int ret;
95762306a36Sopenharmony_ci	u32 payload_length;
95862306a36Sopenharmony_ci	struct iscsi_login_req *login_req;
95962306a36Sopenharmony_ci	struct iscsi_login_rsp *login_rsp;
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci	login_req = (struct iscsi_login_req *) login->req;
96262306a36Sopenharmony_ci	login_rsp = (struct iscsi_login_rsp *) login->rsp;
96362306a36Sopenharmony_ci	payload_length = ntoh24(login_req->dlength);
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	ret = iscsi_decode_text_input(
96662306a36Sopenharmony_ci			PHASE_OPERATIONAL|PHASE_DECLARATIVE,
96762306a36Sopenharmony_ci			SENDER_INITIATOR|SENDER_RECEIVER,
96862306a36Sopenharmony_ci			login->req_buf,
96962306a36Sopenharmony_ci			payload_length,
97062306a36Sopenharmony_ci			conn);
97162306a36Sopenharmony_ci	if (ret < 0) {
97262306a36Sopenharmony_ci		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
97362306a36Sopenharmony_ci				ISCSI_LOGIN_STATUS_INIT_ERR);
97462306a36Sopenharmony_ci		return -1;
97562306a36Sopenharmony_ci	}
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	if (login->first_request)
97862306a36Sopenharmony_ci		if (iscsi_target_check_first_request(conn, login) < 0)
97962306a36Sopenharmony_ci			return -1;
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci	if (iscsi_target_check_for_existing_instances(conn, login) < 0)
98262306a36Sopenharmony_ci		return -1;
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	ret = iscsi_encode_text_output(
98562306a36Sopenharmony_ci			PHASE_OPERATIONAL|PHASE_DECLARATIVE,
98662306a36Sopenharmony_ci			SENDER_TARGET,
98762306a36Sopenharmony_ci			login->rsp_buf,
98862306a36Sopenharmony_ci			&login->rsp_length,
98962306a36Sopenharmony_ci			conn->param_list,
99062306a36Sopenharmony_ci			conn->tpg->tpg_attrib.login_keys_workaround);
99162306a36Sopenharmony_ci	if (ret < 0) {
99262306a36Sopenharmony_ci		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
99362306a36Sopenharmony_ci				ISCSI_LOGIN_STATUS_INIT_ERR);
99462306a36Sopenharmony_ci		return -1;
99562306a36Sopenharmony_ci	}
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci	if (!iscsi_conn_authenticated(conn, login)) {
99862306a36Sopenharmony_ci		pr_err("Initiator is requesting CSG: 1, has not been"
99962306a36Sopenharmony_ci		       " successfully authenticated, and the Target is"
100062306a36Sopenharmony_ci		       " enforcing iSCSI Authentication, login failed.\n");
100162306a36Sopenharmony_ci		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
100262306a36Sopenharmony_ci				ISCSI_LOGIN_STATUS_AUTH_FAILED);
100362306a36Sopenharmony_ci		return -1;
100462306a36Sopenharmony_ci	}
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	if (!iscsi_check_negotiated_keys(conn->param_list))
100762306a36Sopenharmony_ci		if ((login_req->flags & ISCSI_FLAG_LOGIN_NEXT_STAGE3) &&
100862306a36Sopenharmony_ci		    (login_req->flags & ISCSI_FLAG_LOGIN_TRANSIT))
100962306a36Sopenharmony_ci			login_rsp->flags |= ISCSI_FLAG_LOGIN_NEXT_STAGE3 |
101062306a36Sopenharmony_ci					    ISCSI_FLAG_LOGIN_TRANSIT;
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	return 0;
101362306a36Sopenharmony_ci}
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci/*
101662306a36Sopenharmony_ci * RETURN VALUE:
101762306a36Sopenharmony_ci *
101862306a36Sopenharmony_ci *  1 = Login successful
101962306a36Sopenharmony_ci * -1 = Login failed
102062306a36Sopenharmony_ci *  0 = More PDU exchanges required
102162306a36Sopenharmony_ci */
102262306a36Sopenharmony_cistatic int iscsi_target_do_login(struct iscsit_conn *conn, struct iscsi_login *login)
102362306a36Sopenharmony_ci{
102462306a36Sopenharmony_ci	int pdu_count = 0;
102562306a36Sopenharmony_ci	struct iscsi_login_req *login_req;
102662306a36Sopenharmony_ci	struct iscsi_login_rsp *login_rsp;
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	login_req = (struct iscsi_login_req *) login->req;
102962306a36Sopenharmony_ci	login_rsp = (struct iscsi_login_rsp *) login->rsp;
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci	while (1) {
103262306a36Sopenharmony_ci		if (++pdu_count > MAX_LOGIN_PDUS) {
103362306a36Sopenharmony_ci			pr_err("MAX_LOGIN_PDUS count reached.\n");
103462306a36Sopenharmony_ci			iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
103562306a36Sopenharmony_ci					ISCSI_LOGIN_STATUS_TARGET_ERROR);
103662306a36Sopenharmony_ci			return -1;
103762306a36Sopenharmony_ci		}
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci		switch (ISCSI_LOGIN_CURRENT_STAGE(login_req->flags)) {
104062306a36Sopenharmony_ci		case 0:
104162306a36Sopenharmony_ci			login_rsp->flags &= ~ISCSI_FLAG_LOGIN_CURRENT_STAGE_MASK;
104262306a36Sopenharmony_ci			if (iscsi_target_handle_csg_zero(conn, login) < 0)
104362306a36Sopenharmony_ci				return -1;
104462306a36Sopenharmony_ci			break;
104562306a36Sopenharmony_ci		case 1:
104662306a36Sopenharmony_ci			login_rsp->flags |= ISCSI_FLAG_LOGIN_CURRENT_STAGE1;
104762306a36Sopenharmony_ci			if (iscsi_target_handle_csg_one(conn, login) < 0)
104862306a36Sopenharmony_ci				return -1;
104962306a36Sopenharmony_ci			if (login_rsp->flags & ISCSI_FLAG_LOGIN_TRANSIT) {
105062306a36Sopenharmony_ci				/*
105162306a36Sopenharmony_ci				 * Check to make sure the TCP connection has not
105262306a36Sopenharmony_ci				 * dropped asynchronously while session reinstatement
105362306a36Sopenharmony_ci				 * was occuring in this kthread context, before
105462306a36Sopenharmony_ci				 * transitioning to full feature phase operation.
105562306a36Sopenharmony_ci				 */
105662306a36Sopenharmony_ci				if (iscsi_target_sk_check_close(conn))
105762306a36Sopenharmony_ci					return -1;
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci				login->tsih = conn->sess->tsih;
106062306a36Sopenharmony_ci				login->login_complete = 1;
106162306a36Sopenharmony_ci				iscsi_target_restore_sock_callbacks(conn);
106262306a36Sopenharmony_ci				if (iscsi_target_do_tx_login_io(conn,
106362306a36Sopenharmony_ci						login) < 0)
106462306a36Sopenharmony_ci					return -1;
106562306a36Sopenharmony_ci				return 1;
106662306a36Sopenharmony_ci			}
106762306a36Sopenharmony_ci			break;
106862306a36Sopenharmony_ci		default:
106962306a36Sopenharmony_ci			pr_err("Illegal CSG: %d received from"
107062306a36Sopenharmony_ci				" Initiator, protocol error.\n",
107162306a36Sopenharmony_ci				ISCSI_LOGIN_CURRENT_STAGE(login_req->flags));
107262306a36Sopenharmony_ci			break;
107362306a36Sopenharmony_ci		}
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci		if (iscsi_target_do_tx_login_io(conn, login) < 0)
107662306a36Sopenharmony_ci			return -1;
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci		if (login_rsp->flags & ISCSI_FLAG_LOGIN_TRANSIT) {
107962306a36Sopenharmony_ci			login_rsp->flags &= ~ISCSI_FLAG_LOGIN_TRANSIT;
108062306a36Sopenharmony_ci			login_rsp->flags &= ~ISCSI_FLAG_LOGIN_NEXT_STAGE_MASK;
108162306a36Sopenharmony_ci		}
108262306a36Sopenharmony_ci		break;
108362306a36Sopenharmony_ci	}
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	return 0;
108662306a36Sopenharmony_ci}
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_cistatic void iscsi_initiatorname_tolower(
108962306a36Sopenharmony_ci	char *param_buf)
109062306a36Sopenharmony_ci{
109162306a36Sopenharmony_ci	char *c;
109262306a36Sopenharmony_ci	u32 iqn_size = strlen(param_buf), i;
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci	for (i = 0; i < iqn_size; i++) {
109562306a36Sopenharmony_ci		c = &param_buf[i];
109662306a36Sopenharmony_ci		if (!isupper(*c))
109762306a36Sopenharmony_ci			continue;
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci		*c = tolower(*c);
110062306a36Sopenharmony_ci	}
110162306a36Sopenharmony_ci}
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci/*
110462306a36Sopenharmony_ci * Processes the first Login Request..
110562306a36Sopenharmony_ci */
110662306a36Sopenharmony_ciint iscsi_target_locate_portal(
110762306a36Sopenharmony_ci	struct iscsi_np *np,
110862306a36Sopenharmony_ci	struct iscsit_conn *conn,
110962306a36Sopenharmony_ci	struct iscsi_login *login)
111062306a36Sopenharmony_ci{
111162306a36Sopenharmony_ci	char *i_buf = NULL, *s_buf = NULL, *t_buf = NULL;
111262306a36Sopenharmony_ci	char *tmpbuf, *start = NULL, *end = NULL, *key, *value;
111362306a36Sopenharmony_ci	struct iscsit_session *sess = conn->sess;
111462306a36Sopenharmony_ci	struct iscsi_tiqn *tiqn;
111562306a36Sopenharmony_ci	struct iscsi_tpg_np *tpg_np = NULL;
111662306a36Sopenharmony_ci	struct iscsi_login_req *login_req;
111762306a36Sopenharmony_ci	struct se_node_acl *se_nacl;
111862306a36Sopenharmony_ci	u32 payload_length, queue_depth = 0;
111962306a36Sopenharmony_ci	int sessiontype = 0, ret = 0, tag_num, tag_size;
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	INIT_DELAYED_WORK(&conn->login_work, iscsi_target_do_login_rx);
112262306a36Sopenharmony_ci	iscsi_target_set_sock_callbacks(conn);
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci	login->np = np;
112562306a36Sopenharmony_ci	conn->tpg = NULL;
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci	login_req = (struct iscsi_login_req *) login->req;
112862306a36Sopenharmony_ci	payload_length = ntoh24(login_req->dlength);
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci	tmpbuf = kmemdup_nul(login->req_buf, payload_length, GFP_KERNEL);
113162306a36Sopenharmony_ci	if (!tmpbuf) {
113262306a36Sopenharmony_ci		pr_err("Unable to allocate memory for tmpbuf.\n");
113362306a36Sopenharmony_ci		return -1;
113462306a36Sopenharmony_ci	}
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	start = tmpbuf;
113762306a36Sopenharmony_ci	end = (start + payload_length);
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	/*
114062306a36Sopenharmony_ci	 * Locate the initial keys expected from the Initiator node in
114162306a36Sopenharmony_ci	 * the first login request in order to progress with the login phase.
114262306a36Sopenharmony_ci	 */
114362306a36Sopenharmony_ci	while (start < end) {
114462306a36Sopenharmony_ci		if (iscsi_extract_key_value(start, &key, &value) < 0) {
114562306a36Sopenharmony_ci			ret = -1;
114662306a36Sopenharmony_ci			goto out;
114762306a36Sopenharmony_ci		}
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci		if (!strncmp(key, "InitiatorName", 13))
115062306a36Sopenharmony_ci			i_buf = value;
115162306a36Sopenharmony_ci		else if (!strncmp(key, "SessionType", 11))
115262306a36Sopenharmony_ci			s_buf = value;
115362306a36Sopenharmony_ci		else if (!strncmp(key, "TargetName", 10))
115462306a36Sopenharmony_ci			t_buf = value;
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci		start += strlen(key) + strlen(value) + 2;
115762306a36Sopenharmony_ci	}
115862306a36Sopenharmony_ci	/*
115962306a36Sopenharmony_ci	 * See 5.3.  Login Phase.
116062306a36Sopenharmony_ci	 */
116162306a36Sopenharmony_ci	if (!i_buf) {
116262306a36Sopenharmony_ci		pr_err("InitiatorName key not received"
116362306a36Sopenharmony_ci			" in first login request.\n");
116462306a36Sopenharmony_ci		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
116562306a36Sopenharmony_ci			ISCSI_LOGIN_STATUS_MISSING_FIELDS);
116662306a36Sopenharmony_ci		ret = -1;
116762306a36Sopenharmony_ci		goto out;
116862306a36Sopenharmony_ci	}
116962306a36Sopenharmony_ci	/*
117062306a36Sopenharmony_ci	 * Convert the incoming InitiatorName to lowercase following
117162306a36Sopenharmony_ci	 * RFC-3720 3.2.6.1. section c) that says that iSCSI IQNs
117262306a36Sopenharmony_ci	 * are NOT case sensitive.
117362306a36Sopenharmony_ci	 */
117462306a36Sopenharmony_ci	iscsi_initiatorname_tolower(i_buf);
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci	if (!s_buf) {
117762306a36Sopenharmony_ci		if (!login->leading_connection)
117862306a36Sopenharmony_ci			goto get_target;
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci		pr_err("SessionType key not received"
118162306a36Sopenharmony_ci			" in first login request.\n");
118262306a36Sopenharmony_ci		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
118362306a36Sopenharmony_ci			ISCSI_LOGIN_STATUS_MISSING_FIELDS);
118462306a36Sopenharmony_ci		ret = -1;
118562306a36Sopenharmony_ci		goto out;
118662306a36Sopenharmony_ci	}
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	/*
118962306a36Sopenharmony_ci	 * Use default portal group for discovery sessions.
119062306a36Sopenharmony_ci	 */
119162306a36Sopenharmony_ci	sessiontype = strncmp(s_buf, DISCOVERY, 9);
119262306a36Sopenharmony_ci	if (!sessiontype) {
119362306a36Sopenharmony_ci		if (!login->leading_connection)
119462306a36Sopenharmony_ci			goto get_target;
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci		sess->sess_ops->SessionType = 1;
119762306a36Sopenharmony_ci		/*
119862306a36Sopenharmony_ci		 * Setup crc32c modules from libcrypto
119962306a36Sopenharmony_ci		 */
120062306a36Sopenharmony_ci		if (iscsi_login_setup_crypto(conn) < 0) {
120162306a36Sopenharmony_ci			pr_err("iscsi_login_setup_crypto() failed\n");
120262306a36Sopenharmony_ci			ret = -1;
120362306a36Sopenharmony_ci			goto out;
120462306a36Sopenharmony_ci		}
120562306a36Sopenharmony_ci		/*
120662306a36Sopenharmony_ci		 * Serialize access across the discovery struct iscsi_portal_group to
120762306a36Sopenharmony_ci		 * process login attempt.
120862306a36Sopenharmony_ci		 */
120962306a36Sopenharmony_ci		conn->tpg = iscsit_global->discovery_tpg;
121062306a36Sopenharmony_ci		if (iscsit_access_np(np, conn->tpg) < 0) {
121162306a36Sopenharmony_ci			iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
121262306a36Sopenharmony_ci				ISCSI_LOGIN_STATUS_SVC_UNAVAILABLE);
121362306a36Sopenharmony_ci			conn->tpg = NULL;
121462306a36Sopenharmony_ci			ret = -1;
121562306a36Sopenharmony_ci			goto out;
121662306a36Sopenharmony_ci		}
121762306a36Sopenharmony_ci		ret = 0;
121862306a36Sopenharmony_ci		goto alloc_tags;
121962306a36Sopenharmony_ci	}
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ciget_target:
122262306a36Sopenharmony_ci	if (!t_buf) {
122362306a36Sopenharmony_ci		pr_err("TargetName key not received"
122462306a36Sopenharmony_ci			" in first login request while"
122562306a36Sopenharmony_ci			" SessionType=Normal.\n");
122662306a36Sopenharmony_ci		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
122762306a36Sopenharmony_ci			ISCSI_LOGIN_STATUS_MISSING_FIELDS);
122862306a36Sopenharmony_ci		ret = -1;
122962306a36Sopenharmony_ci		goto out;
123062306a36Sopenharmony_ci	}
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci	/*
123362306a36Sopenharmony_ci	 * Locate Target IQN from Storage Node.
123462306a36Sopenharmony_ci	 */
123562306a36Sopenharmony_ci	tiqn = iscsit_get_tiqn_for_login(t_buf);
123662306a36Sopenharmony_ci	if (!tiqn) {
123762306a36Sopenharmony_ci		pr_err("Unable to locate Target IQN: %s in"
123862306a36Sopenharmony_ci			" Storage Node\n", t_buf);
123962306a36Sopenharmony_ci		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
124062306a36Sopenharmony_ci				ISCSI_LOGIN_STATUS_SVC_UNAVAILABLE);
124162306a36Sopenharmony_ci		ret = -1;
124262306a36Sopenharmony_ci		goto out;
124362306a36Sopenharmony_ci	}
124462306a36Sopenharmony_ci	pr_debug("Located Storage Object: %s\n", tiqn->tiqn);
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci	/*
124762306a36Sopenharmony_ci	 * Locate Target Portal Group from Storage Node.
124862306a36Sopenharmony_ci	 */
124962306a36Sopenharmony_ci	conn->tpg = iscsit_get_tpg_from_np(tiqn, np, &tpg_np);
125062306a36Sopenharmony_ci	if (!conn->tpg) {
125162306a36Sopenharmony_ci		pr_err("Unable to locate Target Portal Group"
125262306a36Sopenharmony_ci				" on %s\n", tiqn->tiqn);
125362306a36Sopenharmony_ci		iscsit_put_tiqn_for_login(tiqn);
125462306a36Sopenharmony_ci		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
125562306a36Sopenharmony_ci				ISCSI_LOGIN_STATUS_SVC_UNAVAILABLE);
125662306a36Sopenharmony_ci		ret = -1;
125762306a36Sopenharmony_ci		goto out;
125862306a36Sopenharmony_ci	}
125962306a36Sopenharmony_ci	conn->tpg_np = tpg_np;
126062306a36Sopenharmony_ci	pr_debug("Located Portal Group Object: %hu\n", conn->tpg->tpgt);
126162306a36Sopenharmony_ci	/*
126262306a36Sopenharmony_ci	 * Setup crc32c modules from libcrypto
126362306a36Sopenharmony_ci	 */
126462306a36Sopenharmony_ci	if (iscsi_login_setup_crypto(conn) < 0) {
126562306a36Sopenharmony_ci		pr_err("iscsi_login_setup_crypto() failed\n");
126662306a36Sopenharmony_ci		kref_put(&tpg_np->tpg_np_kref, iscsit_login_kref_put);
126762306a36Sopenharmony_ci		iscsit_put_tiqn_for_login(tiqn);
126862306a36Sopenharmony_ci		conn->tpg = NULL;
126962306a36Sopenharmony_ci		ret = -1;
127062306a36Sopenharmony_ci		goto out;
127162306a36Sopenharmony_ci	}
127262306a36Sopenharmony_ci	/*
127362306a36Sopenharmony_ci	 * Serialize access across the struct iscsi_portal_group to
127462306a36Sopenharmony_ci	 * process login attempt.
127562306a36Sopenharmony_ci	 */
127662306a36Sopenharmony_ci	if (iscsit_access_np(np, conn->tpg) < 0) {
127762306a36Sopenharmony_ci		kref_put(&tpg_np->tpg_np_kref, iscsit_login_kref_put);
127862306a36Sopenharmony_ci		iscsit_put_tiqn_for_login(tiqn);
127962306a36Sopenharmony_ci		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
128062306a36Sopenharmony_ci				ISCSI_LOGIN_STATUS_SVC_UNAVAILABLE);
128162306a36Sopenharmony_ci		conn->tpg = NULL;
128262306a36Sopenharmony_ci		ret = -1;
128362306a36Sopenharmony_ci		goto out;
128462306a36Sopenharmony_ci	}
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci	/*
128762306a36Sopenharmony_ci	 * conn->sess->node_acl will be set when the referenced
128862306a36Sopenharmony_ci	 * struct iscsit_session is located from received ISID+TSIH in
128962306a36Sopenharmony_ci	 * iscsi_login_non_zero_tsih_s2().
129062306a36Sopenharmony_ci	 */
129162306a36Sopenharmony_ci	if (!login->leading_connection) {
129262306a36Sopenharmony_ci		ret = 0;
129362306a36Sopenharmony_ci		goto out;
129462306a36Sopenharmony_ci	}
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci	/*
129762306a36Sopenharmony_ci	 * This value is required in iscsi_login_zero_tsih_s2()
129862306a36Sopenharmony_ci	 */
129962306a36Sopenharmony_ci	sess->sess_ops->SessionType = 0;
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci	/*
130262306a36Sopenharmony_ci	 * Locate incoming Initiator IQN reference from Storage Node.
130362306a36Sopenharmony_ci	 */
130462306a36Sopenharmony_ci	sess->se_sess->se_node_acl = core_tpg_check_initiator_node_acl(
130562306a36Sopenharmony_ci			&conn->tpg->tpg_se_tpg, i_buf);
130662306a36Sopenharmony_ci	if (!sess->se_sess->se_node_acl) {
130762306a36Sopenharmony_ci		pr_err("iSCSI Initiator Node: %s is not authorized to"
130862306a36Sopenharmony_ci			" access iSCSI target portal group: %hu.\n",
130962306a36Sopenharmony_ci				i_buf, conn->tpg->tpgt);
131062306a36Sopenharmony_ci		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR,
131162306a36Sopenharmony_ci				ISCSI_LOGIN_STATUS_TGT_FORBIDDEN);
131262306a36Sopenharmony_ci		ret = -1;
131362306a36Sopenharmony_ci		goto out;
131462306a36Sopenharmony_ci	}
131562306a36Sopenharmony_ci	se_nacl = sess->se_sess->se_node_acl;
131662306a36Sopenharmony_ci	queue_depth = se_nacl->queue_depth;
131762306a36Sopenharmony_ci	/*
131862306a36Sopenharmony_ci	 * Setup pre-allocated tags based upon allowed per NodeACL CmdSN
131962306a36Sopenharmony_ci	 * depth for non immediate commands, plus extra tags for immediate
132062306a36Sopenharmony_ci	 * commands.
132162306a36Sopenharmony_ci	 *
132262306a36Sopenharmony_ci	 * Also enforce a ISCSIT_MIN_TAGS to prevent unnecessary contention
132362306a36Sopenharmony_ci	 * in per-cpu-ida tag allocation logic + small queue_depth.
132462306a36Sopenharmony_ci	 */
132562306a36Sopenharmony_cialloc_tags:
132662306a36Sopenharmony_ci	tag_num = max_t(u32, ISCSIT_MIN_TAGS, queue_depth);
132762306a36Sopenharmony_ci	tag_num = (tag_num * 2) + ISCSIT_EXTRA_TAGS;
132862306a36Sopenharmony_ci	tag_size = sizeof(struct iscsit_cmd) + conn->conn_transport->priv_size;
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci	ret = transport_alloc_session_tags(sess->se_sess, tag_num, tag_size);
133162306a36Sopenharmony_ci	if (ret < 0) {
133262306a36Sopenharmony_ci		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
133362306a36Sopenharmony_ci				    ISCSI_LOGIN_STATUS_NO_RESOURCES);
133462306a36Sopenharmony_ci		ret = -1;
133562306a36Sopenharmony_ci	}
133662306a36Sopenharmony_ciout:
133762306a36Sopenharmony_ci	kfree(tmpbuf);
133862306a36Sopenharmony_ci	return ret;
133962306a36Sopenharmony_ci}
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_ciint iscsi_target_start_negotiation(
134262306a36Sopenharmony_ci	struct iscsi_login *login,
134362306a36Sopenharmony_ci	struct iscsit_conn *conn)
134462306a36Sopenharmony_ci{
134562306a36Sopenharmony_ci	int ret;
134662306a36Sopenharmony_ci
134762306a36Sopenharmony_ci	if (conn->sock) {
134862306a36Sopenharmony_ci		struct sock *sk = conn->sock->sk;
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci		write_lock_bh(&sk->sk_callback_lock);
135162306a36Sopenharmony_ci		set_bit(LOGIN_FLAGS_READY, &conn->login_flags);
135262306a36Sopenharmony_ci		set_bit(LOGIN_FLAGS_INITIAL_PDU, &conn->login_flags);
135362306a36Sopenharmony_ci		write_unlock_bh(&sk->sk_callback_lock);
135462306a36Sopenharmony_ci	}
135562306a36Sopenharmony_ci	/*
135662306a36Sopenharmony_ci	 * If iscsi_target_do_login returns zero to signal more PDU
135762306a36Sopenharmony_ci	 * exchanges are required to complete the login, go ahead and
135862306a36Sopenharmony_ci	 * clear LOGIN_FLAGS_INITIAL_PDU but only if the TCP connection
135962306a36Sopenharmony_ci	 * is still active.
136062306a36Sopenharmony_ci	 *
136162306a36Sopenharmony_ci	 * Otherwise if TCP connection dropped asynchronously, go ahead
136262306a36Sopenharmony_ci	 * and perform connection cleanup now.
136362306a36Sopenharmony_ci	 */
136462306a36Sopenharmony_ci	ret = iscsi_target_do_login(conn, login);
136562306a36Sopenharmony_ci	if (!ret) {
136662306a36Sopenharmony_ci		spin_lock(&conn->login_worker_lock);
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ci		if (iscsi_target_sk_check_and_clear(conn, LOGIN_FLAGS_INITIAL_PDU))
136962306a36Sopenharmony_ci			ret = -1;
137062306a36Sopenharmony_ci		else if (!test_bit(LOGIN_FLAGS_WORKER_RUNNING, &conn->login_flags)) {
137162306a36Sopenharmony_ci			if (iscsit_set_login_timer_kworker(conn, NULL) < 0) {
137262306a36Sopenharmony_ci				/*
137362306a36Sopenharmony_ci				 * The timeout has expired already.
137462306a36Sopenharmony_ci				 * Schedule login_work to perform the cleanup.
137562306a36Sopenharmony_ci				 */
137662306a36Sopenharmony_ci				schedule_delayed_work(&conn->login_work, 0);
137762306a36Sopenharmony_ci			}
137862306a36Sopenharmony_ci		}
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci		spin_unlock(&conn->login_worker_lock);
138162306a36Sopenharmony_ci	}
138262306a36Sopenharmony_ci
138362306a36Sopenharmony_ci	if (ret < 0) {
138462306a36Sopenharmony_ci		iscsi_target_restore_sock_callbacks(conn);
138562306a36Sopenharmony_ci		iscsi_remove_failed_auth_entry(conn);
138662306a36Sopenharmony_ci	}
138762306a36Sopenharmony_ci	if (ret != 0) {
138862306a36Sopenharmony_ci		iscsit_stop_login_timer(conn);
138962306a36Sopenharmony_ci		cancel_delayed_work_sync(&conn->login_work);
139062306a36Sopenharmony_ci		iscsi_target_nego_release(conn);
139162306a36Sopenharmony_ci	}
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci	return ret;
139462306a36Sopenharmony_ci}
139562306a36Sopenharmony_ci
139662306a36Sopenharmony_civoid iscsi_target_nego_release(struct iscsit_conn *conn)
139762306a36Sopenharmony_ci{
139862306a36Sopenharmony_ci	struct iscsi_login *login = conn->conn_login;
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci	if (!login)
140162306a36Sopenharmony_ci		return;
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci	kfree(login->req_buf);
140462306a36Sopenharmony_ci	kfree(login->rsp_buf);
140562306a36Sopenharmony_ci	kfree(login);
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ci	conn->conn_login = NULL;
140862306a36Sopenharmony_ci}
1409