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