static int __iscsi_target_login_thread()

in iscsi/iscsi_target_login.c [1219:1429]


static int __iscsi_target_login_thread(struct iscsi_np *np)
{
	u8 *buffer, zero_tsih = 0;
	int ret = 0, rc;
	struct iscsi_conn *conn = NULL;
	struct iscsi_login *login;
	struct iscsi_portal_group *tpg = NULL;
	struct iscsi_login_req *pdu;
	struct iscsi_tpg_np *tpg_np;
	bool new_sess = false;

	flush_signals(current);

	spin_lock_bh(&np->np_thread_lock);
	if (atomic_dec_if_positive(&np->np_reset_count) >= 0) {
		np->np_thread_state = ISCSI_NP_THREAD_ACTIVE;
		spin_unlock_bh(&np->np_thread_lock);
		complete(&np->np_restart_comp);
		return 1;
	} else if (np->np_thread_state == ISCSI_NP_THREAD_SHUTDOWN) {
		spin_unlock_bh(&np->np_thread_lock);
		goto exit;
	} else {
		np->np_thread_state = ISCSI_NP_THREAD_ACTIVE;
	}
	spin_unlock_bh(&np->np_thread_lock);

	conn = iscsit_alloc_conn(np);
	if (!conn) {
		/* Get another socket */
		return 1;
	}

	rc = np->np_transport->iscsit_accept_np(np, conn);
	if (rc == -ENOSYS) {
		complete(&np->np_restart_comp);
		iscsit_free_conn(conn);
		goto exit;
	} else if (rc < 0) {
		spin_lock_bh(&np->np_thread_lock);
		if (atomic_dec_if_positive(&np->np_reset_count) >= 0) {
			np->np_thread_state = ISCSI_NP_THREAD_ACTIVE;
			spin_unlock_bh(&np->np_thread_lock);
			complete(&np->np_restart_comp);
			iscsit_free_conn(conn);
			/* Get another socket */
			return 1;
		}
		spin_unlock_bh(&np->np_thread_lock);
		iscsit_free_conn(conn);
		return 1;
	}
	/*
	 * Perform the remaining iSCSI connection initialization items..
	 */
	login = iscsi_login_init_conn(conn);
	if (!login) {
		goto new_sess_out;
	}

	iscsi_start_login_thread_timer(np);

	pr_debug("Moving to TARG_CONN_STATE_XPT_UP.\n");
	conn->conn_state = TARG_CONN_STATE_XPT_UP;
	/*
	 * This will process the first login request + payload..
	 */
	rc = np->np_transport->iscsit_get_login_rx(conn, login);
	if (rc == 1)
		return 1;
	else if (rc < 0)
		goto new_sess_out;

	buffer = &login->req[0];
	pdu = (struct iscsi_login_req *)buffer;
	/*
	 * Used by iscsit_tx_login_rsp() for Login Resonses PDUs
	 * when Status-Class != 0.
	*/
	conn->login_itt	= pdu->itt;

	spin_lock_bh(&np->np_thread_lock);
	if (np->np_thread_state != ISCSI_NP_THREAD_ACTIVE) {
		spin_unlock_bh(&np->np_thread_lock);
		pr_err("iSCSI Network Portal on %pISpc currently not"
			" active.\n", &np->np_sockaddr);
		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR,
				ISCSI_LOGIN_STATUS_SVC_UNAVAILABLE);
		goto new_sess_out;
	}
	spin_unlock_bh(&np->np_thread_lock);

	conn->network_transport = np->np_network_transport;

	pr_debug("Received iSCSI login request from %pISpc on %s Network"
		" Portal %pISpc\n", &conn->login_sockaddr, np->np_transport->name,
		&conn->local_sockaddr);

	pr_debug("Moving to TARG_CONN_STATE_IN_LOGIN.\n");
	conn->conn_state	= TARG_CONN_STATE_IN_LOGIN;

	if (iscsi_login_check_initiator_version(conn, pdu->max_version,
			pdu->min_version) < 0)
		goto new_sess_out;

	zero_tsih = (pdu->tsih == 0x0000);
	if (zero_tsih) {
		/*
		 * This is the leading connection of a new session.
		 * We wait until after authentication to check for
		 * session reinstatement.
		 */
		if (iscsi_login_zero_tsih_s1(conn, buffer) < 0)
			goto new_sess_out;
	} else {
		/*
		 * Add a new connection to an existing session.
		 * We check for a non-existant session in
		 * iscsi_login_non_zero_tsih_s2() below based
		 * on ISID/TSIH, but wait until after authentication
		 * to check for connection reinstatement, etc.
		 */
		if (iscsi_login_non_zero_tsih_s1(conn, buffer) < 0)
			goto new_sess_out;
	}
	/*
	 * SessionType: Discovery
	 *
	 * 	Locates Default Portal
	 *
	 * SessionType: Normal
	 *
	 * 	Locates Target Portal from NP -> Target IQN
	 */
	rc = iscsi_target_locate_portal(np, conn, login);
	if (rc < 0) {
		tpg = conn->tpg;
		goto new_sess_out;
	}
	login->zero_tsih = zero_tsih;

	if (conn->sess)
		conn->sess->se_sess->sup_prot_ops =
			conn->conn_transport->iscsit_get_sup_prot_ops(conn);

	tpg = conn->tpg;
	if (!tpg) {
		pr_err("Unable to locate struct iscsi_conn->tpg\n");
		goto new_sess_out;
	}

	if (zero_tsih) {
		if (iscsi_login_zero_tsih_s2(conn) < 0)
			goto new_sess_out;
	} else {
		if (iscsi_login_non_zero_tsih_s2(conn, buffer) < 0)
			goto old_sess_out;
	}

	if (conn->conn_transport->iscsit_validate_params) {
		ret = conn->conn_transport->iscsit_validate_params(conn);
		if (ret < 0) {
			if (zero_tsih)
				goto new_sess_out;
			else
				goto old_sess_out;
		}
	}

	ret = iscsi_target_start_negotiation(login, conn);
	if (ret < 0)
		goto new_sess_out;

	iscsi_stop_login_thread_timer(np);

	if (ret == 1) {
		tpg_np = conn->tpg_np;

		iscsi_post_login_handler(np, conn, zero_tsih);
		iscsit_deaccess_np(np, tpg, tpg_np);
	}

	tpg = NULL;
	tpg_np = NULL;
	/* Get another socket */
	return 1;

new_sess_out:
	new_sess = true;
old_sess_out:
	iscsi_stop_login_thread_timer(np);
	tpg_np = conn->tpg_np;
	iscsi_target_login_sess_out(conn, zero_tsih, new_sess);
	new_sess = false;

	if (tpg) {
		iscsit_deaccess_np(np, tpg, tpg_np);
		tpg = NULL;
		tpg_np = NULL;
	}

	return 1;

exit:
	iscsi_stop_login_thread_timer(np);
	spin_lock_bh(&np->np_thread_lock);
	np->np_thread_state = ISCSI_NP_THREAD_EXIT;
	spin_unlock_bh(&np->np_thread_lock);

	return 0;
}