1 | /******************************************************************************* |
2 | * |
3 | * This file contains the Linux/SCSI LLD virtual SCSI initiator driver |
4 | * for emulated SAS initiator ports |
5 | * |
6 | * © Copyright 2011-2013 Datera, Inc. |
7 | * |
8 | * Licensed to the Linux Foundation under the General Public License (GPL) version 2. |
9 | * |
10 | * Author: Nicholas A. Bellinger <nab@risingtidesystems.com> |
11 | * |
12 | * This program is free software; you can redistribute it and/or modify |
13 | * it under the terms of the GNU General Public License as published by |
14 | * the Free Software Foundation; either version 2 of the License, or |
15 | * (at your option) any later version. |
16 | * |
17 | * This program is distributed in the hope that it will be useful, |
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
20 | * GNU General Public License for more details. |
21 | ****************************************************************************/ |
22 | |
23 | #include <linux/module.h> |
24 | #include <linux/moduleparam.h> |
25 | #include <linux/init.h> |
26 | #include <linux/slab.h> |
27 | #include <linux/types.h> |
28 | #include <linux/configfs.h> |
29 | #include <scsi/scsi.h> |
30 | #include <scsi/scsi_tcq.h> |
31 | #include <scsi/scsi_host.h> |
32 | #include <scsi/scsi_device.h> |
33 | #include <scsi/scsi_cmnd.h> |
34 | |
35 | #include <target/target_core_base.h> |
36 | #include <target/target_core_fabric.h> |
37 | |
38 | #include "tcm_loop.h" |
39 | |
40 | #define to_tcm_loop_hba(hba) container_of(hba, struct tcm_loop_hba, dev) |
41 | |
42 | static struct kmem_cache *tcm_loop_cmd_cache; |
43 | |
44 | static int tcm_loop_hba_no_cnt; |
45 | |
46 | static int tcm_loop_queue_status(struct se_cmd *se_cmd); |
47 | |
48 | static unsigned int tcm_loop_nr_hw_queues = 1; |
49 | module_param_named(nr_hw_queues, tcm_loop_nr_hw_queues, uint, 0644); |
50 | |
51 | static unsigned int tcm_loop_can_queue = 1024; |
52 | module_param_named(can_queue, tcm_loop_can_queue, uint, 0644); |
53 | |
54 | static unsigned int tcm_loop_cmd_per_lun = 1024; |
55 | module_param_named(cmd_per_lun, tcm_loop_cmd_per_lun, uint, 0644); |
56 | |
57 | /* |
58 | * Called from struct target_core_fabric_ops->check_stop_free() |
59 | */ |
60 | static int tcm_loop_check_stop_free(struct se_cmd *se_cmd) |
61 | { |
62 | return transport_generic_free_cmd(se_cmd, 0); |
63 | } |
64 | |
65 | static void tcm_loop_release_cmd(struct se_cmd *se_cmd) |
66 | { |
67 | struct tcm_loop_cmd *tl_cmd = container_of(se_cmd, |
68 | struct tcm_loop_cmd, tl_se_cmd); |
69 | struct scsi_cmnd *sc = tl_cmd->sc; |
70 | |
71 | if (se_cmd->se_cmd_flags & SCF_SCSI_TMR_CDB) |
72 | kmem_cache_free(s: tcm_loop_cmd_cache, objp: tl_cmd); |
73 | else |
74 | scsi_done(cmd: sc); |
75 | } |
76 | |
77 | static int tcm_loop_show_info(struct seq_file *m, struct Scsi_Host *host) |
78 | { |
79 | seq_puts(m, s: "tcm_loop_proc_info()\n" ); |
80 | return 0; |
81 | } |
82 | |
83 | static int tcm_loop_driver_probe(struct device *); |
84 | static void tcm_loop_driver_remove(struct device *); |
85 | |
86 | static struct bus_type tcm_loop_lld_bus = { |
87 | .name = "tcm_loop_bus" , |
88 | .probe = tcm_loop_driver_probe, |
89 | .remove = tcm_loop_driver_remove, |
90 | }; |
91 | |
92 | static struct device_driver tcm_loop_driverfs = { |
93 | .name = "tcm_loop" , |
94 | .bus = &tcm_loop_lld_bus, |
95 | }; |
96 | /* |
97 | * Used with root_device_register() in tcm_loop_alloc_core_bus() below |
98 | */ |
99 | static struct device *tcm_loop_primary; |
100 | |
101 | static void tcm_loop_target_queue_cmd(struct tcm_loop_cmd *tl_cmd) |
102 | { |
103 | struct se_cmd *se_cmd = &tl_cmd->tl_se_cmd; |
104 | struct scsi_cmnd *sc = tl_cmd->sc; |
105 | struct tcm_loop_nexus *tl_nexus; |
106 | struct tcm_loop_hba *tl_hba; |
107 | struct tcm_loop_tpg *tl_tpg; |
108 | struct scatterlist *sgl_bidi = NULL; |
109 | u32 sgl_bidi_count = 0, transfer_length; |
110 | |
111 | tl_hba = *(struct tcm_loop_hba **)shost_priv(shost: sc->device->host); |
112 | tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id]; |
113 | |
114 | /* |
115 | * Ensure that this tl_tpg reference from the incoming sc->device->id |
116 | * has already been configured via tcm_loop_make_naa_tpg(). |
117 | */ |
118 | if (!tl_tpg->tl_hba) { |
119 | set_host_byte(cmd: sc, status: DID_NO_CONNECT); |
120 | goto out_done; |
121 | } |
122 | if (tl_tpg->tl_transport_status == TCM_TRANSPORT_OFFLINE) { |
123 | set_host_byte(cmd: sc, status: DID_TRANSPORT_DISRUPTED); |
124 | goto out_done; |
125 | } |
126 | tl_nexus = tl_tpg->tl_nexus; |
127 | if (!tl_nexus) { |
128 | scmd_printk(KERN_ERR, sc, |
129 | "TCM_Loop I_T Nexus does not exist\n" ); |
130 | set_host_byte(cmd: sc, status: DID_ERROR); |
131 | goto out_done; |
132 | } |
133 | |
134 | transfer_length = scsi_transfer_length(scmd: sc); |
135 | if (!scsi_prot_sg_count(cmd: sc) && |
136 | scsi_get_prot_op(scmd: sc) != SCSI_PROT_NORMAL) { |
137 | se_cmd->prot_pto = true; |
138 | /* |
139 | * loopback transport doesn't support |
140 | * WRITE_GENERATE, READ_STRIP protection |
141 | * information operations, go ahead unprotected. |
142 | */ |
143 | transfer_length = scsi_bufflen(cmd: sc); |
144 | } |
145 | |
146 | se_cmd->tag = tl_cmd->sc_cmd_tag; |
147 | target_init_cmd(se_cmd, se_sess: tl_nexus->se_sess, sense: &tl_cmd->tl_sense_buf[0], |
148 | unpacked_lun: tl_cmd->sc->device->lun, data_length: transfer_length, |
149 | TCM_SIMPLE_TAG, data_dir: sc->sc_data_direction, flags: 0); |
150 | |
151 | if (target_submit_prep(se_cmd, cdb: sc->cmnd, sgl: scsi_sglist(cmd: sc), |
152 | sgl_count: scsi_sg_count(cmd: sc), sgl_bidi, sgl_bidi_count, |
153 | sgl_prot: scsi_prot_sglist(cmd: sc), sgl_prot_count: scsi_prot_sg_count(cmd: sc), |
154 | GFP_ATOMIC)) |
155 | return; |
156 | |
157 | target_submit(se_cmd); |
158 | return; |
159 | |
160 | out_done: |
161 | scsi_done(cmd: sc); |
162 | } |
163 | |
164 | /* |
165 | * ->queuecommand can be and usually is called from interrupt context, so |
166 | * defer the actual submission to a workqueue. |
167 | */ |
168 | static int tcm_loop_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *sc) |
169 | { |
170 | struct tcm_loop_cmd *tl_cmd = scsi_cmd_priv(cmd: sc); |
171 | |
172 | pr_debug("%s() %d:%d:%d:%llu got CDB: 0x%02x scsi_buf_len: %u\n" , |
173 | __func__, sc->device->host->host_no, sc->device->id, |
174 | sc->device->channel, sc->device->lun, sc->cmnd[0], |
175 | scsi_bufflen(sc)); |
176 | |
177 | memset(tl_cmd, 0, sizeof(*tl_cmd)); |
178 | tl_cmd->sc = sc; |
179 | tl_cmd->sc_cmd_tag = scsi_cmd_to_rq(scmd: sc)->tag; |
180 | |
181 | tcm_loop_target_queue_cmd(tl_cmd); |
182 | return 0; |
183 | } |
184 | |
185 | /* |
186 | * Called from SCSI EH process context to issue a LUN_RESET TMR |
187 | * to struct scsi_device |
188 | */ |
189 | static int tcm_loop_issue_tmr(struct tcm_loop_tpg *tl_tpg, |
190 | u64 lun, int task, enum tcm_tmreq_table tmr) |
191 | { |
192 | struct se_cmd *se_cmd; |
193 | struct se_session *se_sess; |
194 | struct tcm_loop_nexus *tl_nexus; |
195 | struct tcm_loop_cmd *tl_cmd; |
196 | int ret = TMR_FUNCTION_FAILED, rc; |
197 | |
198 | /* |
199 | * Locate the tl_nexus and se_sess pointers |
200 | */ |
201 | tl_nexus = tl_tpg->tl_nexus; |
202 | if (!tl_nexus) { |
203 | pr_err("Unable to perform device reset without active I_T Nexus\n" ); |
204 | return ret; |
205 | } |
206 | |
207 | tl_cmd = kmem_cache_zalloc(k: tcm_loop_cmd_cache, GFP_KERNEL); |
208 | if (!tl_cmd) |
209 | return ret; |
210 | |
211 | init_completion(x: &tl_cmd->tmr_done); |
212 | |
213 | se_cmd = &tl_cmd->tl_se_cmd; |
214 | se_sess = tl_tpg->tl_nexus->se_sess; |
215 | |
216 | rc = target_submit_tmr(se_cmd, se_sess, sense: tl_cmd->tl_sense_buf, unpacked_lun: lun, |
217 | NULL, tm_type: tmr, GFP_KERNEL, task, |
218 | TARGET_SCF_ACK_KREF); |
219 | if (rc < 0) |
220 | goto release; |
221 | wait_for_completion(&tl_cmd->tmr_done); |
222 | ret = se_cmd->se_tmr_req->response; |
223 | target_put_sess_cmd(se_cmd); |
224 | |
225 | out: |
226 | return ret; |
227 | |
228 | release: |
229 | kmem_cache_free(s: tcm_loop_cmd_cache, objp: tl_cmd); |
230 | goto out; |
231 | } |
232 | |
233 | static int tcm_loop_abort_task(struct scsi_cmnd *sc) |
234 | { |
235 | struct tcm_loop_hba *tl_hba; |
236 | struct tcm_loop_tpg *tl_tpg; |
237 | int ret; |
238 | |
239 | /* |
240 | * Locate the tcm_loop_hba_t pointer |
241 | */ |
242 | tl_hba = *(struct tcm_loop_hba **)shost_priv(shost: sc->device->host); |
243 | tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id]; |
244 | ret = tcm_loop_issue_tmr(tl_tpg, lun: sc->device->lun, |
245 | task: scsi_cmd_to_rq(scmd: sc)->tag, tmr: TMR_ABORT_TASK); |
246 | return (ret == TMR_FUNCTION_COMPLETE) ? SUCCESS : FAILED; |
247 | } |
248 | |
249 | /* |
250 | * Called from SCSI EH process context to issue a LUN_RESET TMR |
251 | * to struct scsi_device |
252 | */ |
253 | static int tcm_loop_device_reset(struct scsi_cmnd *sc) |
254 | { |
255 | struct tcm_loop_hba *tl_hba; |
256 | struct tcm_loop_tpg *tl_tpg; |
257 | int ret; |
258 | |
259 | /* |
260 | * Locate the tcm_loop_hba_t pointer |
261 | */ |
262 | tl_hba = *(struct tcm_loop_hba **)shost_priv(shost: sc->device->host); |
263 | tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id]; |
264 | |
265 | ret = tcm_loop_issue_tmr(tl_tpg, lun: sc->device->lun, |
266 | task: 0, tmr: TMR_LUN_RESET); |
267 | return (ret == TMR_FUNCTION_COMPLETE) ? SUCCESS : FAILED; |
268 | } |
269 | |
270 | static int tcm_loop_target_reset(struct scsi_cmnd *sc) |
271 | { |
272 | struct tcm_loop_hba *tl_hba; |
273 | struct tcm_loop_tpg *tl_tpg; |
274 | |
275 | /* |
276 | * Locate the tcm_loop_hba_t pointer |
277 | */ |
278 | tl_hba = *(struct tcm_loop_hba **)shost_priv(shost: sc->device->host); |
279 | if (!tl_hba) { |
280 | pr_err("Unable to perform device reset without active I_T Nexus\n" ); |
281 | return FAILED; |
282 | } |
283 | /* |
284 | * Locate the tl_tpg pointer from TargetID in sc->device->id |
285 | */ |
286 | tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id]; |
287 | if (tl_tpg) { |
288 | tl_tpg->tl_transport_status = TCM_TRANSPORT_ONLINE; |
289 | return SUCCESS; |
290 | } |
291 | return FAILED; |
292 | } |
293 | |
294 | static const struct scsi_host_template tcm_loop_driver_template = { |
295 | .show_info = tcm_loop_show_info, |
296 | .proc_name = "tcm_loopback" , |
297 | .name = "TCM_Loopback" , |
298 | .queuecommand = tcm_loop_queuecommand, |
299 | .change_queue_depth = scsi_change_queue_depth, |
300 | .eh_abort_handler = tcm_loop_abort_task, |
301 | .eh_device_reset_handler = tcm_loop_device_reset, |
302 | .eh_target_reset_handler = tcm_loop_target_reset, |
303 | .this_id = -1, |
304 | .sg_tablesize = 256, |
305 | .max_sectors = 0xFFFF, |
306 | .dma_boundary = PAGE_SIZE - 1, |
307 | .module = THIS_MODULE, |
308 | .track_queue_depth = 1, |
309 | .cmd_size = sizeof(struct tcm_loop_cmd), |
310 | }; |
311 | |
312 | static int tcm_loop_driver_probe(struct device *dev) |
313 | { |
314 | struct tcm_loop_hba *tl_hba; |
315 | struct Scsi_Host *sh; |
316 | int error, host_prot; |
317 | |
318 | tl_hba = to_tcm_loop_hba(dev); |
319 | |
320 | sh = scsi_host_alloc(&tcm_loop_driver_template, |
321 | sizeof(struct tcm_loop_hba)); |
322 | if (!sh) { |
323 | pr_err("Unable to allocate struct scsi_host\n" ); |
324 | return -ENODEV; |
325 | } |
326 | tl_hba->sh = sh; |
327 | |
328 | /* |
329 | * Assign the struct tcm_loop_hba pointer to struct Scsi_Host->hostdata |
330 | */ |
331 | *((struct tcm_loop_hba **)sh->hostdata) = tl_hba; |
332 | /* |
333 | * Setup single ID, Channel and LUN for now.. |
334 | */ |
335 | sh->max_id = 2; |
336 | sh->max_lun = 0; |
337 | sh->max_channel = 0; |
338 | sh->max_cmd_len = SCSI_MAX_VARLEN_CDB_SIZE; |
339 | sh->nr_hw_queues = tcm_loop_nr_hw_queues; |
340 | sh->can_queue = tcm_loop_can_queue; |
341 | sh->cmd_per_lun = tcm_loop_cmd_per_lun; |
342 | |
343 | host_prot = SHOST_DIF_TYPE1_PROTECTION | SHOST_DIF_TYPE2_PROTECTION | |
344 | SHOST_DIF_TYPE3_PROTECTION | SHOST_DIX_TYPE1_PROTECTION | |
345 | SHOST_DIX_TYPE2_PROTECTION | SHOST_DIX_TYPE3_PROTECTION; |
346 | |
347 | scsi_host_set_prot(shost: sh, mask: host_prot); |
348 | scsi_host_set_guard(shost: sh, type: SHOST_DIX_GUARD_CRC); |
349 | |
350 | error = scsi_add_host(host: sh, dev: &tl_hba->dev); |
351 | if (error) { |
352 | pr_err("%s: scsi_add_host failed\n" , __func__); |
353 | scsi_host_put(t: sh); |
354 | return -ENODEV; |
355 | } |
356 | return 0; |
357 | } |
358 | |
359 | static void tcm_loop_driver_remove(struct device *dev) |
360 | { |
361 | struct tcm_loop_hba *tl_hba; |
362 | struct Scsi_Host *sh; |
363 | |
364 | tl_hba = to_tcm_loop_hba(dev); |
365 | sh = tl_hba->sh; |
366 | |
367 | scsi_remove_host(sh); |
368 | scsi_host_put(t: sh); |
369 | } |
370 | |
371 | static void tcm_loop_release_adapter(struct device *dev) |
372 | { |
373 | struct tcm_loop_hba *tl_hba = to_tcm_loop_hba(dev); |
374 | |
375 | kfree(objp: tl_hba); |
376 | } |
377 | |
378 | /* |
379 | * Called from tcm_loop_make_scsi_hba() in tcm_loop_configfs.c |
380 | */ |
381 | static int tcm_loop_setup_hba_bus(struct tcm_loop_hba *tl_hba, int tcm_loop_host_id) |
382 | { |
383 | int ret; |
384 | |
385 | tl_hba->dev.bus = &tcm_loop_lld_bus; |
386 | tl_hba->dev.parent = tcm_loop_primary; |
387 | tl_hba->dev.release = &tcm_loop_release_adapter; |
388 | dev_set_name(dev: &tl_hba->dev, name: "tcm_loop_adapter_%d" , tcm_loop_host_id); |
389 | |
390 | ret = device_register(dev: &tl_hba->dev); |
391 | if (ret) { |
392 | pr_err("device_register() failed for tl_hba->dev: %d\n" , ret); |
393 | put_device(dev: &tl_hba->dev); |
394 | return -ENODEV; |
395 | } |
396 | |
397 | return 0; |
398 | } |
399 | |
400 | /* |
401 | * Called from tcm_loop_fabric_init() in tcl_loop_fabric.c to load the emulated |
402 | * tcm_loop SCSI bus. |
403 | */ |
404 | static int tcm_loop_alloc_core_bus(void) |
405 | { |
406 | int ret; |
407 | |
408 | tcm_loop_primary = root_device_register("tcm_loop_0" ); |
409 | if (IS_ERR(ptr: tcm_loop_primary)) { |
410 | pr_err("Unable to allocate tcm_loop_primary\n" ); |
411 | return PTR_ERR(ptr: tcm_loop_primary); |
412 | } |
413 | |
414 | ret = bus_register(bus: &tcm_loop_lld_bus); |
415 | if (ret) { |
416 | pr_err("bus_register() failed for tcm_loop_lld_bus\n" ); |
417 | goto dev_unreg; |
418 | } |
419 | |
420 | ret = driver_register(drv: &tcm_loop_driverfs); |
421 | if (ret) { |
422 | pr_err("driver_register() failed for tcm_loop_driverfs\n" ); |
423 | goto bus_unreg; |
424 | } |
425 | |
426 | pr_debug("Initialized TCM Loop Core Bus\n" ); |
427 | return ret; |
428 | |
429 | bus_unreg: |
430 | bus_unregister(bus: &tcm_loop_lld_bus); |
431 | dev_unreg: |
432 | root_device_unregister(root: tcm_loop_primary); |
433 | return ret; |
434 | } |
435 | |
436 | static void tcm_loop_release_core_bus(void) |
437 | { |
438 | driver_unregister(drv: &tcm_loop_driverfs); |
439 | bus_unregister(bus: &tcm_loop_lld_bus); |
440 | root_device_unregister(root: tcm_loop_primary); |
441 | |
442 | pr_debug("Releasing TCM Loop Core BUS\n" ); |
443 | } |
444 | |
445 | static inline struct tcm_loop_tpg *tl_tpg(struct se_portal_group *se_tpg) |
446 | { |
447 | return container_of(se_tpg, struct tcm_loop_tpg, tl_se_tpg); |
448 | } |
449 | |
450 | static char *tcm_loop_get_endpoint_wwn(struct se_portal_group *se_tpg) |
451 | { |
452 | /* |
453 | * Return the passed NAA identifier for the Target Port |
454 | */ |
455 | return &tl_tpg(se_tpg)->tl_hba->tl_wwn_address[0]; |
456 | } |
457 | |
458 | static u16 tcm_loop_get_tag(struct se_portal_group *se_tpg) |
459 | { |
460 | /* |
461 | * This Tag is used when forming SCSI Name identifier in EVPD=1 0x83 |
462 | * to represent the SCSI Target Port. |
463 | */ |
464 | return tl_tpg(se_tpg)->tl_tpgt; |
465 | } |
466 | |
467 | /* |
468 | * Returning (1) here allows for target_core_mod struct se_node_acl to be generated |
469 | * based upon the incoming fabric dependent SCSI Initiator Port |
470 | */ |
471 | static int tcm_loop_check_demo_mode(struct se_portal_group *se_tpg) |
472 | { |
473 | return 1; |
474 | } |
475 | |
476 | static int tcm_loop_check_prot_fabric_only(struct se_portal_group *se_tpg) |
477 | { |
478 | struct tcm_loop_tpg *tl_tpg = container_of(se_tpg, struct tcm_loop_tpg, |
479 | tl_se_tpg); |
480 | return tl_tpg->tl_fabric_prot_type; |
481 | } |
482 | |
483 | static u32 tcm_loop_sess_get_index(struct se_session *se_sess) |
484 | { |
485 | return 1; |
486 | } |
487 | |
488 | static int tcm_loop_get_cmd_state(struct se_cmd *se_cmd) |
489 | { |
490 | struct tcm_loop_cmd *tl_cmd = container_of(se_cmd, |
491 | struct tcm_loop_cmd, tl_se_cmd); |
492 | |
493 | return tl_cmd->sc_cmd_state; |
494 | } |
495 | |
496 | static int tcm_loop_write_pending(struct se_cmd *se_cmd) |
497 | { |
498 | /* |
499 | * Since Linux/SCSI has already sent down a struct scsi_cmnd |
500 | * sc->sc_data_direction of DMA_TO_DEVICE with struct scatterlist array |
501 | * memory, and memory has already been mapped to struct se_cmd->t_mem_list |
502 | * format with transport_generic_map_mem_to_cmd(). |
503 | * |
504 | * We now tell TCM to add this WRITE CDB directly into the TCM storage |
505 | * object execution queue. |
506 | */ |
507 | target_execute_cmd(cmd: se_cmd); |
508 | return 0; |
509 | } |
510 | |
511 | static int tcm_loop_queue_data_or_status(const char *func, |
512 | struct se_cmd *se_cmd, u8 scsi_status) |
513 | { |
514 | struct tcm_loop_cmd *tl_cmd = container_of(se_cmd, |
515 | struct tcm_loop_cmd, tl_se_cmd); |
516 | struct scsi_cmnd *sc = tl_cmd->sc; |
517 | |
518 | pr_debug("%s() called for scsi_cmnd: %p cdb: 0x%02x\n" , |
519 | func, sc, sc->cmnd[0]); |
520 | |
521 | if (se_cmd->sense_buffer && |
522 | ((se_cmd->se_cmd_flags & SCF_TRANSPORT_TASK_SENSE) || |
523 | (se_cmd->se_cmd_flags & SCF_EMULATED_TASK_SENSE))) { |
524 | |
525 | memcpy(sc->sense_buffer, se_cmd->sense_buffer, |
526 | SCSI_SENSE_BUFFERSIZE); |
527 | sc->result = SAM_STAT_CHECK_CONDITION; |
528 | } else |
529 | sc->result = scsi_status; |
530 | |
531 | set_host_byte(cmd: sc, status: DID_OK); |
532 | if ((se_cmd->se_cmd_flags & SCF_OVERFLOW_BIT) || |
533 | (se_cmd->se_cmd_flags & SCF_UNDERFLOW_BIT)) |
534 | scsi_set_resid(cmd: sc, resid: se_cmd->residual_count); |
535 | return 0; |
536 | } |
537 | |
538 | static int tcm_loop_queue_data_in(struct se_cmd *se_cmd) |
539 | { |
540 | return tcm_loop_queue_data_or_status(func: __func__, se_cmd, scsi_status: SAM_STAT_GOOD); |
541 | } |
542 | |
543 | static int tcm_loop_queue_status(struct se_cmd *se_cmd) |
544 | { |
545 | return tcm_loop_queue_data_or_status(func: __func__, |
546 | se_cmd, scsi_status: se_cmd->scsi_status); |
547 | } |
548 | |
549 | static void tcm_loop_queue_tm_rsp(struct se_cmd *se_cmd) |
550 | { |
551 | struct tcm_loop_cmd *tl_cmd = container_of(se_cmd, |
552 | struct tcm_loop_cmd, tl_se_cmd); |
553 | |
554 | /* Wake up tcm_loop_issue_tmr(). */ |
555 | complete(&tl_cmd->tmr_done); |
556 | } |
557 | |
558 | static void tcm_loop_aborted_task(struct se_cmd *se_cmd) |
559 | { |
560 | return; |
561 | } |
562 | |
563 | static char *tcm_loop_dump_proto_id(struct tcm_loop_hba *tl_hba) |
564 | { |
565 | switch (tl_hba->tl_proto_id) { |
566 | case SCSI_PROTOCOL_SAS: |
567 | return "SAS" ; |
568 | case SCSI_PROTOCOL_FCP: |
569 | return "FCP" ; |
570 | case SCSI_PROTOCOL_ISCSI: |
571 | return "iSCSI" ; |
572 | default: |
573 | break; |
574 | } |
575 | |
576 | return "Unknown" ; |
577 | } |
578 | |
579 | /* Start items for tcm_loop_port_cit */ |
580 | |
581 | static int tcm_loop_port_link( |
582 | struct se_portal_group *se_tpg, |
583 | struct se_lun *lun) |
584 | { |
585 | struct tcm_loop_tpg *tl_tpg = container_of(se_tpg, |
586 | struct tcm_loop_tpg, tl_se_tpg); |
587 | struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba; |
588 | |
589 | atomic_inc_mb(v: &tl_tpg->tl_tpg_port_count); |
590 | /* |
591 | * Add Linux/SCSI struct scsi_device by HCTL |
592 | */ |
593 | scsi_add_device(host: tl_hba->sh, channel: 0, target: tl_tpg->tl_tpgt, lun: lun->unpacked_lun); |
594 | |
595 | pr_debug("TCM_Loop_ConfigFS: Port Link Successful\n" ); |
596 | return 0; |
597 | } |
598 | |
599 | static void tcm_loop_port_unlink( |
600 | struct se_portal_group *se_tpg, |
601 | struct se_lun *se_lun) |
602 | { |
603 | struct scsi_device *sd; |
604 | struct tcm_loop_hba *tl_hba; |
605 | struct tcm_loop_tpg *tl_tpg; |
606 | |
607 | tl_tpg = container_of(se_tpg, struct tcm_loop_tpg, tl_se_tpg); |
608 | tl_hba = tl_tpg->tl_hba; |
609 | |
610 | sd = scsi_device_lookup(tl_hba->sh, 0, tl_tpg->tl_tpgt, |
611 | se_lun->unpacked_lun); |
612 | if (!sd) { |
613 | pr_err("Unable to locate struct scsi_device for %d:%d:%llu\n" , |
614 | 0, tl_tpg->tl_tpgt, se_lun->unpacked_lun); |
615 | return; |
616 | } |
617 | /* |
618 | * Remove Linux/SCSI struct scsi_device by HCTL |
619 | */ |
620 | scsi_remove_device(sd); |
621 | scsi_device_put(sd); |
622 | |
623 | atomic_dec_mb(v: &tl_tpg->tl_tpg_port_count); |
624 | |
625 | pr_debug("TCM_Loop_ConfigFS: Port Unlink Successful\n" ); |
626 | } |
627 | |
628 | /* End items for tcm_loop_port_cit */ |
629 | |
630 | static ssize_t tcm_loop_tpg_attrib_fabric_prot_type_show( |
631 | struct config_item *item, char *page) |
632 | { |
633 | struct se_portal_group *se_tpg = attrib_to_tpg(item); |
634 | struct tcm_loop_tpg *tl_tpg = container_of(se_tpg, struct tcm_loop_tpg, |
635 | tl_se_tpg); |
636 | |
637 | return sprintf(buf: page, fmt: "%d\n" , tl_tpg->tl_fabric_prot_type); |
638 | } |
639 | |
640 | static ssize_t tcm_loop_tpg_attrib_fabric_prot_type_store( |
641 | struct config_item *item, const char *page, size_t count) |
642 | { |
643 | struct se_portal_group *se_tpg = attrib_to_tpg(item); |
644 | struct tcm_loop_tpg *tl_tpg = container_of(se_tpg, struct tcm_loop_tpg, |
645 | tl_se_tpg); |
646 | unsigned long val; |
647 | int ret = kstrtoul(s: page, base: 0, res: &val); |
648 | |
649 | if (ret) { |
650 | pr_err("kstrtoul() returned %d for fabric_prot_type\n" , ret); |
651 | return ret; |
652 | } |
653 | if (val != 0 && val != 1 && val != 3) { |
654 | pr_err("Invalid qla2xxx fabric_prot_type: %lu\n" , val); |
655 | return -EINVAL; |
656 | } |
657 | tl_tpg->tl_fabric_prot_type = val; |
658 | |
659 | return count; |
660 | } |
661 | |
662 | CONFIGFS_ATTR(tcm_loop_tpg_attrib_, fabric_prot_type); |
663 | |
664 | static struct configfs_attribute *tcm_loop_tpg_attrib_attrs[] = { |
665 | &tcm_loop_tpg_attrib_attr_fabric_prot_type, |
666 | NULL, |
667 | }; |
668 | |
669 | /* Start items for tcm_loop_nexus_cit */ |
670 | |
671 | static int tcm_loop_alloc_sess_cb(struct se_portal_group *se_tpg, |
672 | struct se_session *se_sess, void *p) |
673 | { |
674 | struct tcm_loop_tpg *tl_tpg = container_of(se_tpg, |
675 | struct tcm_loop_tpg, tl_se_tpg); |
676 | |
677 | tl_tpg->tl_nexus = p; |
678 | return 0; |
679 | } |
680 | |
681 | static int tcm_loop_make_nexus( |
682 | struct tcm_loop_tpg *tl_tpg, |
683 | const char *name) |
684 | { |
685 | struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba; |
686 | struct tcm_loop_nexus *tl_nexus; |
687 | int ret; |
688 | |
689 | if (tl_tpg->tl_nexus) { |
690 | pr_debug("tl_tpg->tl_nexus already exists\n" ); |
691 | return -EEXIST; |
692 | } |
693 | |
694 | tl_nexus = kzalloc(size: sizeof(*tl_nexus), GFP_KERNEL); |
695 | if (!tl_nexus) |
696 | return -ENOMEM; |
697 | |
698 | tl_nexus->se_sess = target_setup_session(&tl_tpg->tl_se_tpg, 0, 0, |
699 | prot_op: TARGET_PROT_DIN_PASS | TARGET_PROT_DOUT_PASS, |
700 | name, tl_nexus, callback: tcm_loop_alloc_sess_cb); |
701 | if (IS_ERR(ptr: tl_nexus->se_sess)) { |
702 | ret = PTR_ERR(ptr: tl_nexus->se_sess); |
703 | kfree(objp: tl_nexus); |
704 | return ret; |
705 | } |
706 | |
707 | pr_debug("TCM_Loop_ConfigFS: Established I_T Nexus to emulated %s Initiator Port: %s\n" , |
708 | tcm_loop_dump_proto_id(tl_hba), name); |
709 | return 0; |
710 | } |
711 | |
712 | static int tcm_loop_drop_nexus( |
713 | struct tcm_loop_tpg *tpg) |
714 | { |
715 | struct se_session *se_sess; |
716 | struct tcm_loop_nexus *tl_nexus; |
717 | |
718 | tl_nexus = tpg->tl_nexus; |
719 | if (!tl_nexus) |
720 | return -ENODEV; |
721 | |
722 | se_sess = tl_nexus->se_sess; |
723 | if (!se_sess) |
724 | return -ENODEV; |
725 | |
726 | if (atomic_read(v: &tpg->tl_tpg_port_count)) { |
727 | pr_err("Unable to remove TCM_Loop I_T Nexus with active TPG port count: %d\n" , |
728 | atomic_read(&tpg->tl_tpg_port_count)); |
729 | return -EPERM; |
730 | } |
731 | |
732 | pr_debug("TCM_Loop_ConfigFS: Removing I_T Nexus to emulated %s Initiator Port: %s\n" , |
733 | tcm_loop_dump_proto_id(tpg->tl_hba), |
734 | tl_nexus->se_sess->se_node_acl->initiatorname); |
735 | /* |
736 | * Release the SCSI I_T Nexus to the emulated Target Port |
737 | */ |
738 | target_remove_session(se_sess); |
739 | tpg->tl_nexus = NULL; |
740 | kfree(objp: tl_nexus); |
741 | return 0; |
742 | } |
743 | |
744 | /* End items for tcm_loop_nexus_cit */ |
745 | |
746 | static ssize_t tcm_loop_tpg_nexus_show(struct config_item *item, char *page) |
747 | { |
748 | struct se_portal_group *se_tpg = to_tpg(item); |
749 | struct tcm_loop_tpg *tl_tpg = container_of(se_tpg, |
750 | struct tcm_loop_tpg, tl_se_tpg); |
751 | struct tcm_loop_nexus *tl_nexus; |
752 | ssize_t ret; |
753 | |
754 | tl_nexus = tl_tpg->tl_nexus; |
755 | if (!tl_nexus) |
756 | return -ENODEV; |
757 | |
758 | ret = snprintf(buf: page, PAGE_SIZE, fmt: "%s\n" , |
759 | tl_nexus->se_sess->se_node_acl->initiatorname); |
760 | |
761 | return ret; |
762 | } |
763 | |
764 | static ssize_t tcm_loop_tpg_nexus_store(struct config_item *item, |
765 | const char *page, size_t count) |
766 | { |
767 | struct se_portal_group *se_tpg = to_tpg(item); |
768 | struct tcm_loop_tpg *tl_tpg = container_of(se_tpg, |
769 | struct tcm_loop_tpg, tl_se_tpg); |
770 | struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba; |
771 | unsigned char i_port[TL_WWN_ADDR_LEN], *ptr, *port_ptr; |
772 | int ret; |
773 | /* |
774 | * Shutdown the active I_T nexus if 'NULL' is passed.. |
775 | */ |
776 | if (!strncmp(page, "NULL" , 4)) { |
777 | ret = tcm_loop_drop_nexus(tpg: tl_tpg); |
778 | return (!ret) ? count : ret; |
779 | } |
780 | /* |
781 | * Otherwise make sure the passed virtual Initiator port WWN matches |
782 | * the fabric protocol_id set in tcm_loop_make_scsi_hba(), and call |
783 | * tcm_loop_make_nexus() |
784 | */ |
785 | if (strlen(page) >= TL_WWN_ADDR_LEN) { |
786 | pr_err("Emulated NAA Sas Address: %s, exceeds max: %d\n" , |
787 | page, TL_WWN_ADDR_LEN); |
788 | return -EINVAL; |
789 | } |
790 | snprintf(buf: &i_port[0], TL_WWN_ADDR_LEN, fmt: "%s" , page); |
791 | |
792 | ptr = strstr(i_port, "naa." ); |
793 | if (ptr) { |
794 | if (tl_hba->tl_proto_id != SCSI_PROTOCOL_SAS) { |
795 | pr_err("Passed SAS Initiator Port %s does not match target port protoid: %s\n" , |
796 | i_port, tcm_loop_dump_proto_id(tl_hba)); |
797 | return -EINVAL; |
798 | } |
799 | port_ptr = &i_port[0]; |
800 | goto check_newline; |
801 | } |
802 | ptr = strstr(i_port, "fc." ); |
803 | if (ptr) { |
804 | if (tl_hba->tl_proto_id != SCSI_PROTOCOL_FCP) { |
805 | pr_err("Passed FCP Initiator Port %s does not match target port protoid: %s\n" , |
806 | i_port, tcm_loop_dump_proto_id(tl_hba)); |
807 | return -EINVAL; |
808 | } |
809 | port_ptr = &i_port[3]; /* Skip over "fc." */ |
810 | goto check_newline; |
811 | } |
812 | ptr = strstr(i_port, "iqn." ); |
813 | if (ptr) { |
814 | if (tl_hba->tl_proto_id != SCSI_PROTOCOL_ISCSI) { |
815 | pr_err("Passed iSCSI Initiator Port %s does not match target port protoid: %s\n" , |
816 | i_port, tcm_loop_dump_proto_id(tl_hba)); |
817 | return -EINVAL; |
818 | } |
819 | port_ptr = &i_port[0]; |
820 | goto check_newline; |
821 | } |
822 | pr_err("Unable to locate prefix for emulated Initiator Port: %s\n" , |
823 | i_port); |
824 | return -EINVAL; |
825 | /* |
826 | * Clear any trailing newline for the NAA WWN |
827 | */ |
828 | check_newline: |
829 | if (i_port[strlen(i_port)-1] == '\n') |
830 | i_port[strlen(i_port)-1] = '\0'; |
831 | |
832 | ret = tcm_loop_make_nexus(tl_tpg, name: port_ptr); |
833 | if (ret < 0) |
834 | return ret; |
835 | |
836 | return count; |
837 | } |
838 | |
839 | static ssize_t tcm_loop_tpg_transport_status_show(struct config_item *item, |
840 | char *page) |
841 | { |
842 | struct se_portal_group *se_tpg = to_tpg(item); |
843 | struct tcm_loop_tpg *tl_tpg = container_of(se_tpg, |
844 | struct tcm_loop_tpg, tl_se_tpg); |
845 | const char *status = NULL; |
846 | ssize_t ret = -EINVAL; |
847 | |
848 | switch (tl_tpg->tl_transport_status) { |
849 | case TCM_TRANSPORT_ONLINE: |
850 | status = "online" ; |
851 | break; |
852 | case TCM_TRANSPORT_OFFLINE: |
853 | status = "offline" ; |
854 | break; |
855 | default: |
856 | break; |
857 | } |
858 | |
859 | if (status) |
860 | ret = snprintf(buf: page, PAGE_SIZE, fmt: "%s\n" , status); |
861 | |
862 | return ret; |
863 | } |
864 | |
865 | static ssize_t tcm_loop_tpg_transport_status_store(struct config_item *item, |
866 | const char *page, size_t count) |
867 | { |
868 | struct se_portal_group *se_tpg = to_tpg(item); |
869 | struct tcm_loop_tpg *tl_tpg = container_of(se_tpg, |
870 | struct tcm_loop_tpg, tl_se_tpg); |
871 | |
872 | if (!strncmp(page, "online" , 6)) { |
873 | tl_tpg->tl_transport_status = TCM_TRANSPORT_ONLINE; |
874 | return count; |
875 | } |
876 | if (!strncmp(page, "offline" , 7)) { |
877 | tl_tpg->tl_transport_status = TCM_TRANSPORT_OFFLINE; |
878 | if (tl_tpg->tl_nexus) { |
879 | struct se_session *tl_sess = tl_tpg->tl_nexus->se_sess; |
880 | |
881 | core_allocate_nexus_loss_ua(acl: tl_sess->se_node_acl); |
882 | } |
883 | return count; |
884 | } |
885 | return -EINVAL; |
886 | } |
887 | |
888 | static ssize_t tcm_loop_tpg_address_show(struct config_item *item, |
889 | char *page) |
890 | { |
891 | struct se_portal_group *se_tpg = to_tpg(item); |
892 | struct tcm_loop_tpg *tl_tpg = container_of(se_tpg, |
893 | struct tcm_loop_tpg, tl_se_tpg); |
894 | struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba; |
895 | |
896 | return snprintf(buf: page, PAGE_SIZE, fmt: "%d:0:%d\n" , |
897 | tl_hba->sh->host_no, tl_tpg->tl_tpgt); |
898 | } |
899 | |
900 | CONFIGFS_ATTR(tcm_loop_tpg_, nexus); |
901 | CONFIGFS_ATTR(tcm_loop_tpg_, transport_status); |
902 | CONFIGFS_ATTR_RO(tcm_loop_tpg_, address); |
903 | |
904 | static struct configfs_attribute *tcm_loop_tpg_attrs[] = { |
905 | &tcm_loop_tpg_attr_nexus, |
906 | &tcm_loop_tpg_attr_transport_status, |
907 | &tcm_loop_tpg_attr_address, |
908 | NULL, |
909 | }; |
910 | |
911 | /* Start items for tcm_loop_naa_cit */ |
912 | |
913 | static struct se_portal_group *tcm_loop_make_naa_tpg(struct se_wwn *wwn, |
914 | const char *name) |
915 | { |
916 | struct tcm_loop_hba *tl_hba = container_of(wwn, |
917 | struct tcm_loop_hba, tl_hba_wwn); |
918 | struct tcm_loop_tpg *tl_tpg; |
919 | int ret; |
920 | unsigned long tpgt; |
921 | |
922 | if (strstr(name, "tpgt_" ) != name) { |
923 | pr_err("Unable to locate \"tpgt_#\" directory group\n" ); |
924 | return ERR_PTR(error: -EINVAL); |
925 | } |
926 | if (kstrtoul(s: name+5, base: 10, res: &tpgt)) |
927 | return ERR_PTR(error: -EINVAL); |
928 | |
929 | if (tpgt >= TL_TPGS_PER_HBA) { |
930 | pr_err("Passed tpgt: %lu exceeds TL_TPGS_PER_HBA: %u\n" , |
931 | tpgt, TL_TPGS_PER_HBA); |
932 | return ERR_PTR(error: -EINVAL); |
933 | } |
934 | tl_tpg = &tl_hba->tl_hba_tpgs[tpgt]; |
935 | tl_tpg->tl_hba = tl_hba; |
936 | tl_tpg->tl_tpgt = tpgt; |
937 | /* |
938 | * Register the tl_tpg as a emulated TCM Target Endpoint |
939 | */ |
940 | ret = core_tpg_register(wwn, &tl_tpg->tl_se_tpg, tl_hba->tl_proto_id); |
941 | if (ret < 0) |
942 | return ERR_PTR(error: -ENOMEM); |
943 | |
944 | pr_debug("TCM_Loop_ConfigFS: Allocated Emulated %s Target Port %s,t,0x%04lx\n" , |
945 | tcm_loop_dump_proto_id(tl_hba), |
946 | config_item_name(&wwn->wwn_group.cg_item), tpgt); |
947 | return &tl_tpg->tl_se_tpg; |
948 | } |
949 | |
950 | static void tcm_loop_drop_naa_tpg( |
951 | struct se_portal_group *se_tpg) |
952 | { |
953 | struct se_wwn *wwn = se_tpg->se_tpg_wwn; |
954 | struct tcm_loop_tpg *tl_tpg = container_of(se_tpg, |
955 | struct tcm_loop_tpg, tl_se_tpg); |
956 | struct tcm_loop_hba *tl_hba; |
957 | unsigned short tpgt; |
958 | |
959 | tl_hba = tl_tpg->tl_hba; |
960 | tpgt = tl_tpg->tl_tpgt; |
961 | /* |
962 | * Release the I_T Nexus for the Virtual target link if present |
963 | */ |
964 | tcm_loop_drop_nexus(tpg: tl_tpg); |
965 | /* |
966 | * Deregister the tl_tpg as a emulated TCM Target Endpoint |
967 | */ |
968 | core_tpg_deregister(se_tpg); |
969 | |
970 | tl_tpg->tl_hba = NULL; |
971 | tl_tpg->tl_tpgt = 0; |
972 | |
973 | pr_debug("TCM_Loop_ConfigFS: Deallocated Emulated %s Target Port %s,t,0x%04x\n" , |
974 | tcm_loop_dump_proto_id(tl_hba), |
975 | config_item_name(&wwn->wwn_group.cg_item), tpgt); |
976 | } |
977 | |
978 | /* End items for tcm_loop_naa_cit */ |
979 | |
980 | /* Start items for tcm_loop_cit */ |
981 | |
982 | static struct se_wwn *tcm_loop_make_scsi_hba( |
983 | struct target_fabric_configfs *tf, |
984 | struct config_group *group, |
985 | const char *name) |
986 | { |
987 | struct tcm_loop_hba *tl_hba; |
988 | struct Scsi_Host *sh; |
989 | char *ptr; |
990 | int ret, off = 0; |
991 | |
992 | tl_hba = kzalloc(size: sizeof(*tl_hba), GFP_KERNEL); |
993 | if (!tl_hba) |
994 | return ERR_PTR(error: -ENOMEM); |
995 | |
996 | /* |
997 | * Determine the emulated Protocol Identifier and Target Port Name |
998 | * based on the incoming configfs directory name. |
999 | */ |
1000 | ptr = strstr(name, "naa." ); |
1001 | if (ptr) { |
1002 | tl_hba->tl_proto_id = SCSI_PROTOCOL_SAS; |
1003 | goto check_len; |
1004 | } |
1005 | ptr = strstr(name, "fc." ); |
1006 | if (ptr) { |
1007 | tl_hba->tl_proto_id = SCSI_PROTOCOL_FCP; |
1008 | off = 3; /* Skip over "fc." */ |
1009 | goto check_len; |
1010 | } |
1011 | ptr = strstr(name, "iqn." ); |
1012 | if (!ptr) { |
1013 | pr_err("Unable to locate prefix for emulated Target Port: %s\n" , |
1014 | name); |
1015 | ret = -EINVAL; |
1016 | goto out; |
1017 | } |
1018 | tl_hba->tl_proto_id = SCSI_PROTOCOL_ISCSI; |
1019 | |
1020 | check_len: |
1021 | if (strlen(name) >= TL_WWN_ADDR_LEN) { |
1022 | pr_err("Emulated NAA %s Address: %s, exceeds max: %d\n" , |
1023 | name, tcm_loop_dump_proto_id(tl_hba), TL_WWN_ADDR_LEN); |
1024 | ret = -EINVAL; |
1025 | goto out; |
1026 | } |
1027 | snprintf(buf: &tl_hba->tl_wwn_address[0], TL_WWN_ADDR_LEN, fmt: "%s" , &name[off]); |
1028 | |
1029 | /* |
1030 | * Call device_register(tl_hba->dev) to register the emulated |
1031 | * Linux/SCSI LLD of type struct Scsi_Host at tl_hba->sh after |
1032 | * device_register() callbacks in tcm_loop_driver_probe() |
1033 | */ |
1034 | ret = tcm_loop_setup_hba_bus(tl_hba, tcm_loop_host_id: tcm_loop_hba_no_cnt); |
1035 | if (ret) |
1036 | return ERR_PTR(error: ret); |
1037 | |
1038 | sh = tl_hba->sh; |
1039 | tcm_loop_hba_no_cnt++; |
1040 | pr_debug("TCM_Loop_ConfigFS: Allocated emulated Target %s Address: %s at Linux/SCSI Host ID: %d\n" , |
1041 | tcm_loop_dump_proto_id(tl_hba), name, sh->host_no); |
1042 | return &tl_hba->tl_hba_wwn; |
1043 | out: |
1044 | kfree(objp: tl_hba); |
1045 | return ERR_PTR(error: ret); |
1046 | } |
1047 | |
1048 | static void tcm_loop_drop_scsi_hba( |
1049 | struct se_wwn *wwn) |
1050 | { |
1051 | struct tcm_loop_hba *tl_hba = container_of(wwn, |
1052 | struct tcm_loop_hba, tl_hba_wwn); |
1053 | |
1054 | pr_debug("TCM_Loop_ConfigFS: Deallocating emulated Target %s Address: %s at Linux/SCSI Host ID: %d\n" , |
1055 | tcm_loop_dump_proto_id(tl_hba), tl_hba->tl_wwn_address, |
1056 | tl_hba->sh->host_no); |
1057 | /* |
1058 | * Call device_unregister() on the original tl_hba->dev. |
1059 | * tcm_loop_fabric_scsi.c:tcm_loop_release_adapter() will |
1060 | * release *tl_hba; |
1061 | */ |
1062 | device_unregister(dev: &tl_hba->dev); |
1063 | } |
1064 | |
1065 | /* Start items for tcm_loop_cit */ |
1066 | static ssize_t tcm_loop_wwn_version_show(struct config_item *item, char *page) |
1067 | { |
1068 | return sprintf(buf: page, fmt: "TCM Loopback Fabric module %s\n" , TCM_LOOP_VERSION); |
1069 | } |
1070 | |
1071 | CONFIGFS_ATTR_RO(tcm_loop_wwn_, version); |
1072 | |
1073 | static struct configfs_attribute *tcm_loop_wwn_attrs[] = { |
1074 | &tcm_loop_wwn_attr_version, |
1075 | NULL, |
1076 | }; |
1077 | |
1078 | /* End items for tcm_loop_cit */ |
1079 | |
1080 | static const struct target_core_fabric_ops loop_ops = { |
1081 | .module = THIS_MODULE, |
1082 | .fabric_name = "loopback" , |
1083 | .tpg_get_wwn = tcm_loop_get_endpoint_wwn, |
1084 | .tpg_get_tag = tcm_loop_get_tag, |
1085 | .tpg_check_demo_mode = tcm_loop_check_demo_mode, |
1086 | .tpg_check_prot_fabric_only = tcm_loop_check_prot_fabric_only, |
1087 | .check_stop_free = tcm_loop_check_stop_free, |
1088 | .release_cmd = tcm_loop_release_cmd, |
1089 | .sess_get_index = tcm_loop_sess_get_index, |
1090 | .write_pending = tcm_loop_write_pending, |
1091 | .get_cmd_state = tcm_loop_get_cmd_state, |
1092 | .queue_data_in = tcm_loop_queue_data_in, |
1093 | .queue_status = tcm_loop_queue_status, |
1094 | .queue_tm_rsp = tcm_loop_queue_tm_rsp, |
1095 | .aborted_task = tcm_loop_aborted_task, |
1096 | .fabric_make_wwn = tcm_loop_make_scsi_hba, |
1097 | .fabric_drop_wwn = tcm_loop_drop_scsi_hba, |
1098 | .fabric_make_tpg = tcm_loop_make_naa_tpg, |
1099 | .fabric_drop_tpg = tcm_loop_drop_naa_tpg, |
1100 | .fabric_post_link = tcm_loop_port_link, |
1101 | .fabric_pre_unlink = tcm_loop_port_unlink, |
1102 | .tfc_wwn_attrs = tcm_loop_wwn_attrs, |
1103 | .tfc_tpg_base_attrs = tcm_loop_tpg_attrs, |
1104 | .tfc_tpg_attrib_attrs = tcm_loop_tpg_attrib_attrs, |
1105 | .default_submit_type = TARGET_QUEUE_SUBMIT, |
1106 | .direct_submit_supp = 0, |
1107 | }; |
1108 | |
1109 | static int __init tcm_loop_fabric_init(void) |
1110 | { |
1111 | int ret = -ENOMEM; |
1112 | |
1113 | tcm_loop_cmd_cache = kmem_cache_create(name: "tcm_loop_cmd_cache" , |
1114 | size: sizeof(struct tcm_loop_cmd), |
1115 | align: __alignof__(struct tcm_loop_cmd), |
1116 | flags: 0, NULL); |
1117 | if (!tcm_loop_cmd_cache) { |
1118 | pr_debug("kmem_cache_create() for tcm_loop_cmd_cache failed\n" ); |
1119 | goto out; |
1120 | } |
1121 | |
1122 | ret = tcm_loop_alloc_core_bus(); |
1123 | if (ret) |
1124 | goto out_destroy_cache; |
1125 | |
1126 | ret = target_register_template(fo: &loop_ops); |
1127 | if (ret) |
1128 | goto out_release_core_bus; |
1129 | |
1130 | return 0; |
1131 | |
1132 | out_release_core_bus: |
1133 | tcm_loop_release_core_bus(); |
1134 | out_destroy_cache: |
1135 | kmem_cache_destroy(s: tcm_loop_cmd_cache); |
1136 | out: |
1137 | return ret; |
1138 | } |
1139 | |
1140 | static void __exit tcm_loop_fabric_exit(void) |
1141 | { |
1142 | target_unregister_template(fo: &loop_ops); |
1143 | tcm_loop_release_core_bus(); |
1144 | kmem_cache_destroy(s: tcm_loop_cmd_cache); |
1145 | } |
1146 | |
1147 | MODULE_DESCRIPTION("TCM loopback virtual Linux/SCSI fabric module" ); |
1148 | MODULE_AUTHOR("Nicholas A. Bellinger <nab@risingtidesystems.com>" ); |
1149 | MODULE_LICENSE("GPL" ); |
1150 | module_init(tcm_loop_fabric_init); |
1151 | module_exit(tcm_loop_fabric_exit); |
1152 | |