1// SPDX-License-Identifier: GPL-2.0-or-later
2
3#include <linux/module.h>
4#include <linux/moduleparam.h>
5#include <linux/init.h>
6#include <linux/slab.h>
7#include <linux/types.h>
8#include <linux/configfs.h>
9#include <scsi/scsi.h>
10#include <scsi/scsi_tcq.h>
11#include <scsi/scsi_host.h>
12#include <scsi/scsi_device.h>
13#include <scsi/scsi_cmnd.h>
14
15#include <target/target_core_base.h>
16#include <target/target_core_fabric.h>
17
18#include "tcm_remote.h"
19
20static inline struct tcm_remote_tpg *remote_tpg(struct se_portal_group *se_tpg)
21{
22	return container_of(se_tpg, struct tcm_remote_tpg, remote_se_tpg);
23}
24
25static char *tcm_remote_get_endpoint_wwn(struct se_portal_group *se_tpg)
26{
27	/*
28	 * Return the passed NAA identifier for the Target Port
29	 */
30	return &remote_tpg(se_tpg)->remote_hba->remote_wwn_address[0];
31}
32
33static u16 tcm_remote_get_tag(struct se_portal_group *se_tpg)
34{
35	/*
36	 * This Tag is used when forming SCSI Name identifier in EVPD=1 0x83
37	 * to represent the SCSI Target Port.
38	 */
39	return remote_tpg(se_tpg)->remote_tpgt;
40}
41
42static int tcm_remote_dummy_cmd_fn(struct se_cmd *se_cmd)
43{
44	return 0;
45}
46
47static void tcm_remote_dummy_cmd_void_fn(struct se_cmd *se_cmd)
48{
49
50}
51
52static char *tcm_remote_dump_proto_id(struct tcm_remote_hba *remote_hba)
53{
54	switch (remote_hba->remote_proto_id) {
55	case SCSI_PROTOCOL_SAS:
56		return "SAS";
57	case SCSI_PROTOCOL_SRP:
58		return "SRP";
59	case SCSI_PROTOCOL_FCP:
60		return "FCP";
61	case SCSI_PROTOCOL_ISCSI:
62		return "iSCSI";
63	default:
64		break;
65	}
66
67	return "Unknown";
68}
69
70static int tcm_remote_port_link(
71	struct se_portal_group *se_tpg,
72	struct se_lun *lun)
73{
74	pr_debug("TCM_Remote_ConfigFS: Port Link LUN %lld Successful\n",
75		 lun->unpacked_lun);
76	return 0;
77}
78
79static void tcm_remote_port_unlink(
80	struct se_portal_group *se_tpg,
81	struct se_lun *lun)
82{
83	pr_debug("TCM_Remote_ConfigFS: Port Unlink LUN %lld Successful\n",
84		 lun->unpacked_lun);
85}
86
87static struct se_portal_group *tcm_remote_make_tpg(
88	struct se_wwn *wwn,
89	const char *name)
90{
91	struct tcm_remote_hba *remote_hba = container_of(wwn,
92			struct tcm_remote_hba, remote_hba_wwn);
93	struct tcm_remote_tpg *remote_tpg;
94	unsigned long tpgt;
95	int ret;
96
97	if (strstr(name, "tpgt_") != name) {
98		pr_err("Unable to locate \"tpgt_#\" directory group\n");
99		return ERR_PTR(-EINVAL);
100	}
101	if (kstrtoul(name + 5, 10, &tpgt))
102		return ERR_PTR(-EINVAL);
103
104	if (tpgt >= TL_TPGS_PER_HBA) {
105		pr_err("Passed tpgt: %lu exceeds TL_TPGS_PER_HBA: %u\n",
106		       tpgt, TL_TPGS_PER_HBA);
107		return ERR_PTR(-EINVAL);
108	}
109	remote_tpg = &remote_hba->remote_hba_tpgs[tpgt];
110	remote_tpg->remote_hba = remote_hba;
111	remote_tpg->remote_tpgt = tpgt;
112	/*
113	 * Register the remote_tpg as a emulated TCM Target Endpoint
114	 */
115	ret = core_tpg_register(wwn, &remote_tpg->remote_se_tpg,
116				remote_hba->remote_proto_id);
117	if (ret < 0)
118		return ERR_PTR(-ENOMEM);
119
120	pr_debug("TCM_Remote_ConfigFS: Allocated Emulated %s Target Port %s,t,0x%04lx\n",
121		 tcm_remote_dump_proto_id(remote_hba),
122		 config_item_name(&wwn->wwn_group.cg_item), tpgt);
123	return &remote_tpg->remote_se_tpg;
124}
125
126static void tcm_remote_drop_tpg(struct se_portal_group *se_tpg)
127{
128	struct se_wwn *wwn = se_tpg->se_tpg_wwn;
129	struct tcm_remote_tpg *remote_tpg = container_of(se_tpg,
130				struct tcm_remote_tpg, remote_se_tpg);
131	struct tcm_remote_hba *remote_hba;
132	unsigned short tpgt;
133
134	remote_hba = remote_tpg->remote_hba;
135	tpgt = remote_tpg->remote_tpgt;
136
137	/*
138	 * Deregister the remote_tpg as a emulated TCM Target Endpoint
139	 */
140	core_tpg_deregister(se_tpg);
141
142	remote_tpg->remote_hba = NULL;
143	remote_tpg->remote_tpgt = 0;
144
145	pr_debug("TCM_Remote_ConfigFS: Deallocated Emulated %s Target Port %s,t,0x%04x\n",
146		 tcm_remote_dump_proto_id(remote_hba),
147		 config_item_name(&wwn->wwn_group.cg_item), tpgt);
148}
149
150static struct se_wwn *tcm_remote_make_wwn(
151	struct target_fabric_configfs *tf,
152	struct config_group *group,
153	const char *name)
154{
155	struct tcm_remote_hba *remote_hba;
156	char *ptr;
157	int ret, off = 0;
158
159	remote_hba = kzalloc(sizeof(*remote_hba), GFP_KERNEL);
160	if (!remote_hba)
161		return ERR_PTR(-ENOMEM);
162
163	/*
164	 * Determine the emulated Protocol Identifier and Target Port Name
165	 * based on the incoming configfs directory name.
166	 */
167	ptr = strstr(name, "naa.");
168	if (ptr) {
169		remote_hba->remote_proto_id = SCSI_PROTOCOL_SAS;
170		goto check_len;
171	}
172	ptr = strstr(name, "fc.");
173	if (ptr) {
174		remote_hba->remote_proto_id = SCSI_PROTOCOL_FCP;
175		off = 3; /* Skip over "fc." */
176		goto check_len;
177	}
178	ptr = strstr(name, "0x");
179	if (ptr) {
180		remote_hba->remote_proto_id = SCSI_PROTOCOL_SRP;
181		off = 2; /* Skip over "0x" */
182		goto check_len;
183	}
184	ptr = strstr(name, "iqn.");
185	if (!ptr) {
186		pr_err("Unable to locate prefix for emulated Target Port: %s\n",
187		       name);
188		ret = -EINVAL;
189		goto out;
190	}
191	remote_hba->remote_proto_id = SCSI_PROTOCOL_ISCSI;
192
193check_len:
194	if (strlen(name) >= TL_WWN_ADDR_LEN) {
195		pr_err("Emulated NAA %s Address: %s, exceeds max: %d\n",
196		       name, tcm_remote_dump_proto_id(remote_hba), TL_WWN_ADDR_LEN);
197		ret = -EINVAL;
198		goto out;
199	}
200	snprintf(&remote_hba->remote_wwn_address[0], TL_WWN_ADDR_LEN, "%s", &name[off]);
201
202	pr_debug("TCM_Remote_ConfigFS: Allocated emulated Target %s Address: %s\n",
203		 tcm_remote_dump_proto_id(remote_hba), name);
204	return &remote_hba->remote_hba_wwn;
205out:
206	kfree(remote_hba);
207	return ERR_PTR(ret);
208}
209
210static void tcm_remote_drop_wwn(struct se_wwn *wwn)
211{
212	struct tcm_remote_hba *remote_hba = container_of(wwn,
213				struct tcm_remote_hba, remote_hba_wwn);
214
215	pr_debug("TCM_Remote_ConfigFS: Deallocating emulated Target %s Address: %s\n",
216		 tcm_remote_dump_proto_id(remote_hba),
217		 remote_hba->remote_wwn_address);
218	kfree(remote_hba);
219}
220
221static ssize_t tcm_remote_wwn_version_show(struct config_item *item, char *page)
222{
223	return sprintf(page, "TCM Remote Fabric module %s\n", TCM_REMOTE_VERSION);
224}
225
226CONFIGFS_ATTR_RO(tcm_remote_wwn_, version);
227
228static struct configfs_attribute *tcm_remote_wwn_attrs[] = {
229	&tcm_remote_wwn_attr_version,
230	NULL,
231};
232
233static const struct target_core_fabric_ops remote_ops = {
234	.module				= THIS_MODULE,
235	.fabric_name			= "remote",
236	.tpg_get_wwn			= tcm_remote_get_endpoint_wwn,
237	.tpg_get_tag			= tcm_remote_get_tag,
238	.check_stop_free		= tcm_remote_dummy_cmd_fn,
239	.release_cmd			= tcm_remote_dummy_cmd_void_fn,
240	.write_pending			= tcm_remote_dummy_cmd_fn,
241	.queue_data_in			= tcm_remote_dummy_cmd_fn,
242	.queue_status			= tcm_remote_dummy_cmd_fn,
243	.queue_tm_rsp			= tcm_remote_dummy_cmd_void_fn,
244	.aborted_task			= tcm_remote_dummy_cmd_void_fn,
245	.fabric_make_wwn		= tcm_remote_make_wwn,
246	.fabric_drop_wwn		= tcm_remote_drop_wwn,
247	.fabric_make_tpg		= tcm_remote_make_tpg,
248	.fabric_drop_tpg		= tcm_remote_drop_tpg,
249	.fabric_post_link		= tcm_remote_port_link,
250	.fabric_pre_unlink		= tcm_remote_port_unlink,
251	.tfc_wwn_attrs			= tcm_remote_wwn_attrs,
252};
253
254static int __init tcm_remote_fabric_init(void)
255{
256	return target_register_template(&remote_ops);
257}
258
259static void __exit tcm_remote_fabric_exit(void)
260{
261	target_unregister_template(&remote_ops);
262}
263
264MODULE_DESCRIPTION("TCM virtual remote target");
265MODULE_AUTHOR("Dmitry Bogdanov <d.bogdanov@yadro.com>");
266MODULE_LICENSE("GPL");
267module_init(tcm_remote_fabric_init);
268module_exit(tcm_remote_fabric_exit);
269