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 
remote_tpg(struct se_portal_group * se_tpg)20 static 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 
tcm_remote_get_endpoint_wwn(struct se_portal_group * se_tpg)25 static 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 
tcm_remote_get_tag(struct se_portal_group * se_tpg)33 static 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 
tcm_remote_dummy_cmd_fn(struct se_cmd * se_cmd)42 static int tcm_remote_dummy_cmd_fn(struct se_cmd *se_cmd)
43 {
44 	return 0;
45 }
46 
tcm_remote_dummy_cmd_void_fn(struct se_cmd * se_cmd)47 static void tcm_remote_dummy_cmd_void_fn(struct se_cmd *se_cmd)
48 {
49 
50 }
51 
tcm_remote_dump_proto_id(struct tcm_remote_hba * remote_hba)52 static 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 
tcm_remote_port_link(struct se_portal_group * se_tpg,struct se_lun * lun)70 static 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 
tcm_remote_port_unlink(struct se_portal_group * se_tpg,struct se_lun * lun)79 static 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 
tcm_remote_make_tpg(struct se_wwn * wwn,const char * name)87 static 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 
tcm_remote_drop_tpg(struct se_portal_group * se_tpg)126 static 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 
tcm_remote_make_wwn(struct target_fabric_configfs * tf,struct config_group * group,const char * name)150 static 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 
193 check_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;
205 out:
206 	kfree(remote_hba);
207 	return ERR_PTR(ret);
208 }
209 
tcm_remote_drop_wwn(struct se_wwn * wwn)210 static 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 
tcm_remote_wwn_version_show(struct config_item * item,char * page)221 static 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 
226 CONFIGFS_ATTR_RO(tcm_remote_wwn_, version);
227 
228 static struct configfs_attribute *tcm_remote_wwn_attrs[] = {
229 	&tcm_remote_wwn_attr_version,
230 	NULL,
231 };
232 
233 static 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 
tcm_remote_fabric_init(void)254 static int __init tcm_remote_fabric_init(void)
255 {
256 	return target_register_template(&remote_ops);
257 }
258 
tcm_remote_fabric_exit(void)259 static void __exit tcm_remote_fabric_exit(void)
260 {
261 	target_unregister_template(&remote_ops);
262 }
263 
264 MODULE_DESCRIPTION("TCM virtual remote target");
265 MODULE_AUTHOR("Dmitry Bogdanov <d.bogdanov@yadro.com>");
266 MODULE_LICENSE("GPL");
267 module_init(tcm_remote_fabric_init);
268 module_exit(tcm_remote_fabric_exit);
269