GNU Linux-libre 6.8.7-gnu
[releases.git] / drivers / target / tcm_remote / tcm_remote.c
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(-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
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(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
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
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
254 static int __init tcm_remote_fabric_init(void)
255 {
256         return target_register_template(&remote_ops);
257 }
258
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);