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 | |
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 | |
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 | |
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 | |
42 | static int tcm_remote_dummy_cmd_fn(struct se_cmd *se_cmd) |
43 | { |
44 | return 0; |
45 | } |
46 | |
47 | static void tcm_remote_dummy_cmd_void_fn(struct se_cmd *se_cmd) |
48 | { |
49 | |
50 | } |
51 | |
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 | |
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 | |
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 | |
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(error: -EINVAL); |
100 | } |
101 | if (kstrtoul(s: name + 5, base: 10, res: &tpgt)) |
102 | return ERR_PTR(error: -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(error: -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(error: -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 | |
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 | |
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(size: sizeof(*remote_hba), GFP_KERNEL); |
160 | if (!remote_hba) |
161 | return ERR_PTR(error: -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(buf: &remote_hba->remote_wwn_address[0], TL_WWN_ADDR_LEN, fmt: "%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(objp: remote_hba); |
207 | return ERR_PTR(error: ret); |
208 | } |
209 | |
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(objp: remote_hba); |
219 | } |
220 | |
221 | static ssize_t tcm_remote_wwn_version_show(struct config_item *item, char *page) |
222 | { |
223 | return sprintf(buf: page, fmt: "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 | |
254 | static int __init tcm_remote_fabric_init(void) |
255 | { |
256 | return target_register_template(fo: &remote_ops); |
257 | } |
258 | |
259 | static void __exit tcm_remote_fabric_exit(void) |
260 | { |
261 | target_unregister_template(fo: &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 | |