162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#include <linux/module.h>
462306a36Sopenharmony_ci#include <linux/moduleparam.h>
562306a36Sopenharmony_ci#include <linux/init.h>
662306a36Sopenharmony_ci#include <linux/slab.h>
762306a36Sopenharmony_ci#include <linux/types.h>
862306a36Sopenharmony_ci#include <linux/configfs.h>
962306a36Sopenharmony_ci#include <scsi/scsi.h>
1062306a36Sopenharmony_ci#include <scsi/scsi_tcq.h>
1162306a36Sopenharmony_ci#include <scsi/scsi_host.h>
1262306a36Sopenharmony_ci#include <scsi/scsi_device.h>
1362306a36Sopenharmony_ci#include <scsi/scsi_cmnd.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <target/target_core_base.h>
1662306a36Sopenharmony_ci#include <target/target_core_fabric.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include "tcm_remote.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistatic inline struct tcm_remote_tpg *remote_tpg(struct se_portal_group *se_tpg)
2162306a36Sopenharmony_ci{
2262306a36Sopenharmony_ci	return container_of(se_tpg, struct tcm_remote_tpg, remote_se_tpg);
2362306a36Sopenharmony_ci}
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic char *tcm_remote_get_endpoint_wwn(struct se_portal_group *se_tpg)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	/*
2862306a36Sopenharmony_ci	 * Return the passed NAA identifier for the Target Port
2962306a36Sopenharmony_ci	 */
3062306a36Sopenharmony_ci	return &remote_tpg(se_tpg)->remote_hba->remote_wwn_address[0];
3162306a36Sopenharmony_ci}
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic u16 tcm_remote_get_tag(struct se_portal_group *se_tpg)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	/*
3662306a36Sopenharmony_ci	 * This Tag is used when forming SCSI Name identifier in EVPD=1 0x83
3762306a36Sopenharmony_ci	 * to represent the SCSI Target Port.
3862306a36Sopenharmony_ci	 */
3962306a36Sopenharmony_ci	return remote_tpg(se_tpg)->remote_tpgt;
4062306a36Sopenharmony_ci}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic int tcm_remote_dummy_cmd_fn(struct se_cmd *se_cmd)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	return 0;
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic void tcm_remote_dummy_cmd_void_fn(struct se_cmd *se_cmd)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistatic char *tcm_remote_dump_proto_id(struct tcm_remote_hba *remote_hba)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	switch (remote_hba->remote_proto_id) {
5562306a36Sopenharmony_ci	case SCSI_PROTOCOL_SAS:
5662306a36Sopenharmony_ci		return "SAS";
5762306a36Sopenharmony_ci	case SCSI_PROTOCOL_SRP:
5862306a36Sopenharmony_ci		return "SRP";
5962306a36Sopenharmony_ci	case SCSI_PROTOCOL_FCP:
6062306a36Sopenharmony_ci		return "FCP";
6162306a36Sopenharmony_ci	case SCSI_PROTOCOL_ISCSI:
6262306a36Sopenharmony_ci		return "iSCSI";
6362306a36Sopenharmony_ci	default:
6462306a36Sopenharmony_ci		break;
6562306a36Sopenharmony_ci	}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	return "Unknown";
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic int tcm_remote_port_link(
7162306a36Sopenharmony_ci	struct se_portal_group *se_tpg,
7262306a36Sopenharmony_ci	struct se_lun *lun)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	pr_debug("TCM_Remote_ConfigFS: Port Link LUN %lld Successful\n",
7562306a36Sopenharmony_ci		 lun->unpacked_lun);
7662306a36Sopenharmony_ci	return 0;
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic void tcm_remote_port_unlink(
8062306a36Sopenharmony_ci	struct se_portal_group *se_tpg,
8162306a36Sopenharmony_ci	struct se_lun *lun)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	pr_debug("TCM_Remote_ConfigFS: Port Unlink LUN %lld Successful\n",
8462306a36Sopenharmony_ci		 lun->unpacked_lun);
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic struct se_portal_group *tcm_remote_make_tpg(
8862306a36Sopenharmony_ci	struct se_wwn *wwn,
8962306a36Sopenharmony_ci	const char *name)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	struct tcm_remote_hba *remote_hba = container_of(wwn,
9262306a36Sopenharmony_ci			struct tcm_remote_hba, remote_hba_wwn);
9362306a36Sopenharmony_ci	struct tcm_remote_tpg *remote_tpg;
9462306a36Sopenharmony_ci	unsigned long tpgt;
9562306a36Sopenharmony_ci	int ret;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	if (strstr(name, "tpgt_") != name) {
9862306a36Sopenharmony_ci		pr_err("Unable to locate \"tpgt_#\" directory group\n");
9962306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
10062306a36Sopenharmony_ci	}
10162306a36Sopenharmony_ci	if (kstrtoul(name + 5, 10, &tpgt))
10262306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	if (tpgt >= TL_TPGS_PER_HBA) {
10562306a36Sopenharmony_ci		pr_err("Passed tpgt: %lu exceeds TL_TPGS_PER_HBA: %u\n",
10662306a36Sopenharmony_ci		       tpgt, TL_TPGS_PER_HBA);
10762306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
10862306a36Sopenharmony_ci	}
10962306a36Sopenharmony_ci	remote_tpg = &remote_hba->remote_hba_tpgs[tpgt];
11062306a36Sopenharmony_ci	remote_tpg->remote_hba = remote_hba;
11162306a36Sopenharmony_ci	remote_tpg->remote_tpgt = tpgt;
11262306a36Sopenharmony_ci	/*
11362306a36Sopenharmony_ci	 * Register the remote_tpg as a emulated TCM Target Endpoint
11462306a36Sopenharmony_ci	 */
11562306a36Sopenharmony_ci	ret = core_tpg_register(wwn, &remote_tpg->remote_se_tpg,
11662306a36Sopenharmony_ci				remote_hba->remote_proto_id);
11762306a36Sopenharmony_ci	if (ret < 0)
11862306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	pr_debug("TCM_Remote_ConfigFS: Allocated Emulated %s Target Port %s,t,0x%04lx\n",
12162306a36Sopenharmony_ci		 tcm_remote_dump_proto_id(remote_hba),
12262306a36Sopenharmony_ci		 config_item_name(&wwn->wwn_group.cg_item), tpgt);
12362306a36Sopenharmony_ci	return &remote_tpg->remote_se_tpg;
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic void tcm_remote_drop_tpg(struct se_portal_group *se_tpg)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	struct se_wwn *wwn = se_tpg->se_tpg_wwn;
12962306a36Sopenharmony_ci	struct tcm_remote_tpg *remote_tpg = container_of(se_tpg,
13062306a36Sopenharmony_ci				struct tcm_remote_tpg, remote_se_tpg);
13162306a36Sopenharmony_ci	struct tcm_remote_hba *remote_hba;
13262306a36Sopenharmony_ci	unsigned short tpgt;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	remote_hba = remote_tpg->remote_hba;
13562306a36Sopenharmony_ci	tpgt = remote_tpg->remote_tpgt;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	/*
13862306a36Sopenharmony_ci	 * Deregister the remote_tpg as a emulated TCM Target Endpoint
13962306a36Sopenharmony_ci	 */
14062306a36Sopenharmony_ci	core_tpg_deregister(se_tpg);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	remote_tpg->remote_hba = NULL;
14362306a36Sopenharmony_ci	remote_tpg->remote_tpgt = 0;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	pr_debug("TCM_Remote_ConfigFS: Deallocated Emulated %s Target Port %s,t,0x%04x\n",
14662306a36Sopenharmony_ci		 tcm_remote_dump_proto_id(remote_hba),
14762306a36Sopenharmony_ci		 config_item_name(&wwn->wwn_group.cg_item), tpgt);
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistatic struct se_wwn *tcm_remote_make_wwn(
15162306a36Sopenharmony_ci	struct target_fabric_configfs *tf,
15262306a36Sopenharmony_ci	struct config_group *group,
15362306a36Sopenharmony_ci	const char *name)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	struct tcm_remote_hba *remote_hba;
15662306a36Sopenharmony_ci	char *ptr;
15762306a36Sopenharmony_ci	int ret, off = 0;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	remote_hba = kzalloc(sizeof(*remote_hba), GFP_KERNEL);
16062306a36Sopenharmony_ci	if (!remote_hba)
16162306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	/*
16462306a36Sopenharmony_ci	 * Determine the emulated Protocol Identifier and Target Port Name
16562306a36Sopenharmony_ci	 * based on the incoming configfs directory name.
16662306a36Sopenharmony_ci	 */
16762306a36Sopenharmony_ci	ptr = strstr(name, "naa.");
16862306a36Sopenharmony_ci	if (ptr) {
16962306a36Sopenharmony_ci		remote_hba->remote_proto_id = SCSI_PROTOCOL_SAS;
17062306a36Sopenharmony_ci		goto check_len;
17162306a36Sopenharmony_ci	}
17262306a36Sopenharmony_ci	ptr = strstr(name, "fc.");
17362306a36Sopenharmony_ci	if (ptr) {
17462306a36Sopenharmony_ci		remote_hba->remote_proto_id = SCSI_PROTOCOL_FCP;
17562306a36Sopenharmony_ci		off = 3; /* Skip over "fc." */
17662306a36Sopenharmony_ci		goto check_len;
17762306a36Sopenharmony_ci	}
17862306a36Sopenharmony_ci	ptr = strstr(name, "0x");
17962306a36Sopenharmony_ci	if (ptr) {
18062306a36Sopenharmony_ci		remote_hba->remote_proto_id = SCSI_PROTOCOL_SRP;
18162306a36Sopenharmony_ci		off = 2; /* Skip over "0x" */
18262306a36Sopenharmony_ci		goto check_len;
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci	ptr = strstr(name, "iqn.");
18562306a36Sopenharmony_ci	if (!ptr) {
18662306a36Sopenharmony_ci		pr_err("Unable to locate prefix for emulated Target Port: %s\n",
18762306a36Sopenharmony_ci		       name);
18862306a36Sopenharmony_ci		ret = -EINVAL;
18962306a36Sopenharmony_ci		goto out;
19062306a36Sopenharmony_ci	}
19162306a36Sopenharmony_ci	remote_hba->remote_proto_id = SCSI_PROTOCOL_ISCSI;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_cicheck_len:
19462306a36Sopenharmony_ci	if (strlen(name) >= TL_WWN_ADDR_LEN) {
19562306a36Sopenharmony_ci		pr_err("Emulated NAA %s Address: %s, exceeds max: %d\n",
19662306a36Sopenharmony_ci		       name, tcm_remote_dump_proto_id(remote_hba), TL_WWN_ADDR_LEN);
19762306a36Sopenharmony_ci		ret = -EINVAL;
19862306a36Sopenharmony_ci		goto out;
19962306a36Sopenharmony_ci	}
20062306a36Sopenharmony_ci	snprintf(&remote_hba->remote_wwn_address[0], TL_WWN_ADDR_LEN, "%s", &name[off]);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	pr_debug("TCM_Remote_ConfigFS: Allocated emulated Target %s Address: %s\n",
20362306a36Sopenharmony_ci		 tcm_remote_dump_proto_id(remote_hba), name);
20462306a36Sopenharmony_ci	return &remote_hba->remote_hba_wwn;
20562306a36Sopenharmony_ciout:
20662306a36Sopenharmony_ci	kfree(remote_hba);
20762306a36Sopenharmony_ci	return ERR_PTR(ret);
20862306a36Sopenharmony_ci}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_cistatic void tcm_remote_drop_wwn(struct se_wwn *wwn)
21162306a36Sopenharmony_ci{
21262306a36Sopenharmony_ci	struct tcm_remote_hba *remote_hba = container_of(wwn,
21362306a36Sopenharmony_ci				struct tcm_remote_hba, remote_hba_wwn);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	pr_debug("TCM_Remote_ConfigFS: Deallocating emulated Target %s Address: %s\n",
21662306a36Sopenharmony_ci		 tcm_remote_dump_proto_id(remote_hba),
21762306a36Sopenharmony_ci		 remote_hba->remote_wwn_address);
21862306a36Sopenharmony_ci	kfree(remote_hba);
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_cistatic ssize_t tcm_remote_wwn_version_show(struct config_item *item, char *page)
22262306a36Sopenharmony_ci{
22362306a36Sopenharmony_ci	return sprintf(page, "TCM Remote Fabric module %s\n", TCM_REMOTE_VERSION);
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ciCONFIGFS_ATTR_RO(tcm_remote_wwn_, version);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cistatic struct configfs_attribute *tcm_remote_wwn_attrs[] = {
22962306a36Sopenharmony_ci	&tcm_remote_wwn_attr_version,
23062306a36Sopenharmony_ci	NULL,
23162306a36Sopenharmony_ci};
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_cistatic const struct target_core_fabric_ops remote_ops = {
23462306a36Sopenharmony_ci	.module				= THIS_MODULE,
23562306a36Sopenharmony_ci	.fabric_name			= "remote",
23662306a36Sopenharmony_ci	.tpg_get_wwn			= tcm_remote_get_endpoint_wwn,
23762306a36Sopenharmony_ci	.tpg_get_tag			= tcm_remote_get_tag,
23862306a36Sopenharmony_ci	.check_stop_free		= tcm_remote_dummy_cmd_fn,
23962306a36Sopenharmony_ci	.release_cmd			= tcm_remote_dummy_cmd_void_fn,
24062306a36Sopenharmony_ci	.write_pending			= tcm_remote_dummy_cmd_fn,
24162306a36Sopenharmony_ci	.queue_data_in			= tcm_remote_dummy_cmd_fn,
24262306a36Sopenharmony_ci	.queue_status			= tcm_remote_dummy_cmd_fn,
24362306a36Sopenharmony_ci	.queue_tm_rsp			= tcm_remote_dummy_cmd_void_fn,
24462306a36Sopenharmony_ci	.aborted_task			= tcm_remote_dummy_cmd_void_fn,
24562306a36Sopenharmony_ci	.fabric_make_wwn		= tcm_remote_make_wwn,
24662306a36Sopenharmony_ci	.fabric_drop_wwn		= tcm_remote_drop_wwn,
24762306a36Sopenharmony_ci	.fabric_make_tpg		= tcm_remote_make_tpg,
24862306a36Sopenharmony_ci	.fabric_drop_tpg		= tcm_remote_drop_tpg,
24962306a36Sopenharmony_ci	.fabric_post_link		= tcm_remote_port_link,
25062306a36Sopenharmony_ci	.fabric_pre_unlink		= tcm_remote_port_unlink,
25162306a36Sopenharmony_ci	.tfc_wwn_attrs			= tcm_remote_wwn_attrs,
25262306a36Sopenharmony_ci};
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_cistatic int __init tcm_remote_fabric_init(void)
25562306a36Sopenharmony_ci{
25662306a36Sopenharmony_ci	return target_register_template(&remote_ops);
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistatic void __exit tcm_remote_fabric_exit(void)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	target_unregister_template(&remote_ops);
26262306a36Sopenharmony_ci}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ciMODULE_DESCRIPTION("TCM virtual remote target");
26562306a36Sopenharmony_ciMODULE_AUTHOR("Dmitry Bogdanov <d.bogdanov@yadro.com>");
26662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
26762306a36Sopenharmony_cimodule_init(tcm_remote_fabric_init);
26862306a36Sopenharmony_cimodule_exit(tcm_remote_fabric_exit);
269