18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * SBP2 target driver (SCSI over IEEE1394 in target mode)
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2011  Chris Boot <bootc@bootc.net>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "sbp_target"
98c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/init.h>
148c2ecf20Sopenharmony_ci#include <linux/types.h>
158c2ecf20Sopenharmony_ci#include <linux/string.h>
168c2ecf20Sopenharmony_ci#include <linux/configfs.h>
178c2ecf20Sopenharmony_ci#include <linux/ctype.h>
188c2ecf20Sopenharmony_ci#include <linux/delay.h>
198c2ecf20Sopenharmony_ci#include <linux/firewire.h>
208c2ecf20Sopenharmony_ci#include <linux/firewire-constants.h>
218c2ecf20Sopenharmony_ci#include <scsi/scsi_proto.h>
228c2ecf20Sopenharmony_ci#include <scsi/scsi_tcq.h>
238c2ecf20Sopenharmony_ci#include <target/target_core_base.h>
248c2ecf20Sopenharmony_ci#include <target/target_core_backend.h>
258c2ecf20Sopenharmony_ci#include <target/target_core_fabric.h>
268c2ecf20Sopenharmony_ci#include <asm/unaligned.h>
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#include "sbp_target.h"
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci/* FireWire address region for management and command block address handlers */
318c2ecf20Sopenharmony_cistatic const struct fw_address_region sbp_register_region = {
328c2ecf20Sopenharmony_ci	.start	= CSR_REGISTER_BASE + 0x10000,
338c2ecf20Sopenharmony_ci	.end	= 0x1000000000000ULL,
348c2ecf20Sopenharmony_ci};
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic const u32 sbp_unit_directory_template[] = {
378c2ecf20Sopenharmony_ci	0x1200609e, /* unit_specifier_id: NCITS/T10 */
388c2ecf20Sopenharmony_ci	0x13010483, /* unit_sw_version: 1155D Rev 4 */
398c2ecf20Sopenharmony_ci	0x3800609e, /* command_set_specifier_id: NCITS/T10 */
408c2ecf20Sopenharmony_ci	0x390104d8, /* command_set: SPC-2 */
418c2ecf20Sopenharmony_ci	0x3b000000, /* command_set_revision: 0 */
428c2ecf20Sopenharmony_ci	0x3c000001, /* firmware_revision: 1 */
438c2ecf20Sopenharmony_ci};
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#define SESSION_MAINTENANCE_INTERVAL HZ
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic atomic_t login_id = ATOMIC_INIT(0);
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistatic void session_maintenance_work(struct work_struct *);
508c2ecf20Sopenharmony_cistatic int sbp_run_transaction(struct fw_card *, int, int, int, int,
518c2ecf20Sopenharmony_ci		unsigned long long, void *, size_t);
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic int read_peer_guid(u64 *guid, const struct sbp_management_request *req)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	int ret;
568c2ecf20Sopenharmony_ci	__be32 high, low;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	ret = sbp_run_transaction(req->card, TCODE_READ_QUADLET_REQUEST,
598c2ecf20Sopenharmony_ci			req->node_addr, req->generation, req->speed,
608c2ecf20Sopenharmony_ci			(CSR_REGISTER_BASE | CSR_CONFIG_ROM) + 3 * 4,
618c2ecf20Sopenharmony_ci			&high, sizeof(high));
628c2ecf20Sopenharmony_ci	if (ret != RCODE_COMPLETE)
638c2ecf20Sopenharmony_ci		return ret;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	ret = sbp_run_transaction(req->card, TCODE_READ_QUADLET_REQUEST,
668c2ecf20Sopenharmony_ci			req->node_addr, req->generation, req->speed,
678c2ecf20Sopenharmony_ci			(CSR_REGISTER_BASE | CSR_CONFIG_ROM) + 4 * 4,
688c2ecf20Sopenharmony_ci			&low, sizeof(low));
698c2ecf20Sopenharmony_ci	if (ret != RCODE_COMPLETE)
708c2ecf20Sopenharmony_ci		return ret;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	*guid = (u64)be32_to_cpu(high) << 32 | be32_to_cpu(low);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	return RCODE_COMPLETE;
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic struct sbp_session *sbp_session_find_by_guid(
788c2ecf20Sopenharmony_ci	struct sbp_tpg *tpg, u64 guid)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	struct se_session *se_sess;
818c2ecf20Sopenharmony_ci	struct sbp_session *sess, *found = NULL;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	spin_lock_bh(&tpg->se_tpg.session_lock);
848c2ecf20Sopenharmony_ci	list_for_each_entry(se_sess, &tpg->se_tpg.tpg_sess_list, sess_list) {
858c2ecf20Sopenharmony_ci		sess = se_sess->fabric_sess_ptr;
868c2ecf20Sopenharmony_ci		if (sess->guid == guid)
878c2ecf20Sopenharmony_ci			found = sess;
888c2ecf20Sopenharmony_ci	}
898c2ecf20Sopenharmony_ci	spin_unlock_bh(&tpg->se_tpg.session_lock);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	return found;
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic struct sbp_login_descriptor *sbp_login_find_by_lun(
958c2ecf20Sopenharmony_ci		struct sbp_session *session, u32 unpacked_lun)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	struct sbp_login_descriptor *login, *found = NULL;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	spin_lock_bh(&session->lock);
1008c2ecf20Sopenharmony_ci	list_for_each_entry(login, &session->login_list, link) {
1018c2ecf20Sopenharmony_ci		if (login->login_lun == unpacked_lun)
1028c2ecf20Sopenharmony_ci			found = login;
1038c2ecf20Sopenharmony_ci	}
1048c2ecf20Sopenharmony_ci	spin_unlock_bh(&session->lock);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	return found;
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic int sbp_login_count_all_by_lun(
1108c2ecf20Sopenharmony_ci		struct sbp_tpg *tpg,
1118c2ecf20Sopenharmony_ci		u32 unpacked_lun,
1128c2ecf20Sopenharmony_ci		int exclusive)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	struct se_session *se_sess;
1158c2ecf20Sopenharmony_ci	struct sbp_session *sess;
1168c2ecf20Sopenharmony_ci	struct sbp_login_descriptor *login;
1178c2ecf20Sopenharmony_ci	int count = 0;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	spin_lock_bh(&tpg->se_tpg.session_lock);
1208c2ecf20Sopenharmony_ci	list_for_each_entry(se_sess, &tpg->se_tpg.tpg_sess_list, sess_list) {
1218c2ecf20Sopenharmony_ci		sess = se_sess->fabric_sess_ptr;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci		spin_lock_bh(&sess->lock);
1248c2ecf20Sopenharmony_ci		list_for_each_entry(login, &sess->login_list, link) {
1258c2ecf20Sopenharmony_ci			if (login->login_lun != unpacked_lun)
1268c2ecf20Sopenharmony_ci				continue;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci			if (!exclusive || login->exclusive)
1298c2ecf20Sopenharmony_ci				count++;
1308c2ecf20Sopenharmony_ci		}
1318c2ecf20Sopenharmony_ci		spin_unlock_bh(&sess->lock);
1328c2ecf20Sopenharmony_ci	}
1338c2ecf20Sopenharmony_ci	spin_unlock_bh(&tpg->se_tpg.session_lock);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	return count;
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistatic struct sbp_login_descriptor *sbp_login_find_by_id(
1398c2ecf20Sopenharmony_ci	struct sbp_tpg *tpg, int login_id)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	struct se_session *se_sess;
1428c2ecf20Sopenharmony_ci	struct sbp_session *sess;
1438c2ecf20Sopenharmony_ci	struct sbp_login_descriptor *login, *found = NULL;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	spin_lock_bh(&tpg->se_tpg.session_lock);
1468c2ecf20Sopenharmony_ci	list_for_each_entry(se_sess, &tpg->se_tpg.tpg_sess_list, sess_list) {
1478c2ecf20Sopenharmony_ci		sess = se_sess->fabric_sess_ptr;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci		spin_lock_bh(&sess->lock);
1508c2ecf20Sopenharmony_ci		list_for_each_entry(login, &sess->login_list, link) {
1518c2ecf20Sopenharmony_ci			if (login->login_id == login_id)
1528c2ecf20Sopenharmony_ci				found = login;
1538c2ecf20Sopenharmony_ci		}
1548c2ecf20Sopenharmony_ci		spin_unlock_bh(&sess->lock);
1558c2ecf20Sopenharmony_ci	}
1568c2ecf20Sopenharmony_ci	spin_unlock_bh(&tpg->se_tpg.session_lock);
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	return found;
1598c2ecf20Sopenharmony_ci}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_cistatic u32 sbp_get_lun_from_tpg(struct sbp_tpg *tpg, u32 login_lun, int *err)
1628c2ecf20Sopenharmony_ci{
1638c2ecf20Sopenharmony_ci	struct se_portal_group *se_tpg = &tpg->se_tpg;
1648c2ecf20Sopenharmony_ci	struct se_lun *se_lun;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	rcu_read_lock();
1678c2ecf20Sopenharmony_ci	hlist_for_each_entry_rcu(se_lun, &se_tpg->tpg_lun_hlist, link) {
1688c2ecf20Sopenharmony_ci		if (se_lun->unpacked_lun == login_lun) {
1698c2ecf20Sopenharmony_ci			rcu_read_unlock();
1708c2ecf20Sopenharmony_ci			*err = 0;
1718c2ecf20Sopenharmony_ci			return login_lun;
1728c2ecf20Sopenharmony_ci		}
1738c2ecf20Sopenharmony_ci	}
1748c2ecf20Sopenharmony_ci	rcu_read_unlock();
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	*err = -ENODEV;
1778c2ecf20Sopenharmony_ci	return login_lun;
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_cistatic struct sbp_session *sbp_session_create(
1818c2ecf20Sopenharmony_ci		struct sbp_tpg *tpg,
1828c2ecf20Sopenharmony_ci		u64 guid)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	struct sbp_session *sess;
1858c2ecf20Sopenharmony_ci	int ret;
1868c2ecf20Sopenharmony_ci	char guid_str[17];
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	snprintf(guid_str, sizeof(guid_str), "%016llx", guid);
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	sess = kmalloc(sizeof(*sess), GFP_KERNEL);
1918c2ecf20Sopenharmony_ci	if (!sess)
1928c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	spin_lock_init(&sess->lock);
1958c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&sess->login_list);
1968c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&sess->maint_work, session_maintenance_work);
1978c2ecf20Sopenharmony_ci	sess->guid = guid;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	sess->se_sess = target_setup_session(&tpg->se_tpg, 128,
2008c2ecf20Sopenharmony_ci					     sizeof(struct sbp_target_request),
2018c2ecf20Sopenharmony_ci					     TARGET_PROT_NORMAL, guid_str,
2028c2ecf20Sopenharmony_ci					     sess, NULL);
2038c2ecf20Sopenharmony_ci	if (IS_ERR(sess->se_sess)) {
2048c2ecf20Sopenharmony_ci		pr_err("failed to init se_session\n");
2058c2ecf20Sopenharmony_ci		ret = PTR_ERR(sess->se_sess);
2068c2ecf20Sopenharmony_ci		kfree(sess);
2078c2ecf20Sopenharmony_ci		return ERR_PTR(ret);
2088c2ecf20Sopenharmony_ci	}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	return sess;
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_cistatic void sbp_session_release(struct sbp_session *sess, bool cancel_work)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	spin_lock_bh(&sess->lock);
2168c2ecf20Sopenharmony_ci	if (!list_empty(&sess->login_list)) {
2178c2ecf20Sopenharmony_ci		spin_unlock_bh(&sess->lock);
2188c2ecf20Sopenharmony_ci		return;
2198c2ecf20Sopenharmony_ci	}
2208c2ecf20Sopenharmony_ci	spin_unlock_bh(&sess->lock);
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	if (cancel_work)
2238c2ecf20Sopenharmony_ci		cancel_delayed_work_sync(&sess->maint_work);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	target_remove_session(sess->se_sess);
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	if (sess->card)
2288c2ecf20Sopenharmony_ci		fw_card_put(sess->card);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	kfree(sess);
2318c2ecf20Sopenharmony_ci}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_cistatic void sbp_target_agent_unregister(struct sbp_target_agent *);
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_cistatic void sbp_login_release(struct sbp_login_descriptor *login,
2368c2ecf20Sopenharmony_ci	bool cancel_work)
2378c2ecf20Sopenharmony_ci{
2388c2ecf20Sopenharmony_ci	struct sbp_session *sess = login->sess;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	/* FIXME: abort/wait on tasks */
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	sbp_target_agent_unregister(login->tgt_agt);
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	if (sess) {
2458c2ecf20Sopenharmony_ci		spin_lock_bh(&sess->lock);
2468c2ecf20Sopenharmony_ci		list_del(&login->link);
2478c2ecf20Sopenharmony_ci		spin_unlock_bh(&sess->lock);
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci		sbp_session_release(sess, cancel_work);
2508c2ecf20Sopenharmony_ci	}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	kfree(login);
2538c2ecf20Sopenharmony_ci}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_cistatic struct sbp_target_agent *sbp_target_agent_register(
2568c2ecf20Sopenharmony_ci	struct sbp_login_descriptor *);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_cistatic void sbp_management_request_login(
2598c2ecf20Sopenharmony_ci	struct sbp_management_agent *agent, struct sbp_management_request *req,
2608c2ecf20Sopenharmony_ci	int *status_data_size)
2618c2ecf20Sopenharmony_ci{
2628c2ecf20Sopenharmony_ci	struct sbp_tport *tport = agent->tport;
2638c2ecf20Sopenharmony_ci	struct sbp_tpg *tpg = tport->tpg;
2648c2ecf20Sopenharmony_ci	struct sbp_session *sess;
2658c2ecf20Sopenharmony_ci	struct sbp_login_descriptor *login;
2668c2ecf20Sopenharmony_ci	struct sbp_login_response_block *response;
2678c2ecf20Sopenharmony_ci	u64 guid;
2688c2ecf20Sopenharmony_ci	u32 unpacked_lun;
2698c2ecf20Sopenharmony_ci	int login_response_len, ret;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	unpacked_lun = sbp_get_lun_from_tpg(tpg,
2728c2ecf20Sopenharmony_ci			LOGIN_ORB_LUN(be32_to_cpu(req->orb.misc)), &ret);
2738c2ecf20Sopenharmony_ci	if (ret) {
2748c2ecf20Sopenharmony_ci		pr_notice("login to unknown LUN: %d\n",
2758c2ecf20Sopenharmony_ci			LOGIN_ORB_LUN(be32_to_cpu(req->orb.misc)));
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci		req->status.status = cpu_to_be32(
2788c2ecf20Sopenharmony_ci			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
2798c2ecf20Sopenharmony_ci			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_LUN_NOTSUPP));
2808c2ecf20Sopenharmony_ci		return;
2818c2ecf20Sopenharmony_ci	}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	ret = read_peer_guid(&guid, req);
2848c2ecf20Sopenharmony_ci	if (ret != RCODE_COMPLETE) {
2858c2ecf20Sopenharmony_ci		pr_warn("failed to read peer GUID: %d\n", ret);
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci		req->status.status = cpu_to_be32(
2888c2ecf20Sopenharmony_ci			STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
2898c2ecf20Sopenharmony_ci			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
2908c2ecf20Sopenharmony_ci		return;
2918c2ecf20Sopenharmony_ci	}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	pr_notice("mgt_agent LOGIN to LUN %d from %016llx\n",
2948c2ecf20Sopenharmony_ci		unpacked_lun, guid);
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	sess = sbp_session_find_by_guid(tpg, guid);
2978c2ecf20Sopenharmony_ci	if (sess) {
2988c2ecf20Sopenharmony_ci		login = sbp_login_find_by_lun(sess, unpacked_lun);
2998c2ecf20Sopenharmony_ci		if (login) {
3008c2ecf20Sopenharmony_ci			pr_notice("initiator already logged-in\n");
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci			/*
3038c2ecf20Sopenharmony_ci			 * SBP-2 R4 says we should return access denied, but
3048c2ecf20Sopenharmony_ci			 * that can confuse initiators. Instead we need to
3058c2ecf20Sopenharmony_ci			 * treat this like a reconnect, but send the login
3068c2ecf20Sopenharmony_ci			 * response block like a fresh login.
3078c2ecf20Sopenharmony_ci			 *
3088c2ecf20Sopenharmony_ci			 * This is required particularly in the case of Apple
3098c2ecf20Sopenharmony_ci			 * devices booting off the FireWire target, where
3108c2ecf20Sopenharmony_ci			 * the firmware has an active login to the target. When
3118c2ecf20Sopenharmony_ci			 * the OS takes control of the session it issues its own
3128c2ecf20Sopenharmony_ci			 * LOGIN rather than a RECONNECT. To avoid the machine
3138c2ecf20Sopenharmony_ci			 * waiting until the reconnect_hold expires, we can skip
3148c2ecf20Sopenharmony_ci			 * the ACCESS_DENIED errors to speed things up.
3158c2ecf20Sopenharmony_ci			 */
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci			goto already_logged_in;
3188c2ecf20Sopenharmony_ci		}
3198c2ecf20Sopenharmony_ci	}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	/*
3228c2ecf20Sopenharmony_ci	 * check exclusive bit in login request
3238c2ecf20Sopenharmony_ci	 * reject with access_denied if any logins present
3248c2ecf20Sopenharmony_ci	 */
3258c2ecf20Sopenharmony_ci	if (LOGIN_ORB_EXCLUSIVE(be32_to_cpu(req->orb.misc)) &&
3268c2ecf20Sopenharmony_ci			sbp_login_count_all_by_lun(tpg, unpacked_lun, 0)) {
3278c2ecf20Sopenharmony_ci		pr_warn("refusing exclusive login with other active logins\n");
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci		req->status.status = cpu_to_be32(
3308c2ecf20Sopenharmony_ci			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
3318c2ecf20Sopenharmony_ci			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED));
3328c2ecf20Sopenharmony_ci		return;
3338c2ecf20Sopenharmony_ci	}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	/*
3368c2ecf20Sopenharmony_ci	 * check exclusive bit in any existing login descriptor
3378c2ecf20Sopenharmony_ci	 * reject with access_denied if any exclusive logins present
3388c2ecf20Sopenharmony_ci	 */
3398c2ecf20Sopenharmony_ci	if (sbp_login_count_all_by_lun(tpg, unpacked_lun, 1)) {
3408c2ecf20Sopenharmony_ci		pr_warn("refusing login while another exclusive login present\n");
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci		req->status.status = cpu_to_be32(
3438c2ecf20Sopenharmony_ci			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
3448c2ecf20Sopenharmony_ci			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED));
3458c2ecf20Sopenharmony_ci		return;
3468c2ecf20Sopenharmony_ci	}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	/*
3498c2ecf20Sopenharmony_ci	 * check we haven't exceeded the number of allowed logins
3508c2ecf20Sopenharmony_ci	 * reject with resources_unavailable if we have
3518c2ecf20Sopenharmony_ci	 */
3528c2ecf20Sopenharmony_ci	if (sbp_login_count_all_by_lun(tpg, unpacked_lun, 0) >=
3538c2ecf20Sopenharmony_ci			tport->max_logins_per_lun) {
3548c2ecf20Sopenharmony_ci		pr_warn("max number of logins reached\n");
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci		req->status.status = cpu_to_be32(
3578c2ecf20Sopenharmony_ci			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
3588c2ecf20Sopenharmony_ci			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_RESOURCES_UNAVAIL));
3598c2ecf20Sopenharmony_ci		return;
3608c2ecf20Sopenharmony_ci	}
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	if (!sess) {
3638c2ecf20Sopenharmony_ci		sess = sbp_session_create(tpg, guid);
3648c2ecf20Sopenharmony_ci		if (IS_ERR(sess)) {
3658c2ecf20Sopenharmony_ci			switch (PTR_ERR(sess)) {
3668c2ecf20Sopenharmony_ci			case -EPERM:
3678c2ecf20Sopenharmony_ci				ret = SBP_STATUS_ACCESS_DENIED;
3688c2ecf20Sopenharmony_ci				break;
3698c2ecf20Sopenharmony_ci			default:
3708c2ecf20Sopenharmony_ci				ret = SBP_STATUS_RESOURCES_UNAVAIL;
3718c2ecf20Sopenharmony_ci				break;
3728c2ecf20Sopenharmony_ci			}
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci			req->status.status = cpu_to_be32(
3758c2ecf20Sopenharmony_ci				STATUS_BLOCK_RESP(
3768c2ecf20Sopenharmony_ci					STATUS_RESP_REQUEST_COMPLETE) |
3778c2ecf20Sopenharmony_ci				STATUS_BLOCK_SBP_STATUS(ret));
3788c2ecf20Sopenharmony_ci			return;
3798c2ecf20Sopenharmony_ci		}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci		sess->node_id = req->node_addr;
3828c2ecf20Sopenharmony_ci		sess->card = fw_card_get(req->card);
3838c2ecf20Sopenharmony_ci		sess->generation = req->generation;
3848c2ecf20Sopenharmony_ci		sess->speed = req->speed;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci		schedule_delayed_work(&sess->maint_work,
3878c2ecf20Sopenharmony_ci				SESSION_MAINTENANCE_INTERVAL);
3888c2ecf20Sopenharmony_ci	}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	/* only take the latest reconnect_hold into account */
3918c2ecf20Sopenharmony_ci	sess->reconnect_hold = min(
3928c2ecf20Sopenharmony_ci		1 << LOGIN_ORB_RECONNECT(be32_to_cpu(req->orb.misc)),
3938c2ecf20Sopenharmony_ci		tport->max_reconnect_timeout) - 1;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	login = kmalloc(sizeof(*login), GFP_KERNEL);
3968c2ecf20Sopenharmony_ci	if (!login) {
3978c2ecf20Sopenharmony_ci		pr_err("failed to allocate login descriptor\n");
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci		sbp_session_release(sess, true);
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci		req->status.status = cpu_to_be32(
4028c2ecf20Sopenharmony_ci			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
4038c2ecf20Sopenharmony_ci			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_RESOURCES_UNAVAIL));
4048c2ecf20Sopenharmony_ci		return;
4058c2ecf20Sopenharmony_ci	}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	login->sess = sess;
4088c2ecf20Sopenharmony_ci	login->login_lun = unpacked_lun;
4098c2ecf20Sopenharmony_ci	login->status_fifo_addr = sbp2_pointer_to_addr(&req->orb.status_fifo);
4108c2ecf20Sopenharmony_ci	login->exclusive = LOGIN_ORB_EXCLUSIVE(be32_to_cpu(req->orb.misc));
4118c2ecf20Sopenharmony_ci	login->login_id = atomic_inc_return(&login_id);
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	login->tgt_agt = sbp_target_agent_register(login);
4148c2ecf20Sopenharmony_ci	if (IS_ERR(login->tgt_agt)) {
4158c2ecf20Sopenharmony_ci		ret = PTR_ERR(login->tgt_agt);
4168c2ecf20Sopenharmony_ci		pr_err("failed to map command block handler: %d\n", ret);
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci		sbp_session_release(sess, true);
4198c2ecf20Sopenharmony_ci		kfree(login);
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci		req->status.status = cpu_to_be32(
4228c2ecf20Sopenharmony_ci			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
4238c2ecf20Sopenharmony_ci			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_RESOURCES_UNAVAIL));
4248c2ecf20Sopenharmony_ci		return;
4258c2ecf20Sopenharmony_ci	}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	spin_lock_bh(&sess->lock);
4288c2ecf20Sopenharmony_ci	list_add_tail(&login->link, &sess->login_list);
4298c2ecf20Sopenharmony_ci	spin_unlock_bh(&sess->lock);
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_cialready_logged_in:
4328c2ecf20Sopenharmony_ci	response = kzalloc(sizeof(*response), GFP_KERNEL);
4338c2ecf20Sopenharmony_ci	if (!response) {
4348c2ecf20Sopenharmony_ci		pr_err("failed to allocate login response block\n");
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci		sbp_login_release(login, true);
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci		req->status.status = cpu_to_be32(
4398c2ecf20Sopenharmony_ci			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
4408c2ecf20Sopenharmony_ci			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_RESOURCES_UNAVAIL));
4418c2ecf20Sopenharmony_ci		return;
4428c2ecf20Sopenharmony_ci	}
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	login_response_len = clamp_val(
4458c2ecf20Sopenharmony_ci			LOGIN_ORB_RESPONSE_LENGTH(be32_to_cpu(req->orb.length)),
4468c2ecf20Sopenharmony_ci			12, sizeof(*response));
4478c2ecf20Sopenharmony_ci	response->misc = cpu_to_be32(
4488c2ecf20Sopenharmony_ci		((login_response_len & 0xffff) << 16) |
4498c2ecf20Sopenharmony_ci		(login->login_id & 0xffff));
4508c2ecf20Sopenharmony_ci	response->reconnect_hold = cpu_to_be32(sess->reconnect_hold & 0xffff);
4518c2ecf20Sopenharmony_ci	addr_to_sbp2_pointer(login->tgt_agt->handler.offset,
4528c2ecf20Sopenharmony_ci		&response->command_block_agent);
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	ret = sbp_run_transaction(sess->card, TCODE_WRITE_BLOCK_REQUEST,
4558c2ecf20Sopenharmony_ci		sess->node_id, sess->generation, sess->speed,
4568c2ecf20Sopenharmony_ci		sbp2_pointer_to_addr(&req->orb.ptr2), response,
4578c2ecf20Sopenharmony_ci		login_response_len);
4588c2ecf20Sopenharmony_ci	if (ret != RCODE_COMPLETE) {
4598c2ecf20Sopenharmony_ci		pr_debug("failed to write login response block: %x\n", ret);
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci		kfree(response);
4628c2ecf20Sopenharmony_ci		sbp_login_release(login, true);
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci		req->status.status = cpu_to_be32(
4658c2ecf20Sopenharmony_ci			STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
4668c2ecf20Sopenharmony_ci			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
4678c2ecf20Sopenharmony_ci		return;
4688c2ecf20Sopenharmony_ci	}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	kfree(response);
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	req->status.status = cpu_to_be32(
4738c2ecf20Sopenharmony_ci		STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
4748c2ecf20Sopenharmony_ci		STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
4758c2ecf20Sopenharmony_ci}
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_cistatic void sbp_management_request_query_logins(
4788c2ecf20Sopenharmony_ci	struct sbp_management_agent *agent, struct sbp_management_request *req,
4798c2ecf20Sopenharmony_ci	int *status_data_size)
4808c2ecf20Sopenharmony_ci{
4818c2ecf20Sopenharmony_ci	pr_notice("QUERY LOGINS not implemented\n");
4828c2ecf20Sopenharmony_ci	/* FIXME: implement */
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	req->status.status = cpu_to_be32(
4858c2ecf20Sopenharmony_ci		STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
4868c2ecf20Sopenharmony_ci		STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
4878c2ecf20Sopenharmony_ci}
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_cistatic void sbp_management_request_reconnect(
4908c2ecf20Sopenharmony_ci	struct sbp_management_agent *agent, struct sbp_management_request *req,
4918c2ecf20Sopenharmony_ci	int *status_data_size)
4928c2ecf20Sopenharmony_ci{
4938c2ecf20Sopenharmony_ci	struct sbp_tport *tport = agent->tport;
4948c2ecf20Sopenharmony_ci	struct sbp_tpg *tpg = tport->tpg;
4958c2ecf20Sopenharmony_ci	int ret;
4968c2ecf20Sopenharmony_ci	u64 guid;
4978c2ecf20Sopenharmony_ci	struct sbp_login_descriptor *login;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	ret = read_peer_guid(&guid, req);
5008c2ecf20Sopenharmony_ci	if (ret != RCODE_COMPLETE) {
5018c2ecf20Sopenharmony_ci		pr_warn("failed to read peer GUID: %d\n", ret);
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci		req->status.status = cpu_to_be32(
5048c2ecf20Sopenharmony_ci			STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
5058c2ecf20Sopenharmony_ci			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
5068c2ecf20Sopenharmony_ci		return;
5078c2ecf20Sopenharmony_ci	}
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	pr_notice("mgt_agent RECONNECT from %016llx\n", guid);
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	login = sbp_login_find_by_id(tpg,
5128c2ecf20Sopenharmony_ci		RECONNECT_ORB_LOGIN_ID(be32_to_cpu(req->orb.misc)));
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	if (!login) {
5158c2ecf20Sopenharmony_ci		pr_err("mgt_agent RECONNECT unknown login ID\n");
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci		req->status.status = cpu_to_be32(
5188c2ecf20Sopenharmony_ci			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
5198c2ecf20Sopenharmony_ci			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED));
5208c2ecf20Sopenharmony_ci		return;
5218c2ecf20Sopenharmony_ci	}
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	if (login->sess->guid != guid) {
5248c2ecf20Sopenharmony_ci		pr_err("mgt_agent RECONNECT login GUID doesn't match\n");
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci		req->status.status = cpu_to_be32(
5278c2ecf20Sopenharmony_ci			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
5288c2ecf20Sopenharmony_ci			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED));
5298c2ecf20Sopenharmony_ci		return;
5308c2ecf20Sopenharmony_ci	}
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	spin_lock_bh(&login->sess->lock);
5338c2ecf20Sopenharmony_ci	if (login->sess->card)
5348c2ecf20Sopenharmony_ci		fw_card_put(login->sess->card);
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	/* update the node details */
5378c2ecf20Sopenharmony_ci	login->sess->generation = req->generation;
5388c2ecf20Sopenharmony_ci	login->sess->node_id = req->node_addr;
5398c2ecf20Sopenharmony_ci	login->sess->card = fw_card_get(req->card);
5408c2ecf20Sopenharmony_ci	login->sess->speed = req->speed;
5418c2ecf20Sopenharmony_ci	spin_unlock_bh(&login->sess->lock);
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	req->status.status = cpu_to_be32(
5448c2ecf20Sopenharmony_ci		STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
5458c2ecf20Sopenharmony_ci		STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
5468c2ecf20Sopenharmony_ci}
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_cistatic void sbp_management_request_logout(
5498c2ecf20Sopenharmony_ci	struct sbp_management_agent *agent, struct sbp_management_request *req,
5508c2ecf20Sopenharmony_ci	int *status_data_size)
5518c2ecf20Sopenharmony_ci{
5528c2ecf20Sopenharmony_ci	struct sbp_tport *tport = agent->tport;
5538c2ecf20Sopenharmony_ci	struct sbp_tpg *tpg = tport->tpg;
5548c2ecf20Sopenharmony_ci	int id;
5558c2ecf20Sopenharmony_ci	struct sbp_login_descriptor *login;
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	id = LOGOUT_ORB_LOGIN_ID(be32_to_cpu(req->orb.misc));
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	login = sbp_login_find_by_id(tpg, id);
5608c2ecf20Sopenharmony_ci	if (!login) {
5618c2ecf20Sopenharmony_ci		pr_warn("cannot find login: %d\n", id);
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci		req->status.status = cpu_to_be32(
5648c2ecf20Sopenharmony_ci			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
5658c2ecf20Sopenharmony_ci			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_LOGIN_ID_UNKNOWN));
5668c2ecf20Sopenharmony_ci		return;
5678c2ecf20Sopenharmony_ci	}
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	pr_info("mgt_agent LOGOUT from LUN %d session %d\n",
5708c2ecf20Sopenharmony_ci		login->login_lun, login->login_id);
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	if (req->node_addr != login->sess->node_id) {
5738c2ecf20Sopenharmony_ci		pr_warn("logout from different node ID\n");
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci		req->status.status = cpu_to_be32(
5768c2ecf20Sopenharmony_ci			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
5778c2ecf20Sopenharmony_ci			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_ACCESS_DENIED));
5788c2ecf20Sopenharmony_ci		return;
5798c2ecf20Sopenharmony_ci	}
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci	sbp_login_release(login, true);
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	req->status.status = cpu_to_be32(
5848c2ecf20Sopenharmony_ci		STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
5858c2ecf20Sopenharmony_ci		STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
5868c2ecf20Sopenharmony_ci}
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_cistatic void session_check_for_reset(struct sbp_session *sess)
5898c2ecf20Sopenharmony_ci{
5908c2ecf20Sopenharmony_ci	bool card_valid = false;
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	spin_lock_bh(&sess->lock);
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	if (sess->card) {
5958c2ecf20Sopenharmony_ci		spin_lock_irq(&sess->card->lock);
5968c2ecf20Sopenharmony_ci		card_valid = (sess->card->local_node != NULL);
5978c2ecf20Sopenharmony_ci		spin_unlock_irq(&sess->card->lock);
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci		if (!card_valid) {
6008c2ecf20Sopenharmony_ci			fw_card_put(sess->card);
6018c2ecf20Sopenharmony_ci			sess->card = NULL;
6028c2ecf20Sopenharmony_ci		}
6038c2ecf20Sopenharmony_ci	}
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	if (!card_valid || (sess->generation != sess->card->generation)) {
6068c2ecf20Sopenharmony_ci		pr_info("Waiting for reconnect from node: %016llx\n",
6078c2ecf20Sopenharmony_ci				sess->guid);
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci		sess->node_id = -1;
6108c2ecf20Sopenharmony_ci		sess->reconnect_expires = get_jiffies_64() +
6118c2ecf20Sopenharmony_ci			((sess->reconnect_hold + 1) * HZ);
6128c2ecf20Sopenharmony_ci	}
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	spin_unlock_bh(&sess->lock);
6158c2ecf20Sopenharmony_ci}
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_cistatic void session_reconnect_expired(struct sbp_session *sess)
6188c2ecf20Sopenharmony_ci{
6198c2ecf20Sopenharmony_ci	struct sbp_login_descriptor *login, *temp;
6208c2ecf20Sopenharmony_ci	LIST_HEAD(login_list);
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	pr_info("Reconnect timer expired for node: %016llx\n", sess->guid);
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	spin_lock_bh(&sess->lock);
6258c2ecf20Sopenharmony_ci	list_for_each_entry_safe(login, temp, &sess->login_list, link) {
6268c2ecf20Sopenharmony_ci		login->sess = NULL;
6278c2ecf20Sopenharmony_ci		list_move_tail(&login->link, &login_list);
6288c2ecf20Sopenharmony_ci	}
6298c2ecf20Sopenharmony_ci	spin_unlock_bh(&sess->lock);
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	list_for_each_entry_safe(login, temp, &login_list, link) {
6328c2ecf20Sopenharmony_ci		list_del(&login->link);
6338c2ecf20Sopenharmony_ci		sbp_login_release(login, false);
6348c2ecf20Sopenharmony_ci	}
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	sbp_session_release(sess, false);
6378c2ecf20Sopenharmony_ci}
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_cistatic void session_maintenance_work(struct work_struct *work)
6408c2ecf20Sopenharmony_ci{
6418c2ecf20Sopenharmony_ci	struct sbp_session *sess = container_of(work, struct sbp_session,
6428c2ecf20Sopenharmony_ci			maint_work.work);
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	/* could be called while tearing down the session */
6458c2ecf20Sopenharmony_ci	spin_lock_bh(&sess->lock);
6468c2ecf20Sopenharmony_ci	if (list_empty(&sess->login_list)) {
6478c2ecf20Sopenharmony_ci		spin_unlock_bh(&sess->lock);
6488c2ecf20Sopenharmony_ci		return;
6498c2ecf20Sopenharmony_ci	}
6508c2ecf20Sopenharmony_ci	spin_unlock_bh(&sess->lock);
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	if (sess->node_id != -1) {
6538c2ecf20Sopenharmony_ci		/* check for bus reset and make node_id invalid */
6548c2ecf20Sopenharmony_ci		session_check_for_reset(sess);
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci		schedule_delayed_work(&sess->maint_work,
6578c2ecf20Sopenharmony_ci				SESSION_MAINTENANCE_INTERVAL);
6588c2ecf20Sopenharmony_ci	} else if (!time_after64(get_jiffies_64(), sess->reconnect_expires)) {
6598c2ecf20Sopenharmony_ci		/* still waiting for reconnect */
6608c2ecf20Sopenharmony_ci		schedule_delayed_work(&sess->maint_work,
6618c2ecf20Sopenharmony_ci				SESSION_MAINTENANCE_INTERVAL);
6628c2ecf20Sopenharmony_ci	} else {
6638c2ecf20Sopenharmony_ci		/* reconnect timeout has expired */
6648c2ecf20Sopenharmony_ci		session_reconnect_expired(sess);
6658c2ecf20Sopenharmony_ci	}
6668c2ecf20Sopenharmony_ci}
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_cistatic int tgt_agent_rw_agent_state(struct fw_card *card, int tcode, void *data,
6698c2ecf20Sopenharmony_ci		struct sbp_target_agent *agent)
6708c2ecf20Sopenharmony_ci{
6718c2ecf20Sopenharmony_ci	int state;
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	switch (tcode) {
6748c2ecf20Sopenharmony_ci	case TCODE_READ_QUADLET_REQUEST:
6758c2ecf20Sopenharmony_ci		pr_debug("tgt_agent AGENT_STATE READ\n");
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci		spin_lock_bh(&agent->lock);
6788c2ecf20Sopenharmony_ci		state = agent->state;
6798c2ecf20Sopenharmony_ci		spin_unlock_bh(&agent->lock);
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci		*(__be32 *)data = cpu_to_be32(state);
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci		return RCODE_COMPLETE;
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	case TCODE_WRITE_QUADLET_REQUEST:
6868c2ecf20Sopenharmony_ci		/* ignored */
6878c2ecf20Sopenharmony_ci		return RCODE_COMPLETE;
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	default:
6908c2ecf20Sopenharmony_ci		return RCODE_TYPE_ERROR;
6918c2ecf20Sopenharmony_ci	}
6928c2ecf20Sopenharmony_ci}
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_cistatic int tgt_agent_rw_agent_reset(struct fw_card *card, int tcode, void *data,
6958c2ecf20Sopenharmony_ci		struct sbp_target_agent *agent)
6968c2ecf20Sopenharmony_ci{
6978c2ecf20Sopenharmony_ci	switch (tcode) {
6988c2ecf20Sopenharmony_ci	case TCODE_WRITE_QUADLET_REQUEST:
6998c2ecf20Sopenharmony_ci		pr_debug("tgt_agent AGENT_RESET\n");
7008c2ecf20Sopenharmony_ci		spin_lock_bh(&agent->lock);
7018c2ecf20Sopenharmony_ci		agent->state = AGENT_STATE_RESET;
7028c2ecf20Sopenharmony_ci		spin_unlock_bh(&agent->lock);
7038c2ecf20Sopenharmony_ci		return RCODE_COMPLETE;
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	default:
7068c2ecf20Sopenharmony_ci		return RCODE_TYPE_ERROR;
7078c2ecf20Sopenharmony_ci	}
7088c2ecf20Sopenharmony_ci}
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_cistatic int tgt_agent_rw_orb_pointer(struct fw_card *card, int tcode, void *data,
7118c2ecf20Sopenharmony_ci		struct sbp_target_agent *agent)
7128c2ecf20Sopenharmony_ci{
7138c2ecf20Sopenharmony_ci	struct sbp2_pointer *ptr = data;
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	switch (tcode) {
7168c2ecf20Sopenharmony_ci	case TCODE_WRITE_BLOCK_REQUEST:
7178c2ecf20Sopenharmony_ci		spin_lock_bh(&agent->lock);
7188c2ecf20Sopenharmony_ci		if (agent->state != AGENT_STATE_SUSPENDED &&
7198c2ecf20Sopenharmony_ci				agent->state != AGENT_STATE_RESET) {
7208c2ecf20Sopenharmony_ci			spin_unlock_bh(&agent->lock);
7218c2ecf20Sopenharmony_ci			pr_notice("Ignoring ORB_POINTER write while active.\n");
7228c2ecf20Sopenharmony_ci			return RCODE_CONFLICT_ERROR;
7238c2ecf20Sopenharmony_ci		}
7248c2ecf20Sopenharmony_ci		agent->state = AGENT_STATE_ACTIVE;
7258c2ecf20Sopenharmony_ci		spin_unlock_bh(&agent->lock);
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci		agent->orb_pointer = sbp2_pointer_to_addr(ptr);
7288c2ecf20Sopenharmony_ci		agent->doorbell = false;
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci		pr_debug("tgt_agent ORB_POINTER write: 0x%llx\n",
7318c2ecf20Sopenharmony_ci				agent->orb_pointer);
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci		queue_work(system_unbound_wq, &agent->work);
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci		return RCODE_COMPLETE;
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci	case TCODE_READ_BLOCK_REQUEST:
7388c2ecf20Sopenharmony_ci		pr_debug("tgt_agent ORB_POINTER READ\n");
7398c2ecf20Sopenharmony_ci		spin_lock_bh(&agent->lock);
7408c2ecf20Sopenharmony_ci		addr_to_sbp2_pointer(agent->orb_pointer, ptr);
7418c2ecf20Sopenharmony_ci		spin_unlock_bh(&agent->lock);
7428c2ecf20Sopenharmony_ci		return RCODE_COMPLETE;
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	default:
7458c2ecf20Sopenharmony_ci		return RCODE_TYPE_ERROR;
7468c2ecf20Sopenharmony_ci	}
7478c2ecf20Sopenharmony_ci}
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_cistatic int tgt_agent_rw_doorbell(struct fw_card *card, int tcode, void *data,
7508c2ecf20Sopenharmony_ci		struct sbp_target_agent *agent)
7518c2ecf20Sopenharmony_ci{
7528c2ecf20Sopenharmony_ci	switch (tcode) {
7538c2ecf20Sopenharmony_ci	case TCODE_WRITE_QUADLET_REQUEST:
7548c2ecf20Sopenharmony_ci		spin_lock_bh(&agent->lock);
7558c2ecf20Sopenharmony_ci		if (agent->state != AGENT_STATE_SUSPENDED) {
7568c2ecf20Sopenharmony_ci			spin_unlock_bh(&agent->lock);
7578c2ecf20Sopenharmony_ci			pr_debug("Ignoring DOORBELL while active.\n");
7588c2ecf20Sopenharmony_ci			return RCODE_CONFLICT_ERROR;
7598c2ecf20Sopenharmony_ci		}
7608c2ecf20Sopenharmony_ci		agent->state = AGENT_STATE_ACTIVE;
7618c2ecf20Sopenharmony_ci		spin_unlock_bh(&agent->lock);
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci		agent->doorbell = true;
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci		pr_debug("tgt_agent DOORBELL\n");
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci		queue_work(system_unbound_wq, &agent->work);
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci		return RCODE_COMPLETE;
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci	case TCODE_READ_QUADLET_REQUEST:
7728c2ecf20Sopenharmony_ci		return RCODE_COMPLETE;
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci	default:
7758c2ecf20Sopenharmony_ci		return RCODE_TYPE_ERROR;
7768c2ecf20Sopenharmony_ci	}
7778c2ecf20Sopenharmony_ci}
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_cistatic int tgt_agent_rw_unsolicited_status_enable(struct fw_card *card,
7808c2ecf20Sopenharmony_ci		int tcode, void *data, struct sbp_target_agent *agent)
7818c2ecf20Sopenharmony_ci{
7828c2ecf20Sopenharmony_ci	switch (tcode) {
7838c2ecf20Sopenharmony_ci	case TCODE_WRITE_QUADLET_REQUEST:
7848c2ecf20Sopenharmony_ci		pr_debug("tgt_agent UNSOLICITED_STATUS_ENABLE\n");
7858c2ecf20Sopenharmony_ci		/* ignored as we don't send unsolicited status */
7868c2ecf20Sopenharmony_ci		return RCODE_COMPLETE;
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci	case TCODE_READ_QUADLET_REQUEST:
7898c2ecf20Sopenharmony_ci		return RCODE_COMPLETE;
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	default:
7928c2ecf20Sopenharmony_ci		return RCODE_TYPE_ERROR;
7938c2ecf20Sopenharmony_ci	}
7948c2ecf20Sopenharmony_ci}
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_cistatic void tgt_agent_rw(struct fw_card *card, struct fw_request *request,
7978c2ecf20Sopenharmony_ci		int tcode, int destination, int source, int generation,
7988c2ecf20Sopenharmony_ci		unsigned long long offset, void *data, size_t length,
7998c2ecf20Sopenharmony_ci		void *callback_data)
8008c2ecf20Sopenharmony_ci{
8018c2ecf20Sopenharmony_ci	struct sbp_target_agent *agent = callback_data;
8028c2ecf20Sopenharmony_ci	struct sbp_session *sess = agent->login->sess;
8038c2ecf20Sopenharmony_ci	int sess_gen, sess_node, rcode;
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	spin_lock_bh(&sess->lock);
8068c2ecf20Sopenharmony_ci	sess_gen = sess->generation;
8078c2ecf20Sopenharmony_ci	sess_node = sess->node_id;
8088c2ecf20Sopenharmony_ci	spin_unlock_bh(&sess->lock);
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci	if (generation != sess_gen) {
8118c2ecf20Sopenharmony_ci		pr_notice("ignoring request with wrong generation\n");
8128c2ecf20Sopenharmony_ci		rcode = RCODE_TYPE_ERROR;
8138c2ecf20Sopenharmony_ci		goto out;
8148c2ecf20Sopenharmony_ci	}
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	if (source != sess_node) {
8178c2ecf20Sopenharmony_ci		pr_notice("ignoring request from foreign node (%x != %x)\n",
8188c2ecf20Sopenharmony_ci				source, sess_node);
8198c2ecf20Sopenharmony_ci		rcode = RCODE_TYPE_ERROR;
8208c2ecf20Sopenharmony_ci		goto out;
8218c2ecf20Sopenharmony_ci	}
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	/* turn offset into the offset from the start of the block */
8248c2ecf20Sopenharmony_ci	offset -= agent->handler.offset;
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci	if (offset == 0x00 && length == 4) {
8278c2ecf20Sopenharmony_ci		/* AGENT_STATE */
8288c2ecf20Sopenharmony_ci		rcode = tgt_agent_rw_agent_state(card, tcode, data, agent);
8298c2ecf20Sopenharmony_ci	} else if (offset == 0x04 && length == 4) {
8308c2ecf20Sopenharmony_ci		/* AGENT_RESET */
8318c2ecf20Sopenharmony_ci		rcode = tgt_agent_rw_agent_reset(card, tcode, data, agent);
8328c2ecf20Sopenharmony_ci	} else if (offset == 0x08 && length == 8) {
8338c2ecf20Sopenharmony_ci		/* ORB_POINTER */
8348c2ecf20Sopenharmony_ci		rcode = tgt_agent_rw_orb_pointer(card, tcode, data, agent);
8358c2ecf20Sopenharmony_ci	} else if (offset == 0x10 && length == 4) {
8368c2ecf20Sopenharmony_ci		/* DOORBELL */
8378c2ecf20Sopenharmony_ci		rcode = tgt_agent_rw_doorbell(card, tcode, data, agent);
8388c2ecf20Sopenharmony_ci	} else if (offset == 0x14 && length == 4) {
8398c2ecf20Sopenharmony_ci		/* UNSOLICITED_STATUS_ENABLE */
8408c2ecf20Sopenharmony_ci		rcode = tgt_agent_rw_unsolicited_status_enable(card, tcode,
8418c2ecf20Sopenharmony_ci				data, agent);
8428c2ecf20Sopenharmony_ci	} else {
8438c2ecf20Sopenharmony_ci		rcode = RCODE_ADDRESS_ERROR;
8448c2ecf20Sopenharmony_ci	}
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ciout:
8478c2ecf20Sopenharmony_ci	fw_send_response(card, request, rcode);
8488c2ecf20Sopenharmony_ci}
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_cistatic void sbp_handle_command(struct sbp_target_request *);
8518c2ecf20Sopenharmony_cistatic int sbp_send_status(struct sbp_target_request *);
8528c2ecf20Sopenharmony_cistatic void sbp_free_request(struct sbp_target_request *);
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_cistatic void tgt_agent_process_work(struct work_struct *work)
8558c2ecf20Sopenharmony_ci{
8568c2ecf20Sopenharmony_ci	struct sbp_target_request *req =
8578c2ecf20Sopenharmony_ci		container_of(work, struct sbp_target_request, work);
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci	pr_debug("tgt_orb ptr:0x%llx next_ORB:0x%llx data_descriptor:0x%llx misc:0x%x\n",
8608c2ecf20Sopenharmony_ci			req->orb_pointer,
8618c2ecf20Sopenharmony_ci			sbp2_pointer_to_addr(&req->orb.next_orb),
8628c2ecf20Sopenharmony_ci			sbp2_pointer_to_addr(&req->orb.data_descriptor),
8638c2ecf20Sopenharmony_ci			be32_to_cpu(req->orb.misc));
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci	if (req->orb_pointer >> 32)
8668c2ecf20Sopenharmony_ci		pr_debug("ORB with high bits set\n");
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci	switch (ORB_REQUEST_FORMAT(be32_to_cpu(req->orb.misc))) {
8698c2ecf20Sopenharmony_ci		case 0:/* Format specified by this standard */
8708c2ecf20Sopenharmony_ci			sbp_handle_command(req);
8718c2ecf20Sopenharmony_ci			return;
8728c2ecf20Sopenharmony_ci		case 1: /* Reserved for future standardization */
8738c2ecf20Sopenharmony_ci		case 2: /* Vendor-dependent */
8748c2ecf20Sopenharmony_ci			req->status.status |= cpu_to_be32(
8758c2ecf20Sopenharmony_ci					STATUS_BLOCK_RESP(
8768c2ecf20Sopenharmony_ci						STATUS_RESP_REQUEST_COMPLETE) |
8778c2ecf20Sopenharmony_ci					STATUS_BLOCK_DEAD(0) |
8788c2ecf20Sopenharmony_ci					STATUS_BLOCK_LEN(1) |
8798c2ecf20Sopenharmony_ci					STATUS_BLOCK_SBP_STATUS(
8808c2ecf20Sopenharmony_ci						SBP_STATUS_REQ_TYPE_NOTSUPP));
8818c2ecf20Sopenharmony_ci			sbp_send_status(req);
8828c2ecf20Sopenharmony_ci			return;
8838c2ecf20Sopenharmony_ci		case 3: /* Dummy ORB */
8848c2ecf20Sopenharmony_ci			req->status.status |= cpu_to_be32(
8858c2ecf20Sopenharmony_ci					STATUS_BLOCK_RESP(
8868c2ecf20Sopenharmony_ci						STATUS_RESP_REQUEST_COMPLETE) |
8878c2ecf20Sopenharmony_ci					STATUS_BLOCK_DEAD(0) |
8888c2ecf20Sopenharmony_ci					STATUS_BLOCK_LEN(1) |
8898c2ecf20Sopenharmony_ci					STATUS_BLOCK_SBP_STATUS(
8908c2ecf20Sopenharmony_ci						SBP_STATUS_DUMMY_ORB_COMPLETE));
8918c2ecf20Sopenharmony_ci			sbp_send_status(req);
8928c2ecf20Sopenharmony_ci			return;
8938c2ecf20Sopenharmony_ci		default:
8948c2ecf20Sopenharmony_ci			BUG();
8958c2ecf20Sopenharmony_ci	}
8968c2ecf20Sopenharmony_ci}
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci/* used to double-check we haven't been issued an AGENT_RESET */
8998c2ecf20Sopenharmony_cistatic inline bool tgt_agent_check_active(struct sbp_target_agent *agent)
9008c2ecf20Sopenharmony_ci{
9018c2ecf20Sopenharmony_ci	bool active;
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci	spin_lock_bh(&agent->lock);
9048c2ecf20Sopenharmony_ci	active = (agent->state == AGENT_STATE_ACTIVE);
9058c2ecf20Sopenharmony_ci	spin_unlock_bh(&agent->lock);
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci	return active;
9088c2ecf20Sopenharmony_ci}
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_cistatic struct sbp_target_request *sbp_mgt_get_req(struct sbp_session *sess,
9118c2ecf20Sopenharmony_ci	struct fw_card *card, u64 next_orb)
9128c2ecf20Sopenharmony_ci{
9138c2ecf20Sopenharmony_ci	struct se_session *se_sess = sess->se_sess;
9148c2ecf20Sopenharmony_ci	struct sbp_target_request *req;
9158c2ecf20Sopenharmony_ci	int tag, cpu;
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci	tag = sbitmap_queue_get(&se_sess->sess_tag_pool, &cpu);
9188c2ecf20Sopenharmony_ci	if (tag < 0)
9198c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci	req = &((struct sbp_target_request *)se_sess->sess_cmd_map)[tag];
9228c2ecf20Sopenharmony_ci	memset(req, 0, sizeof(*req));
9238c2ecf20Sopenharmony_ci	req->se_cmd.map_tag = tag;
9248c2ecf20Sopenharmony_ci	req->se_cmd.map_cpu = cpu;
9258c2ecf20Sopenharmony_ci	req->se_cmd.tag = next_orb;
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_ci	return req;
9288c2ecf20Sopenharmony_ci}
9298c2ecf20Sopenharmony_ci
9308c2ecf20Sopenharmony_cistatic void tgt_agent_fetch_work(struct work_struct *work)
9318c2ecf20Sopenharmony_ci{
9328c2ecf20Sopenharmony_ci	struct sbp_target_agent *agent =
9338c2ecf20Sopenharmony_ci		container_of(work, struct sbp_target_agent, work);
9348c2ecf20Sopenharmony_ci	struct sbp_session *sess = agent->login->sess;
9358c2ecf20Sopenharmony_ci	struct sbp_target_request *req;
9368c2ecf20Sopenharmony_ci	int ret;
9378c2ecf20Sopenharmony_ci	bool doorbell = agent->doorbell;
9388c2ecf20Sopenharmony_ci	u64 next_orb = agent->orb_pointer;
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci	while (next_orb && tgt_agent_check_active(agent)) {
9418c2ecf20Sopenharmony_ci		req = sbp_mgt_get_req(sess, sess->card, next_orb);
9428c2ecf20Sopenharmony_ci		if (IS_ERR(req)) {
9438c2ecf20Sopenharmony_ci			spin_lock_bh(&agent->lock);
9448c2ecf20Sopenharmony_ci			agent->state = AGENT_STATE_DEAD;
9458c2ecf20Sopenharmony_ci			spin_unlock_bh(&agent->lock);
9468c2ecf20Sopenharmony_ci			return;
9478c2ecf20Sopenharmony_ci		}
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_ci		req->login = agent->login;
9508c2ecf20Sopenharmony_ci		req->orb_pointer = next_orb;
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ci		req->status.status = cpu_to_be32(STATUS_BLOCK_ORB_OFFSET_HIGH(
9538c2ecf20Sopenharmony_ci					req->orb_pointer >> 32));
9548c2ecf20Sopenharmony_ci		req->status.orb_low = cpu_to_be32(
9558c2ecf20Sopenharmony_ci				req->orb_pointer & 0xfffffffc);
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_ci		/* read in the ORB */
9588c2ecf20Sopenharmony_ci		ret = sbp_run_transaction(sess->card, TCODE_READ_BLOCK_REQUEST,
9598c2ecf20Sopenharmony_ci				sess->node_id, sess->generation, sess->speed,
9608c2ecf20Sopenharmony_ci				req->orb_pointer, &req->orb, sizeof(req->orb));
9618c2ecf20Sopenharmony_ci		if (ret != RCODE_COMPLETE) {
9628c2ecf20Sopenharmony_ci			pr_debug("tgt_orb fetch failed: %x\n", ret);
9638c2ecf20Sopenharmony_ci			req->status.status |= cpu_to_be32(
9648c2ecf20Sopenharmony_ci					STATUS_BLOCK_SRC(
9658c2ecf20Sopenharmony_ci						STATUS_SRC_ORB_FINISHED) |
9668c2ecf20Sopenharmony_ci					STATUS_BLOCK_RESP(
9678c2ecf20Sopenharmony_ci						STATUS_RESP_TRANSPORT_FAILURE) |
9688c2ecf20Sopenharmony_ci					STATUS_BLOCK_DEAD(1) |
9698c2ecf20Sopenharmony_ci					STATUS_BLOCK_LEN(1) |
9708c2ecf20Sopenharmony_ci					STATUS_BLOCK_SBP_STATUS(
9718c2ecf20Sopenharmony_ci						SBP_STATUS_UNSPECIFIED_ERROR));
9728c2ecf20Sopenharmony_ci			spin_lock_bh(&agent->lock);
9738c2ecf20Sopenharmony_ci			agent->state = AGENT_STATE_DEAD;
9748c2ecf20Sopenharmony_ci			spin_unlock_bh(&agent->lock);
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci			sbp_send_status(req);
9778c2ecf20Sopenharmony_ci			return;
9788c2ecf20Sopenharmony_ci		}
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci		/* check the next_ORB field */
9818c2ecf20Sopenharmony_ci		if (be32_to_cpu(req->orb.next_orb.high) & 0x80000000) {
9828c2ecf20Sopenharmony_ci			next_orb = 0;
9838c2ecf20Sopenharmony_ci			req->status.status |= cpu_to_be32(STATUS_BLOCK_SRC(
9848c2ecf20Sopenharmony_ci						STATUS_SRC_ORB_FINISHED));
9858c2ecf20Sopenharmony_ci		} else {
9868c2ecf20Sopenharmony_ci			next_orb = sbp2_pointer_to_addr(&req->orb.next_orb);
9878c2ecf20Sopenharmony_ci			req->status.status |= cpu_to_be32(STATUS_BLOCK_SRC(
9888c2ecf20Sopenharmony_ci						STATUS_SRC_ORB_CONTINUING));
9898c2ecf20Sopenharmony_ci		}
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci		if (tgt_agent_check_active(agent) && !doorbell) {
9928c2ecf20Sopenharmony_ci			INIT_WORK(&req->work, tgt_agent_process_work);
9938c2ecf20Sopenharmony_ci			queue_work(system_unbound_wq, &req->work);
9948c2ecf20Sopenharmony_ci		} else {
9958c2ecf20Sopenharmony_ci			/* don't process this request, just check next_ORB */
9968c2ecf20Sopenharmony_ci			sbp_free_request(req);
9978c2ecf20Sopenharmony_ci		}
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci		spin_lock_bh(&agent->lock);
10008c2ecf20Sopenharmony_ci		doorbell = agent->doorbell = false;
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_ci		/* check if we should carry on processing */
10038c2ecf20Sopenharmony_ci		if (next_orb)
10048c2ecf20Sopenharmony_ci			agent->orb_pointer = next_orb;
10058c2ecf20Sopenharmony_ci		else
10068c2ecf20Sopenharmony_ci			agent->state = AGENT_STATE_SUSPENDED;
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_ci		spin_unlock_bh(&agent->lock);
10098c2ecf20Sopenharmony_ci	};
10108c2ecf20Sopenharmony_ci}
10118c2ecf20Sopenharmony_ci
10128c2ecf20Sopenharmony_cistatic struct sbp_target_agent *sbp_target_agent_register(
10138c2ecf20Sopenharmony_ci		struct sbp_login_descriptor *login)
10148c2ecf20Sopenharmony_ci{
10158c2ecf20Sopenharmony_ci	struct sbp_target_agent *agent;
10168c2ecf20Sopenharmony_ci	int ret;
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci	agent = kmalloc(sizeof(*agent), GFP_KERNEL);
10198c2ecf20Sopenharmony_ci	if (!agent)
10208c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_ci	spin_lock_init(&agent->lock);
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci	agent->handler.length = 0x20;
10258c2ecf20Sopenharmony_ci	agent->handler.address_callback = tgt_agent_rw;
10268c2ecf20Sopenharmony_ci	agent->handler.callback_data = agent;
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_ci	agent->login = login;
10298c2ecf20Sopenharmony_ci	agent->state = AGENT_STATE_RESET;
10308c2ecf20Sopenharmony_ci	INIT_WORK(&agent->work, tgt_agent_fetch_work);
10318c2ecf20Sopenharmony_ci	agent->orb_pointer = 0;
10328c2ecf20Sopenharmony_ci	agent->doorbell = false;
10338c2ecf20Sopenharmony_ci
10348c2ecf20Sopenharmony_ci	ret = fw_core_add_address_handler(&agent->handler,
10358c2ecf20Sopenharmony_ci			&sbp_register_region);
10368c2ecf20Sopenharmony_ci	if (ret < 0) {
10378c2ecf20Sopenharmony_ci		kfree(agent);
10388c2ecf20Sopenharmony_ci		return ERR_PTR(ret);
10398c2ecf20Sopenharmony_ci	}
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_ci	return agent;
10428c2ecf20Sopenharmony_ci}
10438c2ecf20Sopenharmony_ci
10448c2ecf20Sopenharmony_cistatic void sbp_target_agent_unregister(struct sbp_target_agent *agent)
10458c2ecf20Sopenharmony_ci{
10468c2ecf20Sopenharmony_ci	fw_core_remove_address_handler(&agent->handler);
10478c2ecf20Sopenharmony_ci	cancel_work_sync(&agent->work);
10488c2ecf20Sopenharmony_ci	kfree(agent);
10498c2ecf20Sopenharmony_ci}
10508c2ecf20Sopenharmony_ci
10518c2ecf20Sopenharmony_ci/*
10528c2ecf20Sopenharmony_ci * Simple wrapper around fw_run_transaction that retries the transaction several
10538c2ecf20Sopenharmony_ci * times in case of failure, with an exponential backoff.
10548c2ecf20Sopenharmony_ci */
10558c2ecf20Sopenharmony_cistatic int sbp_run_transaction(struct fw_card *card, int tcode, int destination_id,
10568c2ecf20Sopenharmony_ci		int generation, int speed, unsigned long long offset,
10578c2ecf20Sopenharmony_ci		void *payload, size_t length)
10588c2ecf20Sopenharmony_ci{
10598c2ecf20Sopenharmony_ci	int attempt, ret, delay;
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci	for (attempt = 1; attempt <= 5; attempt++) {
10628c2ecf20Sopenharmony_ci		ret = fw_run_transaction(card, tcode, destination_id,
10638c2ecf20Sopenharmony_ci				generation, speed, offset, payload, length);
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_ci		switch (ret) {
10668c2ecf20Sopenharmony_ci		case RCODE_COMPLETE:
10678c2ecf20Sopenharmony_ci		case RCODE_TYPE_ERROR:
10688c2ecf20Sopenharmony_ci		case RCODE_ADDRESS_ERROR:
10698c2ecf20Sopenharmony_ci		case RCODE_GENERATION:
10708c2ecf20Sopenharmony_ci			return ret;
10718c2ecf20Sopenharmony_ci
10728c2ecf20Sopenharmony_ci		default:
10738c2ecf20Sopenharmony_ci			delay = 5 * attempt * attempt;
10748c2ecf20Sopenharmony_ci			usleep_range(delay, delay * 2);
10758c2ecf20Sopenharmony_ci		}
10768c2ecf20Sopenharmony_ci	}
10778c2ecf20Sopenharmony_ci
10788c2ecf20Sopenharmony_ci	return ret;
10798c2ecf20Sopenharmony_ci}
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_ci/*
10828c2ecf20Sopenharmony_ci * Wrapper around sbp_run_transaction that gets the card, destination,
10838c2ecf20Sopenharmony_ci * generation and speed out of the request's session.
10848c2ecf20Sopenharmony_ci */
10858c2ecf20Sopenharmony_cistatic int sbp_run_request_transaction(struct sbp_target_request *req,
10868c2ecf20Sopenharmony_ci		int tcode, unsigned long long offset, void *payload,
10878c2ecf20Sopenharmony_ci		size_t length)
10888c2ecf20Sopenharmony_ci{
10898c2ecf20Sopenharmony_ci	struct sbp_login_descriptor *login = req->login;
10908c2ecf20Sopenharmony_ci	struct sbp_session *sess = login->sess;
10918c2ecf20Sopenharmony_ci	struct fw_card *card;
10928c2ecf20Sopenharmony_ci	int node_id, generation, speed, ret;
10938c2ecf20Sopenharmony_ci
10948c2ecf20Sopenharmony_ci	spin_lock_bh(&sess->lock);
10958c2ecf20Sopenharmony_ci	card = fw_card_get(sess->card);
10968c2ecf20Sopenharmony_ci	node_id = sess->node_id;
10978c2ecf20Sopenharmony_ci	generation = sess->generation;
10988c2ecf20Sopenharmony_ci	speed = sess->speed;
10998c2ecf20Sopenharmony_ci	spin_unlock_bh(&sess->lock);
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_ci	ret = sbp_run_transaction(card, tcode, node_id, generation, speed,
11028c2ecf20Sopenharmony_ci			offset, payload, length);
11038c2ecf20Sopenharmony_ci
11048c2ecf20Sopenharmony_ci	fw_card_put(card);
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_ci	return ret;
11078c2ecf20Sopenharmony_ci}
11088c2ecf20Sopenharmony_ci
11098c2ecf20Sopenharmony_cistatic int sbp_fetch_command(struct sbp_target_request *req)
11108c2ecf20Sopenharmony_ci{
11118c2ecf20Sopenharmony_ci	int ret, cmd_len, copy_len;
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_ci	cmd_len = scsi_command_size(req->orb.command_block);
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_ci	req->cmd_buf = kmalloc(cmd_len, GFP_KERNEL);
11168c2ecf20Sopenharmony_ci	if (!req->cmd_buf)
11178c2ecf20Sopenharmony_ci		return -ENOMEM;
11188c2ecf20Sopenharmony_ci
11198c2ecf20Sopenharmony_ci	memcpy(req->cmd_buf, req->orb.command_block,
11208c2ecf20Sopenharmony_ci		min_t(int, cmd_len, sizeof(req->orb.command_block)));
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci	if (cmd_len > sizeof(req->orb.command_block)) {
11238c2ecf20Sopenharmony_ci		pr_debug("sbp_fetch_command: filling in long command\n");
11248c2ecf20Sopenharmony_ci		copy_len = cmd_len - sizeof(req->orb.command_block);
11258c2ecf20Sopenharmony_ci
11268c2ecf20Sopenharmony_ci		ret = sbp_run_request_transaction(req,
11278c2ecf20Sopenharmony_ci				TCODE_READ_BLOCK_REQUEST,
11288c2ecf20Sopenharmony_ci				req->orb_pointer + sizeof(req->orb),
11298c2ecf20Sopenharmony_ci				req->cmd_buf + sizeof(req->orb.command_block),
11308c2ecf20Sopenharmony_ci				copy_len);
11318c2ecf20Sopenharmony_ci		if (ret != RCODE_COMPLETE)
11328c2ecf20Sopenharmony_ci			return -EIO;
11338c2ecf20Sopenharmony_ci	}
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_ci	return 0;
11368c2ecf20Sopenharmony_ci}
11378c2ecf20Sopenharmony_ci
11388c2ecf20Sopenharmony_cistatic int sbp_fetch_page_table(struct sbp_target_request *req)
11398c2ecf20Sopenharmony_ci{
11408c2ecf20Sopenharmony_ci	int pg_tbl_sz, ret;
11418c2ecf20Sopenharmony_ci	struct sbp_page_table_entry *pg_tbl;
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_ci	if (!CMDBLK_ORB_PG_TBL_PRESENT(be32_to_cpu(req->orb.misc)))
11448c2ecf20Sopenharmony_ci		return 0;
11458c2ecf20Sopenharmony_ci
11468c2ecf20Sopenharmony_ci	pg_tbl_sz = CMDBLK_ORB_DATA_SIZE(be32_to_cpu(req->orb.misc)) *
11478c2ecf20Sopenharmony_ci		sizeof(struct sbp_page_table_entry);
11488c2ecf20Sopenharmony_ci
11498c2ecf20Sopenharmony_ci	pg_tbl = kmalloc(pg_tbl_sz, GFP_KERNEL);
11508c2ecf20Sopenharmony_ci	if (!pg_tbl)
11518c2ecf20Sopenharmony_ci		return -ENOMEM;
11528c2ecf20Sopenharmony_ci
11538c2ecf20Sopenharmony_ci	ret = sbp_run_request_transaction(req, TCODE_READ_BLOCK_REQUEST,
11548c2ecf20Sopenharmony_ci			sbp2_pointer_to_addr(&req->orb.data_descriptor),
11558c2ecf20Sopenharmony_ci			pg_tbl, pg_tbl_sz);
11568c2ecf20Sopenharmony_ci	if (ret != RCODE_COMPLETE) {
11578c2ecf20Sopenharmony_ci		kfree(pg_tbl);
11588c2ecf20Sopenharmony_ci		return -EIO;
11598c2ecf20Sopenharmony_ci	}
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci	req->pg_tbl = pg_tbl;
11628c2ecf20Sopenharmony_ci	return 0;
11638c2ecf20Sopenharmony_ci}
11648c2ecf20Sopenharmony_ci
11658c2ecf20Sopenharmony_cistatic void sbp_calc_data_length_direction(struct sbp_target_request *req,
11668c2ecf20Sopenharmony_ci	u32 *data_len, enum dma_data_direction *data_dir)
11678c2ecf20Sopenharmony_ci{
11688c2ecf20Sopenharmony_ci	int data_size, direction, idx;
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_ci	data_size = CMDBLK_ORB_DATA_SIZE(be32_to_cpu(req->orb.misc));
11718c2ecf20Sopenharmony_ci	direction = CMDBLK_ORB_DIRECTION(be32_to_cpu(req->orb.misc));
11728c2ecf20Sopenharmony_ci
11738c2ecf20Sopenharmony_ci	if (!data_size) {
11748c2ecf20Sopenharmony_ci		*data_len = 0;
11758c2ecf20Sopenharmony_ci		*data_dir = DMA_NONE;
11768c2ecf20Sopenharmony_ci		return;
11778c2ecf20Sopenharmony_ci	}
11788c2ecf20Sopenharmony_ci
11798c2ecf20Sopenharmony_ci	*data_dir = direction ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_ci	if (req->pg_tbl) {
11828c2ecf20Sopenharmony_ci		*data_len = 0;
11838c2ecf20Sopenharmony_ci		for (idx = 0; idx < data_size; idx++) {
11848c2ecf20Sopenharmony_ci			*data_len += be16_to_cpu(
11858c2ecf20Sopenharmony_ci					req->pg_tbl[idx].segment_length);
11868c2ecf20Sopenharmony_ci		}
11878c2ecf20Sopenharmony_ci	} else {
11888c2ecf20Sopenharmony_ci		*data_len = data_size;
11898c2ecf20Sopenharmony_ci	}
11908c2ecf20Sopenharmony_ci}
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_cistatic void sbp_handle_command(struct sbp_target_request *req)
11938c2ecf20Sopenharmony_ci{
11948c2ecf20Sopenharmony_ci	struct sbp_login_descriptor *login = req->login;
11958c2ecf20Sopenharmony_ci	struct sbp_session *sess = login->sess;
11968c2ecf20Sopenharmony_ci	int ret, unpacked_lun;
11978c2ecf20Sopenharmony_ci	u32 data_length;
11988c2ecf20Sopenharmony_ci	enum dma_data_direction data_dir;
11998c2ecf20Sopenharmony_ci
12008c2ecf20Sopenharmony_ci	ret = sbp_fetch_command(req);
12018c2ecf20Sopenharmony_ci	if (ret) {
12028c2ecf20Sopenharmony_ci		pr_debug("sbp_handle_command: fetch command failed: %d\n", ret);
12038c2ecf20Sopenharmony_ci		goto err;
12048c2ecf20Sopenharmony_ci	}
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_ci	ret = sbp_fetch_page_table(req);
12078c2ecf20Sopenharmony_ci	if (ret) {
12088c2ecf20Sopenharmony_ci		pr_debug("sbp_handle_command: fetch page table failed: %d\n",
12098c2ecf20Sopenharmony_ci			ret);
12108c2ecf20Sopenharmony_ci		goto err;
12118c2ecf20Sopenharmony_ci	}
12128c2ecf20Sopenharmony_ci
12138c2ecf20Sopenharmony_ci	unpacked_lun = req->login->login_lun;
12148c2ecf20Sopenharmony_ci	sbp_calc_data_length_direction(req, &data_length, &data_dir);
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci	pr_debug("sbp_handle_command ORB:0x%llx unpacked_lun:%d data_len:%d data_dir:%d\n",
12178c2ecf20Sopenharmony_ci			req->orb_pointer, unpacked_lun, data_length, data_dir);
12188c2ecf20Sopenharmony_ci
12198c2ecf20Sopenharmony_ci	/* only used for printk until we do TMRs */
12208c2ecf20Sopenharmony_ci	req->se_cmd.tag = req->orb_pointer;
12218c2ecf20Sopenharmony_ci	if (target_submit_cmd(&req->se_cmd, sess->se_sess, req->cmd_buf,
12228c2ecf20Sopenharmony_ci			      req->sense_buf, unpacked_lun, data_length,
12238c2ecf20Sopenharmony_ci			      TCM_SIMPLE_TAG, data_dir, TARGET_SCF_ACK_KREF))
12248c2ecf20Sopenharmony_ci		goto err;
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci	return;
12278c2ecf20Sopenharmony_ci
12288c2ecf20Sopenharmony_cierr:
12298c2ecf20Sopenharmony_ci	req->status.status |= cpu_to_be32(
12308c2ecf20Sopenharmony_ci		STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
12318c2ecf20Sopenharmony_ci		STATUS_BLOCK_DEAD(0) |
12328c2ecf20Sopenharmony_ci		STATUS_BLOCK_LEN(1) |
12338c2ecf20Sopenharmony_ci		STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
12348c2ecf20Sopenharmony_ci	sbp_send_status(req);
12358c2ecf20Sopenharmony_ci}
12368c2ecf20Sopenharmony_ci
12378c2ecf20Sopenharmony_ci/*
12388c2ecf20Sopenharmony_ci * DMA_TO_DEVICE = read from initiator (SCSI WRITE)
12398c2ecf20Sopenharmony_ci * DMA_FROM_DEVICE = write to initiator (SCSI READ)
12408c2ecf20Sopenharmony_ci */
12418c2ecf20Sopenharmony_cistatic int sbp_rw_data(struct sbp_target_request *req)
12428c2ecf20Sopenharmony_ci{
12438c2ecf20Sopenharmony_ci	struct sbp_session *sess = req->login->sess;
12448c2ecf20Sopenharmony_ci	int tcode, sg_miter_flags, max_payload, pg_size, speed, node_id,
12458c2ecf20Sopenharmony_ci		generation, num_pte, length, tfr_length,
12468c2ecf20Sopenharmony_ci		rcode = RCODE_COMPLETE;
12478c2ecf20Sopenharmony_ci	struct sbp_page_table_entry *pte;
12488c2ecf20Sopenharmony_ci	unsigned long long offset;
12498c2ecf20Sopenharmony_ci	struct fw_card *card;
12508c2ecf20Sopenharmony_ci	struct sg_mapping_iter iter;
12518c2ecf20Sopenharmony_ci
12528c2ecf20Sopenharmony_ci	if (req->se_cmd.data_direction == DMA_FROM_DEVICE) {
12538c2ecf20Sopenharmony_ci		tcode = TCODE_WRITE_BLOCK_REQUEST;
12548c2ecf20Sopenharmony_ci		sg_miter_flags = SG_MITER_FROM_SG;
12558c2ecf20Sopenharmony_ci	} else {
12568c2ecf20Sopenharmony_ci		tcode = TCODE_READ_BLOCK_REQUEST;
12578c2ecf20Sopenharmony_ci		sg_miter_flags = SG_MITER_TO_SG;
12588c2ecf20Sopenharmony_ci	}
12598c2ecf20Sopenharmony_ci
12608c2ecf20Sopenharmony_ci	max_payload = 4 << CMDBLK_ORB_MAX_PAYLOAD(be32_to_cpu(req->orb.misc));
12618c2ecf20Sopenharmony_ci	speed = CMDBLK_ORB_SPEED(be32_to_cpu(req->orb.misc));
12628c2ecf20Sopenharmony_ci
12638c2ecf20Sopenharmony_ci	pg_size = CMDBLK_ORB_PG_SIZE(be32_to_cpu(req->orb.misc));
12648c2ecf20Sopenharmony_ci	if (pg_size) {
12658c2ecf20Sopenharmony_ci		pr_err("sbp_run_transaction: page size ignored\n");
12668c2ecf20Sopenharmony_ci		pg_size = 0x100 << pg_size;
12678c2ecf20Sopenharmony_ci	}
12688c2ecf20Sopenharmony_ci
12698c2ecf20Sopenharmony_ci	spin_lock_bh(&sess->lock);
12708c2ecf20Sopenharmony_ci	card = fw_card_get(sess->card);
12718c2ecf20Sopenharmony_ci	node_id = sess->node_id;
12728c2ecf20Sopenharmony_ci	generation = sess->generation;
12738c2ecf20Sopenharmony_ci	spin_unlock_bh(&sess->lock);
12748c2ecf20Sopenharmony_ci
12758c2ecf20Sopenharmony_ci	if (req->pg_tbl) {
12768c2ecf20Sopenharmony_ci		pte = req->pg_tbl;
12778c2ecf20Sopenharmony_ci		num_pte = CMDBLK_ORB_DATA_SIZE(be32_to_cpu(req->orb.misc));
12788c2ecf20Sopenharmony_ci
12798c2ecf20Sopenharmony_ci		offset = 0;
12808c2ecf20Sopenharmony_ci		length = 0;
12818c2ecf20Sopenharmony_ci	} else {
12828c2ecf20Sopenharmony_ci		pte = NULL;
12838c2ecf20Sopenharmony_ci		num_pte = 0;
12848c2ecf20Sopenharmony_ci
12858c2ecf20Sopenharmony_ci		offset = sbp2_pointer_to_addr(&req->orb.data_descriptor);
12868c2ecf20Sopenharmony_ci		length = req->se_cmd.data_length;
12878c2ecf20Sopenharmony_ci	}
12888c2ecf20Sopenharmony_ci
12898c2ecf20Sopenharmony_ci	sg_miter_start(&iter, req->se_cmd.t_data_sg, req->se_cmd.t_data_nents,
12908c2ecf20Sopenharmony_ci		sg_miter_flags);
12918c2ecf20Sopenharmony_ci
12928c2ecf20Sopenharmony_ci	while (length || num_pte) {
12938c2ecf20Sopenharmony_ci		if (!length) {
12948c2ecf20Sopenharmony_ci			offset = (u64)be16_to_cpu(pte->segment_base_hi) << 32 |
12958c2ecf20Sopenharmony_ci				be32_to_cpu(pte->segment_base_lo);
12968c2ecf20Sopenharmony_ci			length = be16_to_cpu(pte->segment_length);
12978c2ecf20Sopenharmony_ci
12988c2ecf20Sopenharmony_ci			pte++;
12998c2ecf20Sopenharmony_ci			num_pte--;
13008c2ecf20Sopenharmony_ci		}
13018c2ecf20Sopenharmony_ci
13028c2ecf20Sopenharmony_ci		sg_miter_next(&iter);
13038c2ecf20Sopenharmony_ci
13048c2ecf20Sopenharmony_ci		tfr_length = min3(length, max_payload, (int)iter.length);
13058c2ecf20Sopenharmony_ci
13068c2ecf20Sopenharmony_ci		/* FIXME: take page_size into account */
13078c2ecf20Sopenharmony_ci
13088c2ecf20Sopenharmony_ci		rcode = sbp_run_transaction(card, tcode, node_id,
13098c2ecf20Sopenharmony_ci				generation, speed,
13108c2ecf20Sopenharmony_ci				offset, iter.addr, tfr_length);
13118c2ecf20Sopenharmony_ci
13128c2ecf20Sopenharmony_ci		if (rcode != RCODE_COMPLETE)
13138c2ecf20Sopenharmony_ci			break;
13148c2ecf20Sopenharmony_ci
13158c2ecf20Sopenharmony_ci		length -= tfr_length;
13168c2ecf20Sopenharmony_ci		offset += tfr_length;
13178c2ecf20Sopenharmony_ci		iter.consumed = tfr_length;
13188c2ecf20Sopenharmony_ci	}
13198c2ecf20Sopenharmony_ci
13208c2ecf20Sopenharmony_ci	sg_miter_stop(&iter);
13218c2ecf20Sopenharmony_ci	fw_card_put(card);
13228c2ecf20Sopenharmony_ci
13238c2ecf20Sopenharmony_ci	if (rcode == RCODE_COMPLETE) {
13248c2ecf20Sopenharmony_ci		WARN_ON(length != 0);
13258c2ecf20Sopenharmony_ci		return 0;
13268c2ecf20Sopenharmony_ci	} else {
13278c2ecf20Sopenharmony_ci		return -EIO;
13288c2ecf20Sopenharmony_ci	}
13298c2ecf20Sopenharmony_ci}
13308c2ecf20Sopenharmony_ci
13318c2ecf20Sopenharmony_cistatic int sbp_send_status(struct sbp_target_request *req)
13328c2ecf20Sopenharmony_ci{
13338c2ecf20Sopenharmony_ci	int rc, ret = 0, length;
13348c2ecf20Sopenharmony_ci	struct sbp_login_descriptor *login = req->login;
13358c2ecf20Sopenharmony_ci
13368c2ecf20Sopenharmony_ci	length = (((be32_to_cpu(req->status.status) >> 24) & 0x07) + 1) * 4;
13378c2ecf20Sopenharmony_ci
13388c2ecf20Sopenharmony_ci	rc = sbp_run_request_transaction(req, TCODE_WRITE_BLOCK_REQUEST,
13398c2ecf20Sopenharmony_ci			login->status_fifo_addr, &req->status, length);
13408c2ecf20Sopenharmony_ci	if (rc != RCODE_COMPLETE) {
13418c2ecf20Sopenharmony_ci		pr_debug("sbp_send_status: write failed: 0x%x\n", rc);
13428c2ecf20Sopenharmony_ci		ret = -EIO;
13438c2ecf20Sopenharmony_ci		goto put_ref;
13448c2ecf20Sopenharmony_ci	}
13458c2ecf20Sopenharmony_ci
13468c2ecf20Sopenharmony_ci	pr_debug("sbp_send_status: status write complete for ORB: 0x%llx\n",
13478c2ecf20Sopenharmony_ci			req->orb_pointer);
13488c2ecf20Sopenharmony_ci	/*
13498c2ecf20Sopenharmony_ci	 * Drop the extra ACK_KREF reference taken by target_submit_cmd()
13508c2ecf20Sopenharmony_ci	 * ahead of sbp_check_stop_free() -> transport_generic_free_cmd()
13518c2ecf20Sopenharmony_ci	 * final se_cmd->cmd_kref put.
13528c2ecf20Sopenharmony_ci	 */
13538c2ecf20Sopenharmony_ciput_ref:
13548c2ecf20Sopenharmony_ci	target_put_sess_cmd(&req->se_cmd);
13558c2ecf20Sopenharmony_ci	return ret;
13568c2ecf20Sopenharmony_ci}
13578c2ecf20Sopenharmony_ci
13588c2ecf20Sopenharmony_cistatic void sbp_sense_mangle(struct sbp_target_request *req)
13598c2ecf20Sopenharmony_ci{
13608c2ecf20Sopenharmony_ci	struct se_cmd *se_cmd = &req->se_cmd;
13618c2ecf20Sopenharmony_ci	u8 *sense = req->sense_buf;
13628c2ecf20Sopenharmony_ci	u8 *status = req->status.data;
13638c2ecf20Sopenharmony_ci
13648c2ecf20Sopenharmony_ci	WARN_ON(se_cmd->scsi_sense_length < 18);
13658c2ecf20Sopenharmony_ci
13668c2ecf20Sopenharmony_ci	switch (sense[0] & 0x7f) { 		/* sfmt */
13678c2ecf20Sopenharmony_ci	case 0x70: /* current, fixed */
13688c2ecf20Sopenharmony_ci		status[0] = 0 << 6;
13698c2ecf20Sopenharmony_ci		break;
13708c2ecf20Sopenharmony_ci	case 0x71: /* deferred, fixed */
13718c2ecf20Sopenharmony_ci		status[0] = 1 << 6;
13728c2ecf20Sopenharmony_ci		break;
13738c2ecf20Sopenharmony_ci	case 0x72: /* current, descriptor */
13748c2ecf20Sopenharmony_ci	case 0x73: /* deferred, descriptor */
13758c2ecf20Sopenharmony_ci	default:
13768c2ecf20Sopenharmony_ci		/*
13778c2ecf20Sopenharmony_ci		 * TODO: SBP-3 specifies what we should do with descriptor
13788c2ecf20Sopenharmony_ci		 * format sense data
13798c2ecf20Sopenharmony_ci		 */
13808c2ecf20Sopenharmony_ci		pr_err("sbp_send_sense: unknown sense format: 0x%x\n",
13818c2ecf20Sopenharmony_ci			sense[0]);
13828c2ecf20Sopenharmony_ci		req->status.status |= cpu_to_be32(
13838c2ecf20Sopenharmony_ci			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
13848c2ecf20Sopenharmony_ci			STATUS_BLOCK_DEAD(0) |
13858c2ecf20Sopenharmony_ci			STATUS_BLOCK_LEN(1) |
13868c2ecf20Sopenharmony_ci			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQUEST_ABORTED));
13878c2ecf20Sopenharmony_ci		return;
13888c2ecf20Sopenharmony_ci	}
13898c2ecf20Sopenharmony_ci
13908c2ecf20Sopenharmony_ci	status[0] |= se_cmd->scsi_status & 0x3f;/* status */
13918c2ecf20Sopenharmony_ci	status[1] =
13928c2ecf20Sopenharmony_ci		(sense[0] & 0x80) |		/* valid */
13938c2ecf20Sopenharmony_ci		((sense[2] & 0xe0) >> 1) |	/* mark, eom, ili */
13948c2ecf20Sopenharmony_ci		(sense[2] & 0x0f);		/* sense_key */
13958c2ecf20Sopenharmony_ci	status[2] = se_cmd->scsi_asc;		/* sense_code */
13968c2ecf20Sopenharmony_ci	status[3] = se_cmd->scsi_ascq;		/* sense_qualifier */
13978c2ecf20Sopenharmony_ci
13988c2ecf20Sopenharmony_ci	/* information */
13998c2ecf20Sopenharmony_ci	status[4] = sense[3];
14008c2ecf20Sopenharmony_ci	status[5] = sense[4];
14018c2ecf20Sopenharmony_ci	status[6] = sense[5];
14028c2ecf20Sopenharmony_ci	status[7] = sense[6];
14038c2ecf20Sopenharmony_ci
14048c2ecf20Sopenharmony_ci	/* CDB-dependent */
14058c2ecf20Sopenharmony_ci	status[8] = sense[8];
14068c2ecf20Sopenharmony_ci	status[9] = sense[9];
14078c2ecf20Sopenharmony_ci	status[10] = sense[10];
14088c2ecf20Sopenharmony_ci	status[11] = sense[11];
14098c2ecf20Sopenharmony_ci
14108c2ecf20Sopenharmony_ci	/* fru */
14118c2ecf20Sopenharmony_ci	status[12] = sense[14];
14128c2ecf20Sopenharmony_ci
14138c2ecf20Sopenharmony_ci	/* sense_key-dependent */
14148c2ecf20Sopenharmony_ci	status[13] = sense[15];
14158c2ecf20Sopenharmony_ci	status[14] = sense[16];
14168c2ecf20Sopenharmony_ci	status[15] = sense[17];
14178c2ecf20Sopenharmony_ci
14188c2ecf20Sopenharmony_ci	req->status.status |= cpu_to_be32(
14198c2ecf20Sopenharmony_ci		STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
14208c2ecf20Sopenharmony_ci		STATUS_BLOCK_DEAD(0) |
14218c2ecf20Sopenharmony_ci		STATUS_BLOCK_LEN(5) |
14228c2ecf20Sopenharmony_ci		STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
14238c2ecf20Sopenharmony_ci}
14248c2ecf20Sopenharmony_ci
14258c2ecf20Sopenharmony_cistatic int sbp_send_sense(struct sbp_target_request *req)
14268c2ecf20Sopenharmony_ci{
14278c2ecf20Sopenharmony_ci	struct se_cmd *se_cmd = &req->se_cmd;
14288c2ecf20Sopenharmony_ci
14298c2ecf20Sopenharmony_ci	if (se_cmd->scsi_sense_length) {
14308c2ecf20Sopenharmony_ci		sbp_sense_mangle(req);
14318c2ecf20Sopenharmony_ci	} else {
14328c2ecf20Sopenharmony_ci		req->status.status |= cpu_to_be32(
14338c2ecf20Sopenharmony_ci			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
14348c2ecf20Sopenharmony_ci			STATUS_BLOCK_DEAD(0) |
14358c2ecf20Sopenharmony_ci			STATUS_BLOCK_LEN(1) |
14368c2ecf20Sopenharmony_ci			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_OK));
14378c2ecf20Sopenharmony_ci	}
14388c2ecf20Sopenharmony_ci
14398c2ecf20Sopenharmony_ci	return sbp_send_status(req);
14408c2ecf20Sopenharmony_ci}
14418c2ecf20Sopenharmony_ci
14428c2ecf20Sopenharmony_cistatic void sbp_free_request(struct sbp_target_request *req)
14438c2ecf20Sopenharmony_ci{
14448c2ecf20Sopenharmony_ci	struct se_cmd *se_cmd = &req->se_cmd;
14458c2ecf20Sopenharmony_ci	struct se_session *se_sess = se_cmd->se_sess;
14468c2ecf20Sopenharmony_ci
14478c2ecf20Sopenharmony_ci	kfree(req->pg_tbl);
14488c2ecf20Sopenharmony_ci	kfree(req->cmd_buf);
14498c2ecf20Sopenharmony_ci
14508c2ecf20Sopenharmony_ci	target_free_tag(se_sess, se_cmd);
14518c2ecf20Sopenharmony_ci}
14528c2ecf20Sopenharmony_ci
14538c2ecf20Sopenharmony_cistatic void sbp_mgt_agent_process(struct work_struct *work)
14548c2ecf20Sopenharmony_ci{
14558c2ecf20Sopenharmony_ci	struct sbp_management_agent *agent =
14568c2ecf20Sopenharmony_ci		container_of(work, struct sbp_management_agent, work);
14578c2ecf20Sopenharmony_ci	struct sbp_management_request *req = agent->request;
14588c2ecf20Sopenharmony_ci	int ret;
14598c2ecf20Sopenharmony_ci	int status_data_len = 0;
14608c2ecf20Sopenharmony_ci
14618c2ecf20Sopenharmony_ci	/* fetch the ORB from the initiator */
14628c2ecf20Sopenharmony_ci	ret = sbp_run_transaction(req->card, TCODE_READ_BLOCK_REQUEST,
14638c2ecf20Sopenharmony_ci		req->node_addr, req->generation, req->speed,
14648c2ecf20Sopenharmony_ci		agent->orb_offset, &req->orb, sizeof(req->orb));
14658c2ecf20Sopenharmony_ci	if (ret != RCODE_COMPLETE) {
14668c2ecf20Sopenharmony_ci		pr_debug("mgt_orb fetch failed: %x\n", ret);
14678c2ecf20Sopenharmony_ci		goto out;
14688c2ecf20Sopenharmony_ci	}
14698c2ecf20Sopenharmony_ci
14708c2ecf20Sopenharmony_ci	pr_debug("mgt_orb ptr1:0x%llx ptr2:0x%llx misc:0x%x len:0x%x status_fifo:0x%llx\n",
14718c2ecf20Sopenharmony_ci		sbp2_pointer_to_addr(&req->orb.ptr1),
14728c2ecf20Sopenharmony_ci		sbp2_pointer_to_addr(&req->orb.ptr2),
14738c2ecf20Sopenharmony_ci		be32_to_cpu(req->orb.misc), be32_to_cpu(req->orb.length),
14748c2ecf20Sopenharmony_ci		sbp2_pointer_to_addr(&req->orb.status_fifo));
14758c2ecf20Sopenharmony_ci
14768c2ecf20Sopenharmony_ci	if (!ORB_NOTIFY(be32_to_cpu(req->orb.misc)) ||
14778c2ecf20Sopenharmony_ci		ORB_REQUEST_FORMAT(be32_to_cpu(req->orb.misc)) != 0) {
14788c2ecf20Sopenharmony_ci		pr_err("mgt_orb bad request\n");
14798c2ecf20Sopenharmony_ci		goto out;
14808c2ecf20Sopenharmony_ci	}
14818c2ecf20Sopenharmony_ci
14828c2ecf20Sopenharmony_ci	switch (MANAGEMENT_ORB_FUNCTION(be32_to_cpu(req->orb.misc))) {
14838c2ecf20Sopenharmony_ci	case MANAGEMENT_ORB_FUNCTION_LOGIN:
14848c2ecf20Sopenharmony_ci		sbp_management_request_login(agent, req, &status_data_len);
14858c2ecf20Sopenharmony_ci		break;
14868c2ecf20Sopenharmony_ci
14878c2ecf20Sopenharmony_ci	case MANAGEMENT_ORB_FUNCTION_QUERY_LOGINS:
14888c2ecf20Sopenharmony_ci		sbp_management_request_query_logins(agent, req,
14898c2ecf20Sopenharmony_ci				&status_data_len);
14908c2ecf20Sopenharmony_ci		break;
14918c2ecf20Sopenharmony_ci
14928c2ecf20Sopenharmony_ci	case MANAGEMENT_ORB_FUNCTION_RECONNECT:
14938c2ecf20Sopenharmony_ci		sbp_management_request_reconnect(agent, req, &status_data_len);
14948c2ecf20Sopenharmony_ci		break;
14958c2ecf20Sopenharmony_ci
14968c2ecf20Sopenharmony_ci	case MANAGEMENT_ORB_FUNCTION_SET_PASSWORD:
14978c2ecf20Sopenharmony_ci		pr_notice("SET PASSWORD not implemented\n");
14988c2ecf20Sopenharmony_ci
14998c2ecf20Sopenharmony_ci		req->status.status = cpu_to_be32(
15008c2ecf20Sopenharmony_ci			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
15018c2ecf20Sopenharmony_ci			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
15028c2ecf20Sopenharmony_ci
15038c2ecf20Sopenharmony_ci		break;
15048c2ecf20Sopenharmony_ci
15058c2ecf20Sopenharmony_ci	case MANAGEMENT_ORB_FUNCTION_LOGOUT:
15068c2ecf20Sopenharmony_ci		sbp_management_request_logout(agent, req, &status_data_len);
15078c2ecf20Sopenharmony_ci		break;
15088c2ecf20Sopenharmony_ci
15098c2ecf20Sopenharmony_ci	case MANAGEMENT_ORB_FUNCTION_ABORT_TASK:
15108c2ecf20Sopenharmony_ci		pr_notice("ABORT TASK not implemented\n");
15118c2ecf20Sopenharmony_ci
15128c2ecf20Sopenharmony_ci		req->status.status = cpu_to_be32(
15138c2ecf20Sopenharmony_ci			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
15148c2ecf20Sopenharmony_ci			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
15158c2ecf20Sopenharmony_ci
15168c2ecf20Sopenharmony_ci		break;
15178c2ecf20Sopenharmony_ci
15188c2ecf20Sopenharmony_ci	case MANAGEMENT_ORB_FUNCTION_ABORT_TASK_SET:
15198c2ecf20Sopenharmony_ci		pr_notice("ABORT TASK SET not implemented\n");
15208c2ecf20Sopenharmony_ci
15218c2ecf20Sopenharmony_ci		req->status.status = cpu_to_be32(
15228c2ecf20Sopenharmony_ci			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
15238c2ecf20Sopenharmony_ci			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
15248c2ecf20Sopenharmony_ci
15258c2ecf20Sopenharmony_ci		break;
15268c2ecf20Sopenharmony_ci
15278c2ecf20Sopenharmony_ci	case MANAGEMENT_ORB_FUNCTION_LOGICAL_UNIT_RESET:
15288c2ecf20Sopenharmony_ci		pr_notice("LOGICAL UNIT RESET not implemented\n");
15298c2ecf20Sopenharmony_ci
15308c2ecf20Sopenharmony_ci		req->status.status = cpu_to_be32(
15318c2ecf20Sopenharmony_ci			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
15328c2ecf20Sopenharmony_ci			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
15338c2ecf20Sopenharmony_ci
15348c2ecf20Sopenharmony_ci		break;
15358c2ecf20Sopenharmony_ci
15368c2ecf20Sopenharmony_ci	case MANAGEMENT_ORB_FUNCTION_TARGET_RESET:
15378c2ecf20Sopenharmony_ci		pr_notice("TARGET RESET not implemented\n");
15388c2ecf20Sopenharmony_ci
15398c2ecf20Sopenharmony_ci		req->status.status = cpu_to_be32(
15408c2ecf20Sopenharmony_ci			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
15418c2ecf20Sopenharmony_ci			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
15428c2ecf20Sopenharmony_ci
15438c2ecf20Sopenharmony_ci		break;
15448c2ecf20Sopenharmony_ci
15458c2ecf20Sopenharmony_ci	default:
15468c2ecf20Sopenharmony_ci		pr_notice("unknown management function 0x%x\n",
15478c2ecf20Sopenharmony_ci			MANAGEMENT_ORB_FUNCTION(be32_to_cpu(req->orb.misc)));
15488c2ecf20Sopenharmony_ci
15498c2ecf20Sopenharmony_ci		req->status.status = cpu_to_be32(
15508c2ecf20Sopenharmony_ci			STATUS_BLOCK_RESP(STATUS_RESP_REQUEST_COMPLETE) |
15518c2ecf20Sopenharmony_ci			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_REQ_TYPE_NOTSUPP));
15528c2ecf20Sopenharmony_ci
15538c2ecf20Sopenharmony_ci		break;
15548c2ecf20Sopenharmony_ci	}
15558c2ecf20Sopenharmony_ci
15568c2ecf20Sopenharmony_ci	req->status.status |= cpu_to_be32(
15578c2ecf20Sopenharmony_ci		STATUS_BLOCK_SRC(1) | /* Response to ORB, next_ORB absent */
15588c2ecf20Sopenharmony_ci		STATUS_BLOCK_LEN(DIV_ROUND_UP(status_data_len, 4) + 1) |
15598c2ecf20Sopenharmony_ci		STATUS_BLOCK_ORB_OFFSET_HIGH(agent->orb_offset >> 32));
15608c2ecf20Sopenharmony_ci	req->status.orb_low = cpu_to_be32(agent->orb_offset);
15618c2ecf20Sopenharmony_ci
15628c2ecf20Sopenharmony_ci	/* write the status block back to the initiator */
15638c2ecf20Sopenharmony_ci	ret = sbp_run_transaction(req->card, TCODE_WRITE_BLOCK_REQUEST,
15648c2ecf20Sopenharmony_ci		req->node_addr, req->generation, req->speed,
15658c2ecf20Sopenharmony_ci		sbp2_pointer_to_addr(&req->orb.status_fifo),
15668c2ecf20Sopenharmony_ci		&req->status, 8 + status_data_len);
15678c2ecf20Sopenharmony_ci	if (ret != RCODE_COMPLETE) {
15688c2ecf20Sopenharmony_ci		pr_debug("mgt_orb status write failed: %x\n", ret);
15698c2ecf20Sopenharmony_ci		goto out;
15708c2ecf20Sopenharmony_ci	}
15718c2ecf20Sopenharmony_ci
15728c2ecf20Sopenharmony_ciout:
15738c2ecf20Sopenharmony_ci	fw_card_put(req->card);
15748c2ecf20Sopenharmony_ci	kfree(req);
15758c2ecf20Sopenharmony_ci
15768c2ecf20Sopenharmony_ci	spin_lock_bh(&agent->lock);
15778c2ecf20Sopenharmony_ci	agent->state = MANAGEMENT_AGENT_STATE_IDLE;
15788c2ecf20Sopenharmony_ci	spin_unlock_bh(&agent->lock);
15798c2ecf20Sopenharmony_ci}
15808c2ecf20Sopenharmony_ci
15818c2ecf20Sopenharmony_cistatic void sbp_mgt_agent_rw(struct fw_card *card,
15828c2ecf20Sopenharmony_ci	struct fw_request *request, int tcode, int destination, int source,
15838c2ecf20Sopenharmony_ci	int generation, unsigned long long offset, void *data, size_t length,
15848c2ecf20Sopenharmony_ci	void *callback_data)
15858c2ecf20Sopenharmony_ci{
15868c2ecf20Sopenharmony_ci	struct sbp_management_agent *agent = callback_data;
15878c2ecf20Sopenharmony_ci	struct sbp2_pointer *ptr = data;
15888c2ecf20Sopenharmony_ci	int rcode = RCODE_ADDRESS_ERROR;
15898c2ecf20Sopenharmony_ci
15908c2ecf20Sopenharmony_ci	if (!agent->tport->enable)
15918c2ecf20Sopenharmony_ci		goto out;
15928c2ecf20Sopenharmony_ci
15938c2ecf20Sopenharmony_ci	if ((offset != agent->handler.offset) || (length != 8))
15948c2ecf20Sopenharmony_ci		goto out;
15958c2ecf20Sopenharmony_ci
15968c2ecf20Sopenharmony_ci	if (tcode == TCODE_WRITE_BLOCK_REQUEST) {
15978c2ecf20Sopenharmony_ci		struct sbp_management_request *req;
15988c2ecf20Sopenharmony_ci		int prev_state;
15998c2ecf20Sopenharmony_ci
16008c2ecf20Sopenharmony_ci		spin_lock_bh(&agent->lock);
16018c2ecf20Sopenharmony_ci		prev_state = agent->state;
16028c2ecf20Sopenharmony_ci		agent->state = MANAGEMENT_AGENT_STATE_BUSY;
16038c2ecf20Sopenharmony_ci		spin_unlock_bh(&agent->lock);
16048c2ecf20Sopenharmony_ci
16058c2ecf20Sopenharmony_ci		if (prev_state == MANAGEMENT_AGENT_STATE_BUSY) {
16068c2ecf20Sopenharmony_ci			pr_notice("ignoring management request while busy\n");
16078c2ecf20Sopenharmony_ci			rcode = RCODE_CONFLICT_ERROR;
16088c2ecf20Sopenharmony_ci			goto out;
16098c2ecf20Sopenharmony_ci		}
16108c2ecf20Sopenharmony_ci		req = kzalloc(sizeof(*req), GFP_ATOMIC);
16118c2ecf20Sopenharmony_ci		if (!req) {
16128c2ecf20Sopenharmony_ci			rcode = RCODE_CONFLICT_ERROR;
16138c2ecf20Sopenharmony_ci			goto out;
16148c2ecf20Sopenharmony_ci		}
16158c2ecf20Sopenharmony_ci
16168c2ecf20Sopenharmony_ci		req->card = fw_card_get(card);
16178c2ecf20Sopenharmony_ci		req->generation = generation;
16188c2ecf20Sopenharmony_ci		req->node_addr = source;
16198c2ecf20Sopenharmony_ci		req->speed = fw_get_request_speed(request);
16208c2ecf20Sopenharmony_ci
16218c2ecf20Sopenharmony_ci		agent->orb_offset = sbp2_pointer_to_addr(ptr);
16228c2ecf20Sopenharmony_ci		agent->request = req;
16238c2ecf20Sopenharmony_ci
16248c2ecf20Sopenharmony_ci		queue_work(system_unbound_wq, &agent->work);
16258c2ecf20Sopenharmony_ci		rcode = RCODE_COMPLETE;
16268c2ecf20Sopenharmony_ci	} else if (tcode == TCODE_READ_BLOCK_REQUEST) {
16278c2ecf20Sopenharmony_ci		addr_to_sbp2_pointer(agent->orb_offset, ptr);
16288c2ecf20Sopenharmony_ci		rcode = RCODE_COMPLETE;
16298c2ecf20Sopenharmony_ci	} else {
16308c2ecf20Sopenharmony_ci		rcode = RCODE_TYPE_ERROR;
16318c2ecf20Sopenharmony_ci	}
16328c2ecf20Sopenharmony_ci
16338c2ecf20Sopenharmony_ciout:
16348c2ecf20Sopenharmony_ci	fw_send_response(card, request, rcode);
16358c2ecf20Sopenharmony_ci}
16368c2ecf20Sopenharmony_ci
16378c2ecf20Sopenharmony_cistatic struct sbp_management_agent *sbp_management_agent_register(
16388c2ecf20Sopenharmony_ci		struct sbp_tport *tport)
16398c2ecf20Sopenharmony_ci{
16408c2ecf20Sopenharmony_ci	int ret;
16418c2ecf20Sopenharmony_ci	struct sbp_management_agent *agent;
16428c2ecf20Sopenharmony_ci
16438c2ecf20Sopenharmony_ci	agent = kmalloc(sizeof(*agent), GFP_KERNEL);
16448c2ecf20Sopenharmony_ci	if (!agent)
16458c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
16468c2ecf20Sopenharmony_ci
16478c2ecf20Sopenharmony_ci	spin_lock_init(&agent->lock);
16488c2ecf20Sopenharmony_ci	agent->tport = tport;
16498c2ecf20Sopenharmony_ci	agent->handler.length = 0x08;
16508c2ecf20Sopenharmony_ci	agent->handler.address_callback = sbp_mgt_agent_rw;
16518c2ecf20Sopenharmony_ci	agent->handler.callback_data = agent;
16528c2ecf20Sopenharmony_ci	agent->state = MANAGEMENT_AGENT_STATE_IDLE;
16538c2ecf20Sopenharmony_ci	INIT_WORK(&agent->work, sbp_mgt_agent_process);
16548c2ecf20Sopenharmony_ci	agent->orb_offset = 0;
16558c2ecf20Sopenharmony_ci	agent->request = NULL;
16568c2ecf20Sopenharmony_ci
16578c2ecf20Sopenharmony_ci	ret = fw_core_add_address_handler(&agent->handler,
16588c2ecf20Sopenharmony_ci			&sbp_register_region);
16598c2ecf20Sopenharmony_ci	if (ret < 0) {
16608c2ecf20Sopenharmony_ci		kfree(agent);
16618c2ecf20Sopenharmony_ci		return ERR_PTR(ret);
16628c2ecf20Sopenharmony_ci	}
16638c2ecf20Sopenharmony_ci
16648c2ecf20Sopenharmony_ci	return agent;
16658c2ecf20Sopenharmony_ci}
16668c2ecf20Sopenharmony_ci
16678c2ecf20Sopenharmony_cistatic void sbp_management_agent_unregister(struct sbp_management_agent *agent)
16688c2ecf20Sopenharmony_ci{
16698c2ecf20Sopenharmony_ci	fw_core_remove_address_handler(&agent->handler);
16708c2ecf20Sopenharmony_ci	cancel_work_sync(&agent->work);
16718c2ecf20Sopenharmony_ci	kfree(agent);
16728c2ecf20Sopenharmony_ci}
16738c2ecf20Sopenharmony_ci
16748c2ecf20Sopenharmony_cistatic int sbp_check_true(struct se_portal_group *se_tpg)
16758c2ecf20Sopenharmony_ci{
16768c2ecf20Sopenharmony_ci	return 1;
16778c2ecf20Sopenharmony_ci}
16788c2ecf20Sopenharmony_ci
16798c2ecf20Sopenharmony_cistatic int sbp_check_false(struct se_portal_group *se_tpg)
16808c2ecf20Sopenharmony_ci{
16818c2ecf20Sopenharmony_ci	return 0;
16828c2ecf20Sopenharmony_ci}
16838c2ecf20Sopenharmony_ci
16848c2ecf20Sopenharmony_cistatic char *sbp_get_fabric_wwn(struct se_portal_group *se_tpg)
16858c2ecf20Sopenharmony_ci{
16868c2ecf20Sopenharmony_ci	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
16878c2ecf20Sopenharmony_ci	struct sbp_tport *tport = tpg->tport;
16888c2ecf20Sopenharmony_ci
16898c2ecf20Sopenharmony_ci	return &tport->tport_name[0];
16908c2ecf20Sopenharmony_ci}
16918c2ecf20Sopenharmony_ci
16928c2ecf20Sopenharmony_cistatic u16 sbp_get_tag(struct se_portal_group *se_tpg)
16938c2ecf20Sopenharmony_ci{
16948c2ecf20Sopenharmony_ci	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
16958c2ecf20Sopenharmony_ci	return tpg->tport_tpgt;
16968c2ecf20Sopenharmony_ci}
16978c2ecf20Sopenharmony_ci
16988c2ecf20Sopenharmony_cistatic u32 sbp_tpg_get_inst_index(struct se_portal_group *se_tpg)
16998c2ecf20Sopenharmony_ci{
17008c2ecf20Sopenharmony_ci	return 1;
17018c2ecf20Sopenharmony_ci}
17028c2ecf20Sopenharmony_ci
17038c2ecf20Sopenharmony_cistatic void sbp_release_cmd(struct se_cmd *se_cmd)
17048c2ecf20Sopenharmony_ci{
17058c2ecf20Sopenharmony_ci	struct sbp_target_request *req = container_of(se_cmd,
17068c2ecf20Sopenharmony_ci			struct sbp_target_request, se_cmd);
17078c2ecf20Sopenharmony_ci
17088c2ecf20Sopenharmony_ci	sbp_free_request(req);
17098c2ecf20Sopenharmony_ci}
17108c2ecf20Sopenharmony_ci
17118c2ecf20Sopenharmony_cistatic u32 sbp_sess_get_index(struct se_session *se_sess)
17128c2ecf20Sopenharmony_ci{
17138c2ecf20Sopenharmony_ci	return 0;
17148c2ecf20Sopenharmony_ci}
17158c2ecf20Sopenharmony_ci
17168c2ecf20Sopenharmony_cistatic int sbp_write_pending(struct se_cmd *se_cmd)
17178c2ecf20Sopenharmony_ci{
17188c2ecf20Sopenharmony_ci	struct sbp_target_request *req = container_of(se_cmd,
17198c2ecf20Sopenharmony_ci			struct sbp_target_request, se_cmd);
17208c2ecf20Sopenharmony_ci	int ret;
17218c2ecf20Sopenharmony_ci
17228c2ecf20Sopenharmony_ci	ret = sbp_rw_data(req);
17238c2ecf20Sopenharmony_ci	if (ret) {
17248c2ecf20Sopenharmony_ci		req->status.status |= cpu_to_be32(
17258c2ecf20Sopenharmony_ci			STATUS_BLOCK_RESP(
17268c2ecf20Sopenharmony_ci				STATUS_RESP_TRANSPORT_FAILURE) |
17278c2ecf20Sopenharmony_ci			STATUS_BLOCK_DEAD(0) |
17288c2ecf20Sopenharmony_ci			STATUS_BLOCK_LEN(1) |
17298c2ecf20Sopenharmony_ci			STATUS_BLOCK_SBP_STATUS(
17308c2ecf20Sopenharmony_ci				SBP_STATUS_UNSPECIFIED_ERROR));
17318c2ecf20Sopenharmony_ci		sbp_send_status(req);
17328c2ecf20Sopenharmony_ci		return ret;
17338c2ecf20Sopenharmony_ci	}
17348c2ecf20Sopenharmony_ci
17358c2ecf20Sopenharmony_ci	target_execute_cmd(se_cmd);
17368c2ecf20Sopenharmony_ci	return 0;
17378c2ecf20Sopenharmony_ci}
17388c2ecf20Sopenharmony_ci
17398c2ecf20Sopenharmony_cistatic void sbp_set_default_node_attrs(struct se_node_acl *nacl)
17408c2ecf20Sopenharmony_ci{
17418c2ecf20Sopenharmony_ci	return;
17428c2ecf20Sopenharmony_ci}
17438c2ecf20Sopenharmony_ci
17448c2ecf20Sopenharmony_cistatic int sbp_get_cmd_state(struct se_cmd *se_cmd)
17458c2ecf20Sopenharmony_ci{
17468c2ecf20Sopenharmony_ci	return 0;
17478c2ecf20Sopenharmony_ci}
17488c2ecf20Sopenharmony_ci
17498c2ecf20Sopenharmony_cistatic int sbp_queue_data_in(struct se_cmd *se_cmd)
17508c2ecf20Sopenharmony_ci{
17518c2ecf20Sopenharmony_ci	struct sbp_target_request *req = container_of(se_cmd,
17528c2ecf20Sopenharmony_ci			struct sbp_target_request, se_cmd);
17538c2ecf20Sopenharmony_ci	int ret;
17548c2ecf20Sopenharmony_ci
17558c2ecf20Sopenharmony_ci	ret = sbp_rw_data(req);
17568c2ecf20Sopenharmony_ci	if (ret) {
17578c2ecf20Sopenharmony_ci		req->status.status |= cpu_to_be32(
17588c2ecf20Sopenharmony_ci			STATUS_BLOCK_RESP(STATUS_RESP_TRANSPORT_FAILURE) |
17598c2ecf20Sopenharmony_ci			STATUS_BLOCK_DEAD(0) |
17608c2ecf20Sopenharmony_ci			STATUS_BLOCK_LEN(1) |
17618c2ecf20Sopenharmony_ci			STATUS_BLOCK_SBP_STATUS(SBP_STATUS_UNSPECIFIED_ERROR));
17628c2ecf20Sopenharmony_ci		sbp_send_status(req);
17638c2ecf20Sopenharmony_ci		return ret;
17648c2ecf20Sopenharmony_ci	}
17658c2ecf20Sopenharmony_ci
17668c2ecf20Sopenharmony_ci	return sbp_send_sense(req);
17678c2ecf20Sopenharmony_ci}
17688c2ecf20Sopenharmony_ci
17698c2ecf20Sopenharmony_ci/*
17708c2ecf20Sopenharmony_ci * Called after command (no data transfer) or after the write (to device)
17718c2ecf20Sopenharmony_ci * operation is completed
17728c2ecf20Sopenharmony_ci */
17738c2ecf20Sopenharmony_cistatic int sbp_queue_status(struct se_cmd *se_cmd)
17748c2ecf20Sopenharmony_ci{
17758c2ecf20Sopenharmony_ci	struct sbp_target_request *req = container_of(se_cmd,
17768c2ecf20Sopenharmony_ci			struct sbp_target_request, se_cmd);
17778c2ecf20Sopenharmony_ci
17788c2ecf20Sopenharmony_ci	return sbp_send_sense(req);
17798c2ecf20Sopenharmony_ci}
17808c2ecf20Sopenharmony_ci
17818c2ecf20Sopenharmony_cistatic void sbp_queue_tm_rsp(struct se_cmd *se_cmd)
17828c2ecf20Sopenharmony_ci{
17838c2ecf20Sopenharmony_ci}
17848c2ecf20Sopenharmony_ci
17858c2ecf20Sopenharmony_cistatic void sbp_aborted_task(struct se_cmd *se_cmd)
17868c2ecf20Sopenharmony_ci{
17878c2ecf20Sopenharmony_ci	return;
17888c2ecf20Sopenharmony_ci}
17898c2ecf20Sopenharmony_ci
17908c2ecf20Sopenharmony_cistatic int sbp_check_stop_free(struct se_cmd *se_cmd)
17918c2ecf20Sopenharmony_ci{
17928c2ecf20Sopenharmony_ci	struct sbp_target_request *req = container_of(se_cmd,
17938c2ecf20Sopenharmony_ci			struct sbp_target_request, se_cmd);
17948c2ecf20Sopenharmony_ci
17958c2ecf20Sopenharmony_ci	return transport_generic_free_cmd(&req->se_cmd, 0);
17968c2ecf20Sopenharmony_ci}
17978c2ecf20Sopenharmony_ci
17988c2ecf20Sopenharmony_cistatic int sbp_count_se_tpg_luns(struct se_portal_group *tpg)
17998c2ecf20Sopenharmony_ci{
18008c2ecf20Sopenharmony_ci	struct se_lun *lun;
18018c2ecf20Sopenharmony_ci	int count = 0;
18028c2ecf20Sopenharmony_ci
18038c2ecf20Sopenharmony_ci	rcu_read_lock();
18048c2ecf20Sopenharmony_ci	hlist_for_each_entry_rcu(lun, &tpg->tpg_lun_hlist, link)
18058c2ecf20Sopenharmony_ci		count++;
18068c2ecf20Sopenharmony_ci	rcu_read_unlock();
18078c2ecf20Sopenharmony_ci
18088c2ecf20Sopenharmony_ci	return count;
18098c2ecf20Sopenharmony_ci}
18108c2ecf20Sopenharmony_ci
18118c2ecf20Sopenharmony_cistatic int sbp_update_unit_directory(struct sbp_tport *tport)
18128c2ecf20Sopenharmony_ci{
18138c2ecf20Sopenharmony_ci	struct se_lun *lun;
18148c2ecf20Sopenharmony_ci	int num_luns, num_entries, idx = 0, mgt_agt_addr, ret;
18158c2ecf20Sopenharmony_ci	u32 *data;
18168c2ecf20Sopenharmony_ci
18178c2ecf20Sopenharmony_ci	if (tport->unit_directory.data) {
18188c2ecf20Sopenharmony_ci		fw_core_remove_descriptor(&tport->unit_directory);
18198c2ecf20Sopenharmony_ci		kfree(tport->unit_directory.data);
18208c2ecf20Sopenharmony_ci		tport->unit_directory.data = NULL;
18218c2ecf20Sopenharmony_ci	}
18228c2ecf20Sopenharmony_ci
18238c2ecf20Sopenharmony_ci	if (!tport->enable || !tport->tpg)
18248c2ecf20Sopenharmony_ci		return 0;
18258c2ecf20Sopenharmony_ci
18268c2ecf20Sopenharmony_ci	num_luns = sbp_count_se_tpg_luns(&tport->tpg->se_tpg);
18278c2ecf20Sopenharmony_ci
18288c2ecf20Sopenharmony_ci	/*
18298c2ecf20Sopenharmony_ci	 * Number of entries in the final unit directory:
18308c2ecf20Sopenharmony_ci	 *  - all of those in the template
18318c2ecf20Sopenharmony_ci	 *  - management_agent
18328c2ecf20Sopenharmony_ci	 *  - unit_characteristics
18338c2ecf20Sopenharmony_ci	 *  - reconnect_timeout
18348c2ecf20Sopenharmony_ci	 *  - unit unique ID
18358c2ecf20Sopenharmony_ci	 *  - one for each LUN
18368c2ecf20Sopenharmony_ci	 *
18378c2ecf20Sopenharmony_ci	 *  MUST NOT include leaf or sub-directory entries
18388c2ecf20Sopenharmony_ci	 */
18398c2ecf20Sopenharmony_ci	num_entries = ARRAY_SIZE(sbp_unit_directory_template) + 4 + num_luns;
18408c2ecf20Sopenharmony_ci
18418c2ecf20Sopenharmony_ci	if (tport->directory_id != -1)
18428c2ecf20Sopenharmony_ci		num_entries++;
18438c2ecf20Sopenharmony_ci
18448c2ecf20Sopenharmony_ci	/* allocate num_entries + 4 for the header and unique ID leaf */
18458c2ecf20Sopenharmony_ci	data = kcalloc((num_entries + 4), sizeof(u32), GFP_KERNEL);
18468c2ecf20Sopenharmony_ci	if (!data)
18478c2ecf20Sopenharmony_ci		return -ENOMEM;
18488c2ecf20Sopenharmony_ci
18498c2ecf20Sopenharmony_ci	/* directory_length */
18508c2ecf20Sopenharmony_ci	data[idx++] = num_entries << 16;
18518c2ecf20Sopenharmony_ci
18528c2ecf20Sopenharmony_ci	/* directory_id */
18538c2ecf20Sopenharmony_ci	if (tport->directory_id != -1)
18548c2ecf20Sopenharmony_ci		data[idx++] = (CSR_DIRECTORY_ID << 24) | tport->directory_id;
18558c2ecf20Sopenharmony_ci
18568c2ecf20Sopenharmony_ci	/* unit directory template */
18578c2ecf20Sopenharmony_ci	memcpy(&data[idx], sbp_unit_directory_template,
18588c2ecf20Sopenharmony_ci			sizeof(sbp_unit_directory_template));
18598c2ecf20Sopenharmony_ci	idx += ARRAY_SIZE(sbp_unit_directory_template);
18608c2ecf20Sopenharmony_ci
18618c2ecf20Sopenharmony_ci	/* management_agent */
18628c2ecf20Sopenharmony_ci	mgt_agt_addr = (tport->mgt_agt->handler.offset - CSR_REGISTER_BASE) / 4;
18638c2ecf20Sopenharmony_ci	data[idx++] = 0x54000000 | (mgt_agt_addr & 0x00ffffff);
18648c2ecf20Sopenharmony_ci
18658c2ecf20Sopenharmony_ci	/* unit_characteristics */
18668c2ecf20Sopenharmony_ci	data[idx++] = 0x3a000000 |
18678c2ecf20Sopenharmony_ci		(((tport->mgt_orb_timeout * 2) << 8) & 0xff00) |
18688c2ecf20Sopenharmony_ci		SBP_ORB_FETCH_SIZE;
18698c2ecf20Sopenharmony_ci
18708c2ecf20Sopenharmony_ci	/* reconnect_timeout */
18718c2ecf20Sopenharmony_ci	data[idx++] = 0x3d000000 | (tport->max_reconnect_timeout & 0xffff);
18728c2ecf20Sopenharmony_ci
18738c2ecf20Sopenharmony_ci	/* unit unique ID (leaf is just after LUNs) */
18748c2ecf20Sopenharmony_ci	data[idx++] = 0x8d000000 | (num_luns + 1);
18758c2ecf20Sopenharmony_ci
18768c2ecf20Sopenharmony_ci	rcu_read_lock();
18778c2ecf20Sopenharmony_ci	hlist_for_each_entry_rcu(lun, &tport->tpg->se_tpg.tpg_lun_hlist, link) {
18788c2ecf20Sopenharmony_ci		struct se_device *dev;
18798c2ecf20Sopenharmony_ci		int type;
18808c2ecf20Sopenharmony_ci		/*
18818c2ecf20Sopenharmony_ci		 * rcu_dereference_raw protected by se_lun->lun_group symlink
18828c2ecf20Sopenharmony_ci		 * reference to se_device->dev_group.
18838c2ecf20Sopenharmony_ci		 */
18848c2ecf20Sopenharmony_ci		dev = rcu_dereference_raw(lun->lun_se_dev);
18858c2ecf20Sopenharmony_ci		type = dev->transport->get_device_type(dev);
18868c2ecf20Sopenharmony_ci
18878c2ecf20Sopenharmony_ci		/* logical_unit_number */
18888c2ecf20Sopenharmony_ci		data[idx++] = 0x14000000 |
18898c2ecf20Sopenharmony_ci			((type << 16) & 0x1f0000) |
18908c2ecf20Sopenharmony_ci			(lun->unpacked_lun & 0xffff);
18918c2ecf20Sopenharmony_ci	}
18928c2ecf20Sopenharmony_ci	rcu_read_unlock();
18938c2ecf20Sopenharmony_ci
18948c2ecf20Sopenharmony_ci	/* unit unique ID leaf */
18958c2ecf20Sopenharmony_ci	data[idx++] = 2 << 16;
18968c2ecf20Sopenharmony_ci	data[idx++] = tport->guid >> 32;
18978c2ecf20Sopenharmony_ci	data[idx++] = tport->guid;
18988c2ecf20Sopenharmony_ci
18998c2ecf20Sopenharmony_ci	tport->unit_directory.length = idx;
19008c2ecf20Sopenharmony_ci	tport->unit_directory.key = (CSR_DIRECTORY | CSR_UNIT) << 24;
19018c2ecf20Sopenharmony_ci	tport->unit_directory.data = data;
19028c2ecf20Sopenharmony_ci
19038c2ecf20Sopenharmony_ci	ret = fw_core_add_descriptor(&tport->unit_directory);
19048c2ecf20Sopenharmony_ci	if (ret < 0) {
19058c2ecf20Sopenharmony_ci		kfree(tport->unit_directory.data);
19068c2ecf20Sopenharmony_ci		tport->unit_directory.data = NULL;
19078c2ecf20Sopenharmony_ci	}
19088c2ecf20Sopenharmony_ci
19098c2ecf20Sopenharmony_ci	return ret;
19108c2ecf20Sopenharmony_ci}
19118c2ecf20Sopenharmony_ci
19128c2ecf20Sopenharmony_cistatic ssize_t sbp_parse_wwn(const char *name, u64 *wwn)
19138c2ecf20Sopenharmony_ci{
19148c2ecf20Sopenharmony_ci	const char *cp;
19158c2ecf20Sopenharmony_ci	char c, nibble;
19168c2ecf20Sopenharmony_ci	int pos = 0, err;
19178c2ecf20Sopenharmony_ci
19188c2ecf20Sopenharmony_ci	*wwn = 0;
19198c2ecf20Sopenharmony_ci	for (cp = name; cp < &name[SBP_NAMELEN - 1]; cp++) {
19208c2ecf20Sopenharmony_ci		c = *cp;
19218c2ecf20Sopenharmony_ci		if (c == '\n' && cp[1] == '\0')
19228c2ecf20Sopenharmony_ci			continue;
19238c2ecf20Sopenharmony_ci		if (c == '\0') {
19248c2ecf20Sopenharmony_ci			err = 2;
19258c2ecf20Sopenharmony_ci			if (pos != 16)
19268c2ecf20Sopenharmony_ci				goto fail;
19278c2ecf20Sopenharmony_ci			return cp - name;
19288c2ecf20Sopenharmony_ci		}
19298c2ecf20Sopenharmony_ci		err = 3;
19308c2ecf20Sopenharmony_ci		if (isdigit(c))
19318c2ecf20Sopenharmony_ci			nibble = c - '0';
19328c2ecf20Sopenharmony_ci		else if (isxdigit(c))
19338c2ecf20Sopenharmony_ci			nibble = tolower(c) - 'a' + 10;
19348c2ecf20Sopenharmony_ci		else
19358c2ecf20Sopenharmony_ci			goto fail;
19368c2ecf20Sopenharmony_ci		*wwn = (*wwn << 4) | nibble;
19378c2ecf20Sopenharmony_ci		pos++;
19388c2ecf20Sopenharmony_ci	}
19398c2ecf20Sopenharmony_ci	err = 4;
19408c2ecf20Sopenharmony_cifail:
19418c2ecf20Sopenharmony_ci	printk(KERN_INFO "err %u len %zu pos %u\n",
19428c2ecf20Sopenharmony_ci			err, cp - name, pos);
19438c2ecf20Sopenharmony_ci	return -1;
19448c2ecf20Sopenharmony_ci}
19458c2ecf20Sopenharmony_ci
19468c2ecf20Sopenharmony_cistatic ssize_t sbp_format_wwn(char *buf, size_t len, u64 wwn)
19478c2ecf20Sopenharmony_ci{
19488c2ecf20Sopenharmony_ci	return snprintf(buf, len, "%016llx", wwn);
19498c2ecf20Sopenharmony_ci}
19508c2ecf20Sopenharmony_ci
19518c2ecf20Sopenharmony_cistatic int sbp_init_nodeacl(struct se_node_acl *se_nacl, const char *name)
19528c2ecf20Sopenharmony_ci{
19538c2ecf20Sopenharmony_ci	u64 guid = 0;
19548c2ecf20Sopenharmony_ci
19558c2ecf20Sopenharmony_ci	if (sbp_parse_wwn(name, &guid) < 0)
19568c2ecf20Sopenharmony_ci		return -EINVAL;
19578c2ecf20Sopenharmony_ci	return 0;
19588c2ecf20Sopenharmony_ci}
19598c2ecf20Sopenharmony_ci
19608c2ecf20Sopenharmony_cistatic int sbp_post_link_lun(
19618c2ecf20Sopenharmony_ci		struct se_portal_group *se_tpg,
19628c2ecf20Sopenharmony_ci		struct se_lun *se_lun)
19638c2ecf20Sopenharmony_ci{
19648c2ecf20Sopenharmony_ci	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
19658c2ecf20Sopenharmony_ci
19668c2ecf20Sopenharmony_ci	return sbp_update_unit_directory(tpg->tport);
19678c2ecf20Sopenharmony_ci}
19688c2ecf20Sopenharmony_ci
19698c2ecf20Sopenharmony_cistatic void sbp_pre_unlink_lun(
19708c2ecf20Sopenharmony_ci		struct se_portal_group *se_tpg,
19718c2ecf20Sopenharmony_ci		struct se_lun *se_lun)
19728c2ecf20Sopenharmony_ci{
19738c2ecf20Sopenharmony_ci	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
19748c2ecf20Sopenharmony_ci	struct sbp_tport *tport = tpg->tport;
19758c2ecf20Sopenharmony_ci	int ret;
19768c2ecf20Sopenharmony_ci
19778c2ecf20Sopenharmony_ci	if (sbp_count_se_tpg_luns(&tpg->se_tpg) == 0)
19788c2ecf20Sopenharmony_ci		tport->enable = 0;
19798c2ecf20Sopenharmony_ci
19808c2ecf20Sopenharmony_ci	ret = sbp_update_unit_directory(tport);
19818c2ecf20Sopenharmony_ci	if (ret < 0)
19828c2ecf20Sopenharmony_ci		pr_err("unlink LUN: failed to update unit directory\n");
19838c2ecf20Sopenharmony_ci}
19848c2ecf20Sopenharmony_ci
19858c2ecf20Sopenharmony_cistatic struct se_portal_group *sbp_make_tpg(struct se_wwn *wwn,
19868c2ecf20Sopenharmony_ci					    const char *name)
19878c2ecf20Sopenharmony_ci{
19888c2ecf20Sopenharmony_ci	struct sbp_tport *tport =
19898c2ecf20Sopenharmony_ci		container_of(wwn, struct sbp_tport, tport_wwn);
19908c2ecf20Sopenharmony_ci
19918c2ecf20Sopenharmony_ci	struct sbp_tpg *tpg;
19928c2ecf20Sopenharmony_ci	unsigned long tpgt;
19938c2ecf20Sopenharmony_ci	int ret;
19948c2ecf20Sopenharmony_ci
19958c2ecf20Sopenharmony_ci	if (strstr(name, "tpgt_") != name)
19968c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
19978c2ecf20Sopenharmony_ci	if (kstrtoul(name + 5, 10, &tpgt) || tpgt > UINT_MAX)
19988c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
19998c2ecf20Sopenharmony_ci
20008c2ecf20Sopenharmony_ci	if (tport->tpg) {
20018c2ecf20Sopenharmony_ci		pr_err("Only one TPG per Unit is possible.\n");
20028c2ecf20Sopenharmony_ci		return ERR_PTR(-EBUSY);
20038c2ecf20Sopenharmony_ci	}
20048c2ecf20Sopenharmony_ci
20058c2ecf20Sopenharmony_ci	tpg = kzalloc(sizeof(*tpg), GFP_KERNEL);
20068c2ecf20Sopenharmony_ci	if (!tpg)
20078c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
20088c2ecf20Sopenharmony_ci
20098c2ecf20Sopenharmony_ci	tpg->tport = tport;
20108c2ecf20Sopenharmony_ci	tpg->tport_tpgt = tpgt;
20118c2ecf20Sopenharmony_ci	tport->tpg = tpg;
20128c2ecf20Sopenharmony_ci
20138c2ecf20Sopenharmony_ci	/* default attribute values */
20148c2ecf20Sopenharmony_ci	tport->enable = 0;
20158c2ecf20Sopenharmony_ci	tport->directory_id = -1;
20168c2ecf20Sopenharmony_ci	tport->mgt_orb_timeout = 15;
20178c2ecf20Sopenharmony_ci	tport->max_reconnect_timeout = 5;
20188c2ecf20Sopenharmony_ci	tport->max_logins_per_lun = 1;
20198c2ecf20Sopenharmony_ci
20208c2ecf20Sopenharmony_ci	tport->mgt_agt = sbp_management_agent_register(tport);
20218c2ecf20Sopenharmony_ci	if (IS_ERR(tport->mgt_agt)) {
20228c2ecf20Sopenharmony_ci		ret = PTR_ERR(tport->mgt_agt);
20238c2ecf20Sopenharmony_ci		goto out_free_tpg;
20248c2ecf20Sopenharmony_ci	}
20258c2ecf20Sopenharmony_ci
20268c2ecf20Sopenharmony_ci	ret = core_tpg_register(wwn, &tpg->se_tpg, SCSI_PROTOCOL_SBP);
20278c2ecf20Sopenharmony_ci	if (ret < 0)
20288c2ecf20Sopenharmony_ci		goto out_unreg_mgt_agt;
20298c2ecf20Sopenharmony_ci
20308c2ecf20Sopenharmony_ci	return &tpg->se_tpg;
20318c2ecf20Sopenharmony_ci
20328c2ecf20Sopenharmony_ciout_unreg_mgt_agt:
20338c2ecf20Sopenharmony_ci	sbp_management_agent_unregister(tport->mgt_agt);
20348c2ecf20Sopenharmony_ciout_free_tpg:
20358c2ecf20Sopenharmony_ci	tport->tpg = NULL;
20368c2ecf20Sopenharmony_ci	kfree(tpg);
20378c2ecf20Sopenharmony_ci	return ERR_PTR(ret);
20388c2ecf20Sopenharmony_ci}
20398c2ecf20Sopenharmony_ci
20408c2ecf20Sopenharmony_cistatic void sbp_drop_tpg(struct se_portal_group *se_tpg)
20418c2ecf20Sopenharmony_ci{
20428c2ecf20Sopenharmony_ci	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
20438c2ecf20Sopenharmony_ci	struct sbp_tport *tport = tpg->tport;
20448c2ecf20Sopenharmony_ci
20458c2ecf20Sopenharmony_ci	core_tpg_deregister(se_tpg);
20468c2ecf20Sopenharmony_ci	sbp_management_agent_unregister(tport->mgt_agt);
20478c2ecf20Sopenharmony_ci	tport->tpg = NULL;
20488c2ecf20Sopenharmony_ci	kfree(tpg);
20498c2ecf20Sopenharmony_ci}
20508c2ecf20Sopenharmony_ci
20518c2ecf20Sopenharmony_cistatic struct se_wwn *sbp_make_tport(
20528c2ecf20Sopenharmony_ci		struct target_fabric_configfs *tf,
20538c2ecf20Sopenharmony_ci		struct config_group *group,
20548c2ecf20Sopenharmony_ci		const char *name)
20558c2ecf20Sopenharmony_ci{
20568c2ecf20Sopenharmony_ci	struct sbp_tport *tport;
20578c2ecf20Sopenharmony_ci	u64 guid = 0;
20588c2ecf20Sopenharmony_ci
20598c2ecf20Sopenharmony_ci	if (sbp_parse_wwn(name, &guid) < 0)
20608c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
20618c2ecf20Sopenharmony_ci
20628c2ecf20Sopenharmony_ci	tport = kzalloc(sizeof(*tport), GFP_KERNEL);
20638c2ecf20Sopenharmony_ci	if (!tport)
20648c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
20658c2ecf20Sopenharmony_ci
20668c2ecf20Sopenharmony_ci	tport->guid = guid;
20678c2ecf20Sopenharmony_ci	sbp_format_wwn(tport->tport_name, SBP_NAMELEN, guid);
20688c2ecf20Sopenharmony_ci
20698c2ecf20Sopenharmony_ci	return &tport->tport_wwn;
20708c2ecf20Sopenharmony_ci}
20718c2ecf20Sopenharmony_ci
20728c2ecf20Sopenharmony_cistatic void sbp_drop_tport(struct se_wwn *wwn)
20738c2ecf20Sopenharmony_ci{
20748c2ecf20Sopenharmony_ci	struct sbp_tport *tport =
20758c2ecf20Sopenharmony_ci		container_of(wwn, struct sbp_tport, tport_wwn);
20768c2ecf20Sopenharmony_ci
20778c2ecf20Sopenharmony_ci	kfree(tport);
20788c2ecf20Sopenharmony_ci}
20798c2ecf20Sopenharmony_ci
20808c2ecf20Sopenharmony_cistatic ssize_t sbp_wwn_version_show(struct config_item *item, char *page)
20818c2ecf20Sopenharmony_ci{
20828c2ecf20Sopenharmony_ci	return sprintf(page, "FireWire SBP fabric module %s\n", SBP_VERSION);
20838c2ecf20Sopenharmony_ci}
20848c2ecf20Sopenharmony_ci
20858c2ecf20Sopenharmony_ciCONFIGFS_ATTR_RO(sbp_wwn_, version);
20868c2ecf20Sopenharmony_ci
20878c2ecf20Sopenharmony_cistatic struct configfs_attribute *sbp_wwn_attrs[] = {
20888c2ecf20Sopenharmony_ci	&sbp_wwn_attr_version,
20898c2ecf20Sopenharmony_ci	NULL,
20908c2ecf20Sopenharmony_ci};
20918c2ecf20Sopenharmony_ci
20928c2ecf20Sopenharmony_cistatic ssize_t sbp_tpg_directory_id_show(struct config_item *item, char *page)
20938c2ecf20Sopenharmony_ci{
20948c2ecf20Sopenharmony_ci	struct se_portal_group *se_tpg = to_tpg(item);
20958c2ecf20Sopenharmony_ci	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
20968c2ecf20Sopenharmony_ci	struct sbp_tport *tport = tpg->tport;
20978c2ecf20Sopenharmony_ci
20988c2ecf20Sopenharmony_ci	if (tport->directory_id == -1)
20998c2ecf20Sopenharmony_ci		return sprintf(page, "implicit\n");
21008c2ecf20Sopenharmony_ci	else
21018c2ecf20Sopenharmony_ci		return sprintf(page, "%06x\n", tport->directory_id);
21028c2ecf20Sopenharmony_ci}
21038c2ecf20Sopenharmony_ci
21048c2ecf20Sopenharmony_cistatic ssize_t sbp_tpg_directory_id_store(struct config_item *item,
21058c2ecf20Sopenharmony_ci		const char *page, size_t count)
21068c2ecf20Sopenharmony_ci{
21078c2ecf20Sopenharmony_ci	struct se_portal_group *se_tpg = to_tpg(item);
21088c2ecf20Sopenharmony_ci	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
21098c2ecf20Sopenharmony_ci	struct sbp_tport *tport = tpg->tport;
21108c2ecf20Sopenharmony_ci	unsigned long val;
21118c2ecf20Sopenharmony_ci
21128c2ecf20Sopenharmony_ci	if (tport->enable) {
21138c2ecf20Sopenharmony_ci		pr_err("Cannot change the directory_id on an active target.\n");
21148c2ecf20Sopenharmony_ci		return -EBUSY;
21158c2ecf20Sopenharmony_ci	}
21168c2ecf20Sopenharmony_ci
21178c2ecf20Sopenharmony_ci	if (strstr(page, "implicit") == page) {
21188c2ecf20Sopenharmony_ci		tport->directory_id = -1;
21198c2ecf20Sopenharmony_ci	} else {
21208c2ecf20Sopenharmony_ci		if (kstrtoul(page, 16, &val) < 0)
21218c2ecf20Sopenharmony_ci			return -EINVAL;
21228c2ecf20Sopenharmony_ci		if (val > 0xffffff)
21238c2ecf20Sopenharmony_ci			return -EINVAL;
21248c2ecf20Sopenharmony_ci
21258c2ecf20Sopenharmony_ci		tport->directory_id = val;
21268c2ecf20Sopenharmony_ci	}
21278c2ecf20Sopenharmony_ci
21288c2ecf20Sopenharmony_ci	return count;
21298c2ecf20Sopenharmony_ci}
21308c2ecf20Sopenharmony_ci
21318c2ecf20Sopenharmony_cistatic ssize_t sbp_tpg_enable_show(struct config_item *item, char *page)
21328c2ecf20Sopenharmony_ci{
21338c2ecf20Sopenharmony_ci	struct se_portal_group *se_tpg = to_tpg(item);
21348c2ecf20Sopenharmony_ci	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
21358c2ecf20Sopenharmony_ci	struct sbp_tport *tport = tpg->tport;
21368c2ecf20Sopenharmony_ci	return sprintf(page, "%d\n", tport->enable);
21378c2ecf20Sopenharmony_ci}
21388c2ecf20Sopenharmony_ci
21398c2ecf20Sopenharmony_cistatic ssize_t sbp_tpg_enable_store(struct config_item *item,
21408c2ecf20Sopenharmony_ci		const char *page, size_t count)
21418c2ecf20Sopenharmony_ci{
21428c2ecf20Sopenharmony_ci	struct se_portal_group *se_tpg = to_tpg(item);
21438c2ecf20Sopenharmony_ci	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
21448c2ecf20Sopenharmony_ci	struct sbp_tport *tport = tpg->tport;
21458c2ecf20Sopenharmony_ci	unsigned long val;
21468c2ecf20Sopenharmony_ci	int ret;
21478c2ecf20Sopenharmony_ci
21488c2ecf20Sopenharmony_ci	if (kstrtoul(page, 0, &val) < 0)
21498c2ecf20Sopenharmony_ci		return -EINVAL;
21508c2ecf20Sopenharmony_ci	if ((val != 0) && (val != 1))
21518c2ecf20Sopenharmony_ci		return -EINVAL;
21528c2ecf20Sopenharmony_ci
21538c2ecf20Sopenharmony_ci	if (tport->enable == val)
21548c2ecf20Sopenharmony_ci		return count;
21558c2ecf20Sopenharmony_ci
21568c2ecf20Sopenharmony_ci	if (val) {
21578c2ecf20Sopenharmony_ci		if (sbp_count_se_tpg_luns(&tpg->se_tpg) == 0) {
21588c2ecf20Sopenharmony_ci			pr_err("Cannot enable a target with no LUNs!\n");
21598c2ecf20Sopenharmony_ci			return -EINVAL;
21608c2ecf20Sopenharmony_ci		}
21618c2ecf20Sopenharmony_ci	} else {
21628c2ecf20Sopenharmony_ci		/* XXX: force-shutdown sessions instead? */
21638c2ecf20Sopenharmony_ci		spin_lock_bh(&se_tpg->session_lock);
21648c2ecf20Sopenharmony_ci		if (!list_empty(&se_tpg->tpg_sess_list)) {
21658c2ecf20Sopenharmony_ci			spin_unlock_bh(&se_tpg->session_lock);
21668c2ecf20Sopenharmony_ci			return -EBUSY;
21678c2ecf20Sopenharmony_ci		}
21688c2ecf20Sopenharmony_ci		spin_unlock_bh(&se_tpg->session_lock);
21698c2ecf20Sopenharmony_ci	}
21708c2ecf20Sopenharmony_ci
21718c2ecf20Sopenharmony_ci	tport->enable = val;
21728c2ecf20Sopenharmony_ci
21738c2ecf20Sopenharmony_ci	ret = sbp_update_unit_directory(tport);
21748c2ecf20Sopenharmony_ci	if (ret < 0) {
21758c2ecf20Sopenharmony_ci		pr_err("Could not update Config ROM\n");
21768c2ecf20Sopenharmony_ci		return ret;
21778c2ecf20Sopenharmony_ci	}
21788c2ecf20Sopenharmony_ci
21798c2ecf20Sopenharmony_ci	return count;
21808c2ecf20Sopenharmony_ci}
21818c2ecf20Sopenharmony_ci
21828c2ecf20Sopenharmony_ciCONFIGFS_ATTR(sbp_tpg_, directory_id);
21838c2ecf20Sopenharmony_ciCONFIGFS_ATTR(sbp_tpg_, enable);
21848c2ecf20Sopenharmony_ci
21858c2ecf20Sopenharmony_cistatic struct configfs_attribute *sbp_tpg_base_attrs[] = {
21868c2ecf20Sopenharmony_ci	&sbp_tpg_attr_directory_id,
21878c2ecf20Sopenharmony_ci	&sbp_tpg_attr_enable,
21888c2ecf20Sopenharmony_ci	NULL,
21898c2ecf20Sopenharmony_ci};
21908c2ecf20Sopenharmony_ci
21918c2ecf20Sopenharmony_cistatic ssize_t sbp_tpg_attrib_mgt_orb_timeout_show(struct config_item *item,
21928c2ecf20Sopenharmony_ci		char *page)
21938c2ecf20Sopenharmony_ci{
21948c2ecf20Sopenharmony_ci	struct se_portal_group *se_tpg = attrib_to_tpg(item);
21958c2ecf20Sopenharmony_ci	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
21968c2ecf20Sopenharmony_ci	struct sbp_tport *tport = tpg->tport;
21978c2ecf20Sopenharmony_ci	return sprintf(page, "%d\n", tport->mgt_orb_timeout);
21988c2ecf20Sopenharmony_ci}
21998c2ecf20Sopenharmony_ci
22008c2ecf20Sopenharmony_cistatic ssize_t sbp_tpg_attrib_mgt_orb_timeout_store(struct config_item *item,
22018c2ecf20Sopenharmony_ci		const char *page, size_t count)
22028c2ecf20Sopenharmony_ci{
22038c2ecf20Sopenharmony_ci	struct se_portal_group *se_tpg = attrib_to_tpg(item);
22048c2ecf20Sopenharmony_ci	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
22058c2ecf20Sopenharmony_ci	struct sbp_tport *tport = tpg->tport;
22068c2ecf20Sopenharmony_ci	unsigned long val;
22078c2ecf20Sopenharmony_ci	int ret;
22088c2ecf20Sopenharmony_ci
22098c2ecf20Sopenharmony_ci	if (kstrtoul(page, 0, &val) < 0)
22108c2ecf20Sopenharmony_ci		return -EINVAL;
22118c2ecf20Sopenharmony_ci	if ((val < 1) || (val > 127))
22128c2ecf20Sopenharmony_ci		return -EINVAL;
22138c2ecf20Sopenharmony_ci
22148c2ecf20Sopenharmony_ci	if (tport->mgt_orb_timeout == val)
22158c2ecf20Sopenharmony_ci		return count;
22168c2ecf20Sopenharmony_ci
22178c2ecf20Sopenharmony_ci	tport->mgt_orb_timeout = val;
22188c2ecf20Sopenharmony_ci
22198c2ecf20Sopenharmony_ci	ret = sbp_update_unit_directory(tport);
22208c2ecf20Sopenharmony_ci	if (ret < 0)
22218c2ecf20Sopenharmony_ci		return ret;
22228c2ecf20Sopenharmony_ci
22238c2ecf20Sopenharmony_ci	return count;
22248c2ecf20Sopenharmony_ci}
22258c2ecf20Sopenharmony_ci
22268c2ecf20Sopenharmony_cistatic ssize_t sbp_tpg_attrib_max_reconnect_timeout_show(struct config_item *item,
22278c2ecf20Sopenharmony_ci		char *page)
22288c2ecf20Sopenharmony_ci{
22298c2ecf20Sopenharmony_ci	struct se_portal_group *se_tpg = attrib_to_tpg(item);
22308c2ecf20Sopenharmony_ci	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
22318c2ecf20Sopenharmony_ci	struct sbp_tport *tport = tpg->tport;
22328c2ecf20Sopenharmony_ci	return sprintf(page, "%d\n", tport->max_reconnect_timeout);
22338c2ecf20Sopenharmony_ci}
22348c2ecf20Sopenharmony_ci
22358c2ecf20Sopenharmony_cistatic ssize_t sbp_tpg_attrib_max_reconnect_timeout_store(struct config_item *item,
22368c2ecf20Sopenharmony_ci		const char *page, size_t count)
22378c2ecf20Sopenharmony_ci{
22388c2ecf20Sopenharmony_ci	struct se_portal_group *se_tpg = attrib_to_tpg(item);
22398c2ecf20Sopenharmony_ci	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
22408c2ecf20Sopenharmony_ci	struct sbp_tport *tport = tpg->tport;
22418c2ecf20Sopenharmony_ci	unsigned long val;
22428c2ecf20Sopenharmony_ci	int ret;
22438c2ecf20Sopenharmony_ci
22448c2ecf20Sopenharmony_ci	if (kstrtoul(page, 0, &val) < 0)
22458c2ecf20Sopenharmony_ci		return -EINVAL;
22468c2ecf20Sopenharmony_ci	if ((val < 1) || (val > 32767))
22478c2ecf20Sopenharmony_ci		return -EINVAL;
22488c2ecf20Sopenharmony_ci
22498c2ecf20Sopenharmony_ci	if (tport->max_reconnect_timeout == val)
22508c2ecf20Sopenharmony_ci		return count;
22518c2ecf20Sopenharmony_ci
22528c2ecf20Sopenharmony_ci	tport->max_reconnect_timeout = val;
22538c2ecf20Sopenharmony_ci
22548c2ecf20Sopenharmony_ci	ret = sbp_update_unit_directory(tport);
22558c2ecf20Sopenharmony_ci	if (ret < 0)
22568c2ecf20Sopenharmony_ci		return ret;
22578c2ecf20Sopenharmony_ci
22588c2ecf20Sopenharmony_ci	return count;
22598c2ecf20Sopenharmony_ci}
22608c2ecf20Sopenharmony_ci
22618c2ecf20Sopenharmony_cistatic ssize_t sbp_tpg_attrib_max_logins_per_lun_show(struct config_item *item,
22628c2ecf20Sopenharmony_ci		char *page)
22638c2ecf20Sopenharmony_ci{
22648c2ecf20Sopenharmony_ci	struct se_portal_group *se_tpg = attrib_to_tpg(item);
22658c2ecf20Sopenharmony_ci	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
22668c2ecf20Sopenharmony_ci	struct sbp_tport *tport = tpg->tport;
22678c2ecf20Sopenharmony_ci	return sprintf(page, "%d\n", tport->max_logins_per_lun);
22688c2ecf20Sopenharmony_ci}
22698c2ecf20Sopenharmony_ci
22708c2ecf20Sopenharmony_cistatic ssize_t sbp_tpg_attrib_max_logins_per_lun_store(struct config_item *item,
22718c2ecf20Sopenharmony_ci		const char *page, size_t count)
22728c2ecf20Sopenharmony_ci{
22738c2ecf20Sopenharmony_ci	struct se_portal_group *se_tpg = attrib_to_tpg(item);
22748c2ecf20Sopenharmony_ci	struct sbp_tpg *tpg = container_of(se_tpg, struct sbp_tpg, se_tpg);
22758c2ecf20Sopenharmony_ci	struct sbp_tport *tport = tpg->tport;
22768c2ecf20Sopenharmony_ci	unsigned long val;
22778c2ecf20Sopenharmony_ci
22788c2ecf20Sopenharmony_ci	if (kstrtoul(page, 0, &val) < 0)
22798c2ecf20Sopenharmony_ci		return -EINVAL;
22808c2ecf20Sopenharmony_ci	if ((val < 1) || (val > 127))
22818c2ecf20Sopenharmony_ci		return -EINVAL;
22828c2ecf20Sopenharmony_ci
22838c2ecf20Sopenharmony_ci	/* XXX: also check against current count? */
22848c2ecf20Sopenharmony_ci
22858c2ecf20Sopenharmony_ci	tport->max_logins_per_lun = val;
22868c2ecf20Sopenharmony_ci
22878c2ecf20Sopenharmony_ci	return count;
22888c2ecf20Sopenharmony_ci}
22898c2ecf20Sopenharmony_ci
22908c2ecf20Sopenharmony_ciCONFIGFS_ATTR(sbp_tpg_attrib_, mgt_orb_timeout);
22918c2ecf20Sopenharmony_ciCONFIGFS_ATTR(sbp_tpg_attrib_, max_reconnect_timeout);
22928c2ecf20Sopenharmony_ciCONFIGFS_ATTR(sbp_tpg_attrib_, max_logins_per_lun);
22938c2ecf20Sopenharmony_ci
22948c2ecf20Sopenharmony_cistatic struct configfs_attribute *sbp_tpg_attrib_attrs[] = {
22958c2ecf20Sopenharmony_ci	&sbp_tpg_attrib_attr_mgt_orb_timeout,
22968c2ecf20Sopenharmony_ci	&sbp_tpg_attrib_attr_max_reconnect_timeout,
22978c2ecf20Sopenharmony_ci	&sbp_tpg_attrib_attr_max_logins_per_lun,
22988c2ecf20Sopenharmony_ci	NULL,
22998c2ecf20Sopenharmony_ci};
23008c2ecf20Sopenharmony_ci
23018c2ecf20Sopenharmony_cistatic const struct target_core_fabric_ops sbp_ops = {
23028c2ecf20Sopenharmony_ci	.module				= THIS_MODULE,
23038c2ecf20Sopenharmony_ci	.fabric_name			= "sbp",
23048c2ecf20Sopenharmony_ci	.tpg_get_wwn			= sbp_get_fabric_wwn,
23058c2ecf20Sopenharmony_ci	.tpg_get_tag			= sbp_get_tag,
23068c2ecf20Sopenharmony_ci	.tpg_check_demo_mode		= sbp_check_true,
23078c2ecf20Sopenharmony_ci	.tpg_check_demo_mode_cache	= sbp_check_true,
23088c2ecf20Sopenharmony_ci	.tpg_check_demo_mode_write_protect = sbp_check_false,
23098c2ecf20Sopenharmony_ci	.tpg_check_prod_mode_write_protect = sbp_check_false,
23108c2ecf20Sopenharmony_ci	.tpg_get_inst_index		= sbp_tpg_get_inst_index,
23118c2ecf20Sopenharmony_ci	.release_cmd			= sbp_release_cmd,
23128c2ecf20Sopenharmony_ci	.sess_get_index			= sbp_sess_get_index,
23138c2ecf20Sopenharmony_ci	.write_pending			= sbp_write_pending,
23148c2ecf20Sopenharmony_ci	.set_default_node_attributes	= sbp_set_default_node_attrs,
23158c2ecf20Sopenharmony_ci	.get_cmd_state			= sbp_get_cmd_state,
23168c2ecf20Sopenharmony_ci	.queue_data_in			= sbp_queue_data_in,
23178c2ecf20Sopenharmony_ci	.queue_status			= sbp_queue_status,
23188c2ecf20Sopenharmony_ci	.queue_tm_rsp			= sbp_queue_tm_rsp,
23198c2ecf20Sopenharmony_ci	.aborted_task			= sbp_aborted_task,
23208c2ecf20Sopenharmony_ci	.check_stop_free		= sbp_check_stop_free,
23218c2ecf20Sopenharmony_ci
23228c2ecf20Sopenharmony_ci	.fabric_make_wwn		= sbp_make_tport,
23238c2ecf20Sopenharmony_ci	.fabric_drop_wwn		= sbp_drop_tport,
23248c2ecf20Sopenharmony_ci	.fabric_make_tpg		= sbp_make_tpg,
23258c2ecf20Sopenharmony_ci	.fabric_drop_tpg		= sbp_drop_tpg,
23268c2ecf20Sopenharmony_ci	.fabric_post_link		= sbp_post_link_lun,
23278c2ecf20Sopenharmony_ci	.fabric_pre_unlink		= sbp_pre_unlink_lun,
23288c2ecf20Sopenharmony_ci	.fabric_make_np			= NULL,
23298c2ecf20Sopenharmony_ci	.fabric_drop_np			= NULL,
23308c2ecf20Sopenharmony_ci	.fabric_init_nodeacl		= sbp_init_nodeacl,
23318c2ecf20Sopenharmony_ci
23328c2ecf20Sopenharmony_ci	.tfc_wwn_attrs			= sbp_wwn_attrs,
23338c2ecf20Sopenharmony_ci	.tfc_tpg_base_attrs		= sbp_tpg_base_attrs,
23348c2ecf20Sopenharmony_ci	.tfc_tpg_attrib_attrs		= sbp_tpg_attrib_attrs,
23358c2ecf20Sopenharmony_ci};
23368c2ecf20Sopenharmony_ci
23378c2ecf20Sopenharmony_cistatic int __init sbp_init(void)
23388c2ecf20Sopenharmony_ci{
23398c2ecf20Sopenharmony_ci	return target_register_template(&sbp_ops);
23408c2ecf20Sopenharmony_ci};
23418c2ecf20Sopenharmony_ci
23428c2ecf20Sopenharmony_cistatic void __exit sbp_exit(void)
23438c2ecf20Sopenharmony_ci{
23448c2ecf20Sopenharmony_ci	target_unregister_template(&sbp_ops);
23458c2ecf20Sopenharmony_ci};
23468c2ecf20Sopenharmony_ci
23478c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("FireWire SBP fabric driver");
23488c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
23498c2ecf20Sopenharmony_cimodule_init(sbp_init);
23508c2ecf20Sopenharmony_cimodule_exit(sbp_exit);
2351