Commit b43f1886e4d3ea3f68665eaea96526ccdd53741d
1 parent
683497566d
Exists in
ti-lsk-linux-4.1.y
and in
10 other branches
tcm_loop: Fix memory leak in tcm_loop_submission_work error path
This patch fixes a tcm_loop_cmd descriptor memory leak in the tcm_loop_submission_work() error path, and would result in warnings about leaked tcm_loop_cmd_cache objects at module unload time. Go ahead and invoke kmem_cache_free() to release tl_cmd back to tcm_loop_cmd_cache before calling sc->scsi_done(). Reported-by: Sebastian Herbszt <herbszt@gmx.de> Tested-by: Sebastian Herbszt <herbszt@gmx.de> Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
Showing 1 changed file with 1 additions and 0 deletions Inline Diff
drivers/target/loopback/tcm_loop.c
1 | /******************************************************************************* | 1 | /******************************************************************************* |
2 | * | 2 | * |
3 | * This file contains the Linux/SCSI LLD virtual SCSI initiator driver | 3 | * This file contains the Linux/SCSI LLD virtual SCSI initiator driver |
4 | * for emulated SAS initiator ports | 4 | * for emulated SAS initiator ports |
5 | * | 5 | * |
6 | * © Copyright 2011-2013 Datera, Inc. | 6 | * © Copyright 2011-2013 Datera, Inc. |
7 | * | 7 | * |
8 | * Licensed to the Linux Foundation under the General Public License (GPL) version 2. | 8 | * Licensed to the Linux Foundation under the General Public License (GPL) version 2. |
9 | * | 9 | * |
10 | * Author: Nicholas A. Bellinger <nab@risingtidesystems.com> | 10 | * Author: Nicholas A. Bellinger <nab@risingtidesystems.com> |
11 | * | 11 | * |
12 | * This program is free software; you can redistribute it and/or modify | 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 | 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 | 14 | * the Free Software Foundation; either version 2 of the License, or |
15 | * (at your option) any later version. | 15 | * (at your option) any later version. |
16 | * | 16 | * |
17 | * This program is distributed in the hope that it will be useful, | 17 | * This program is distributed in the hope that it will be useful, |
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
20 | * GNU General Public License for more details. | 20 | * GNU General Public License for more details. |
21 | ****************************************************************************/ | 21 | ****************************************************************************/ |
22 | 22 | ||
23 | #include <linux/module.h> | 23 | #include <linux/module.h> |
24 | #include <linux/moduleparam.h> | 24 | #include <linux/moduleparam.h> |
25 | #include <linux/init.h> | 25 | #include <linux/init.h> |
26 | #include <linux/slab.h> | 26 | #include <linux/slab.h> |
27 | #include <linux/types.h> | 27 | #include <linux/types.h> |
28 | #include <linux/configfs.h> | 28 | #include <linux/configfs.h> |
29 | #include <scsi/scsi.h> | 29 | #include <scsi/scsi.h> |
30 | #include <scsi/scsi_tcq.h> | 30 | #include <scsi/scsi_tcq.h> |
31 | #include <scsi/scsi_host.h> | 31 | #include <scsi/scsi_host.h> |
32 | #include <scsi/scsi_device.h> | 32 | #include <scsi/scsi_device.h> |
33 | #include <scsi/scsi_cmnd.h> | 33 | #include <scsi/scsi_cmnd.h> |
34 | 34 | ||
35 | #include <target/target_core_base.h> | 35 | #include <target/target_core_base.h> |
36 | #include <target/target_core_fabric.h> | 36 | #include <target/target_core_fabric.h> |
37 | #include <target/target_core_fabric_configfs.h> | 37 | #include <target/target_core_fabric_configfs.h> |
38 | #include <target/target_core_configfs.h> | 38 | #include <target/target_core_configfs.h> |
39 | 39 | ||
40 | #include "tcm_loop.h" | 40 | #include "tcm_loop.h" |
41 | 41 | ||
42 | #define to_tcm_loop_hba(hba) container_of(hba, struct tcm_loop_hba, dev) | 42 | #define to_tcm_loop_hba(hba) container_of(hba, struct tcm_loop_hba, dev) |
43 | 43 | ||
44 | /* Local pointer to allocated TCM configfs fabric module */ | 44 | /* Local pointer to allocated TCM configfs fabric module */ |
45 | static struct target_fabric_configfs *tcm_loop_fabric_configfs; | 45 | static struct target_fabric_configfs *tcm_loop_fabric_configfs; |
46 | 46 | ||
47 | static struct workqueue_struct *tcm_loop_workqueue; | 47 | static struct workqueue_struct *tcm_loop_workqueue; |
48 | static struct kmem_cache *tcm_loop_cmd_cache; | 48 | static struct kmem_cache *tcm_loop_cmd_cache; |
49 | 49 | ||
50 | static int tcm_loop_hba_no_cnt; | 50 | static int tcm_loop_hba_no_cnt; |
51 | 51 | ||
52 | static int tcm_loop_queue_status(struct se_cmd *se_cmd); | 52 | static int tcm_loop_queue_status(struct se_cmd *se_cmd); |
53 | 53 | ||
54 | /* | 54 | /* |
55 | * Called from struct target_core_fabric_ops->check_stop_free() | 55 | * Called from struct target_core_fabric_ops->check_stop_free() |
56 | */ | 56 | */ |
57 | static int tcm_loop_check_stop_free(struct se_cmd *se_cmd) | 57 | static int tcm_loop_check_stop_free(struct se_cmd *se_cmd) |
58 | { | 58 | { |
59 | /* | 59 | /* |
60 | * Do not release struct se_cmd's containing a valid TMR | 60 | * Do not release struct se_cmd's containing a valid TMR |
61 | * pointer. These will be released directly in tcm_loop_device_reset() | 61 | * pointer. These will be released directly in tcm_loop_device_reset() |
62 | * with transport_generic_free_cmd(). | 62 | * with transport_generic_free_cmd(). |
63 | */ | 63 | */ |
64 | if (se_cmd->se_cmd_flags & SCF_SCSI_TMR_CDB) | 64 | if (se_cmd->se_cmd_flags & SCF_SCSI_TMR_CDB) |
65 | return 0; | 65 | return 0; |
66 | /* | 66 | /* |
67 | * Release the struct se_cmd, which will make a callback to release | 67 | * Release the struct se_cmd, which will make a callback to release |
68 | * struct tcm_loop_cmd * in tcm_loop_deallocate_core_cmd() | 68 | * struct tcm_loop_cmd * in tcm_loop_deallocate_core_cmd() |
69 | */ | 69 | */ |
70 | transport_generic_free_cmd(se_cmd, 0); | 70 | transport_generic_free_cmd(se_cmd, 0); |
71 | return 1; | 71 | return 1; |
72 | } | 72 | } |
73 | 73 | ||
74 | static void tcm_loop_release_cmd(struct se_cmd *se_cmd) | 74 | static void tcm_loop_release_cmd(struct se_cmd *se_cmd) |
75 | { | 75 | { |
76 | struct tcm_loop_cmd *tl_cmd = container_of(se_cmd, | 76 | struct tcm_loop_cmd *tl_cmd = container_of(se_cmd, |
77 | struct tcm_loop_cmd, tl_se_cmd); | 77 | struct tcm_loop_cmd, tl_se_cmd); |
78 | 78 | ||
79 | kmem_cache_free(tcm_loop_cmd_cache, tl_cmd); | 79 | kmem_cache_free(tcm_loop_cmd_cache, tl_cmd); |
80 | } | 80 | } |
81 | 81 | ||
82 | static int tcm_loop_show_info(struct seq_file *m, struct Scsi_Host *host) | 82 | static int tcm_loop_show_info(struct seq_file *m, struct Scsi_Host *host) |
83 | { | 83 | { |
84 | seq_printf(m, "tcm_loop_proc_info()\n"); | 84 | seq_printf(m, "tcm_loop_proc_info()\n"); |
85 | return 0; | 85 | return 0; |
86 | } | 86 | } |
87 | 87 | ||
88 | static int tcm_loop_driver_probe(struct device *); | 88 | static int tcm_loop_driver_probe(struct device *); |
89 | static int tcm_loop_driver_remove(struct device *); | 89 | static int tcm_loop_driver_remove(struct device *); |
90 | 90 | ||
91 | static int pseudo_lld_bus_match(struct device *dev, | 91 | static int pseudo_lld_bus_match(struct device *dev, |
92 | struct device_driver *dev_driver) | 92 | struct device_driver *dev_driver) |
93 | { | 93 | { |
94 | return 1; | 94 | return 1; |
95 | } | 95 | } |
96 | 96 | ||
97 | static struct bus_type tcm_loop_lld_bus = { | 97 | static struct bus_type tcm_loop_lld_bus = { |
98 | .name = "tcm_loop_bus", | 98 | .name = "tcm_loop_bus", |
99 | .match = pseudo_lld_bus_match, | 99 | .match = pseudo_lld_bus_match, |
100 | .probe = tcm_loop_driver_probe, | 100 | .probe = tcm_loop_driver_probe, |
101 | .remove = tcm_loop_driver_remove, | 101 | .remove = tcm_loop_driver_remove, |
102 | }; | 102 | }; |
103 | 103 | ||
104 | static struct device_driver tcm_loop_driverfs = { | 104 | static struct device_driver tcm_loop_driverfs = { |
105 | .name = "tcm_loop", | 105 | .name = "tcm_loop", |
106 | .bus = &tcm_loop_lld_bus, | 106 | .bus = &tcm_loop_lld_bus, |
107 | }; | 107 | }; |
108 | /* | 108 | /* |
109 | * Used with root_device_register() in tcm_loop_alloc_core_bus() below | 109 | * Used with root_device_register() in tcm_loop_alloc_core_bus() below |
110 | */ | 110 | */ |
111 | struct device *tcm_loop_primary; | 111 | struct device *tcm_loop_primary; |
112 | 112 | ||
113 | /* | 113 | /* |
114 | * Copied from drivers/scsi/libfc/fc_fcp.c:fc_change_queue_depth() and | 114 | * Copied from drivers/scsi/libfc/fc_fcp.c:fc_change_queue_depth() and |
115 | * drivers/scsi/libiscsi.c:iscsi_change_queue_depth() | 115 | * drivers/scsi/libiscsi.c:iscsi_change_queue_depth() |
116 | */ | 116 | */ |
117 | static int tcm_loop_change_queue_depth( | 117 | static int tcm_loop_change_queue_depth( |
118 | struct scsi_device *sdev, | 118 | struct scsi_device *sdev, |
119 | int depth, | 119 | int depth, |
120 | int reason) | 120 | int reason) |
121 | { | 121 | { |
122 | switch (reason) { | 122 | switch (reason) { |
123 | case SCSI_QDEPTH_DEFAULT: | 123 | case SCSI_QDEPTH_DEFAULT: |
124 | scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth); | 124 | scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth); |
125 | break; | 125 | break; |
126 | case SCSI_QDEPTH_QFULL: | 126 | case SCSI_QDEPTH_QFULL: |
127 | scsi_track_queue_full(sdev, depth); | 127 | scsi_track_queue_full(sdev, depth); |
128 | break; | 128 | break; |
129 | case SCSI_QDEPTH_RAMP_UP: | 129 | case SCSI_QDEPTH_RAMP_UP: |
130 | scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth); | 130 | scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth); |
131 | break; | 131 | break; |
132 | default: | 132 | default: |
133 | return -EOPNOTSUPP; | 133 | return -EOPNOTSUPP; |
134 | } | 134 | } |
135 | return sdev->queue_depth; | 135 | return sdev->queue_depth; |
136 | } | 136 | } |
137 | 137 | ||
138 | static int tcm_loop_change_queue_type(struct scsi_device *sdev, int tag) | 138 | static int tcm_loop_change_queue_type(struct scsi_device *sdev, int tag) |
139 | { | 139 | { |
140 | if (sdev->tagged_supported) { | 140 | if (sdev->tagged_supported) { |
141 | scsi_set_tag_type(sdev, tag); | 141 | scsi_set_tag_type(sdev, tag); |
142 | 142 | ||
143 | if (tag) | 143 | if (tag) |
144 | scsi_activate_tcq(sdev, sdev->queue_depth); | 144 | scsi_activate_tcq(sdev, sdev->queue_depth); |
145 | else | 145 | else |
146 | scsi_deactivate_tcq(sdev, sdev->queue_depth); | 146 | scsi_deactivate_tcq(sdev, sdev->queue_depth); |
147 | } else | 147 | } else |
148 | tag = 0; | 148 | tag = 0; |
149 | 149 | ||
150 | return tag; | 150 | return tag; |
151 | } | 151 | } |
152 | 152 | ||
153 | /* | 153 | /* |
154 | * Locate the SAM Task Attr from struct scsi_cmnd * | 154 | * Locate the SAM Task Attr from struct scsi_cmnd * |
155 | */ | 155 | */ |
156 | static int tcm_loop_sam_attr(struct scsi_cmnd *sc) | 156 | static int tcm_loop_sam_attr(struct scsi_cmnd *sc) |
157 | { | 157 | { |
158 | if (sc->device->tagged_supported) { | 158 | if (sc->device->tagged_supported) { |
159 | switch (sc->tag) { | 159 | switch (sc->tag) { |
160 | case HEAD_OF_QUEUE_TAG: | 160 | case HEAD_OF_QUEUE_TAG: |
161 | return MSG_HEAD_TAG; | 161 | return MSG_HEAD_TAG; |
162 | case ORDERED_QUEUE_TAG: | 162 | case ORDERED_QUEUE_TAG: |
163 | return MSG_ORDERED_TAG; | 163 | return MSG_ORDERED_TAG; |
164 | default: | 164 | default: |
165 | break; | 165 | break; |
166 | } | 166 | } |
167 | } | 167 | } |
168 | 168 | ||
169 | return MSG_SIMPLE_TAG; | 169 | return MSG_SIMPLE_TAG; |
170 | } | 170 | } |
171 | 171 | ||
172 | static void tcm_loop_submission_work(struct work_struct *work) | 172 | static void tcm_loop_submission_work(struct work_struct *work) |
173 | { | 173 | { |
174 | struct tcm_loop_cmd *tl_cmd = | 174 | struct tcm_loop_cmd *tl_cmd = |
175 | container_of(work, struct tcm_loop_cmd, work); | 175 | container_of(work, struct tcm_loop_cmd, work); |
176 | struct se_cmd *se_cmd = &tl_cmd->tl_se_cmd; | 176 | struct se_cmd *se_cmd = &tl_cmd->tl_se_cmd; |
177 | struct scsi_cmnd *sc = tl_cmd->sc; | 177 | struct scsi_cmnd *sc = tl_cmd->sc; |
178 | struct tcm_loop_nexus *tl_nexus; | 178 | struct tcm_loop_nexus *tl_nexus; |
179 | struct tcm_loop_hba *tl_hba; | 179 | struct tcm_loop_hba *tl_hba; |
180 | struct tcm_loop_tpg *tl_tpg; | 180 | struct tcm_loop_tpg *tl_tpg; |
181 | struct scatterlist *sgl_bidi = NULL; | 181 | struct scatterlist *sgl_bidi = NULL; |
182 | u32 sgl_bidi_count = 0, transfer_length; | 182 | u32 sgl_bidi_count = 0, transfer_length; |
183 | int rc; | 183 | int rc; |
184 | 184 | ||
185 | tl_hba = *(struct tcm_loop_hba **)shost_priv(sc->device->host); | 185 | tl_hba = *(struct tcm_loop_hba **)shost_priv(sc->device->host); |
186 | tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id]; | 186 | tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id]; |
187 | 187 | ||
188 | /* | 188 | /* |
189 | * Ensure that this tl_tpg reference from the incoming sc->device->id | 189 | * Ensure that this tl_tpg reference from the incoming sc->device->id |
190 | * has already been configured via tcm_loop_make_naa_tpg(). | 190 | * has already been configured via tcm_loop_make_naa_tpg(). |
191 | */ | 191 | */ |
192 | if (!tl_tpg->tl_hba) { | 192 | if (!tl_tpg->tl_hba) { |
193 | set_host_byte(sc, DID_NO_CONNECT); | 193 | set_host_byte(sc, DID_NO_CONNECT); |
194 | goto out_done; | 194 | goto out_done; |
195 | } | 195 | } |
196 | if (tl_tpg->tl_transport_status == TCM_TRANSPORT_OFFLINE) { | 196 | if (tl_tpg->tl_transport_status == TCM_TRANSPORT_OFFLINE) { |
197 | set_host_byte(sc, DID_TRANSPORT_DISRUPTED); | 197 | set_host_byte(sc, DID_TRANSPORT_DISRUPTED); |
198 | goto out_done; | 198 | goto out_done; |
199 | } | 199 | } |
200 | tl_nexus = tl_hba->tl_nexus; | 200 | tl_nexus = tl_hba->tl_nexus; |
201 | if (!tl_nexus) { | 201 | if (!tl_nexus) { |
202 | scmd_printk(KERN_ERR, sc, "TCM_Loop I_T Nexus" | 202 | scmd_printk(KERN_ERR, sc, "TCM_Loop I_T Nexus" |
203 | " does not exist\n"); | 203 | " does not exist\n"); |
204 | set_host_byte(sc, DID_ERROR); | 204 | set_host_byte(sc, DID_ERROR); |
205 | goto out_done; | 205 | goto out_done; |
206 | } | 206 | } |
207 | if (scsi_bidi_cmnd(sc)) { | 207 | if (scsi_bidi_cmnd(sc)) { |
208 | struct scsi_data_buffer *sdb = scsi_in(sc); | 208 | struct scsi_data_buffer *sdb = scsi_in(sc); |
209 | 209 | ||
210 | sgl_bidi = sdb->table.sgl; | 210 | sgl_bidi = sdb->table.sgl; |
211 | sgl_bidi_count = sdb->table.nents; | 211 | sgl_bidi_count = sdb->table.nents; |
212 | se_cmd->se_cmd_flags |= SCF_BIDI; | 212 | se_cmd->se_cmd_flags |= SCF_BIDI; |
213 | 213 | ||
214 | } | 214 | } |
215 | 215 | ||
216 | transfer_length = scsi_transfer_length(sc); | 216 | transfer_length = scsi_transfer_length(sc); |
217 | if (!scsi_prot_sg_count(sc) && | 217 | if (!scsi_prot_sg_count(sc) && |
218 | scsi_get_prot_op(sc) != SCSI_PROT_NORMAL) { | 218 | scsi_get_prot_op(sc) != SCSI_PROT_NORMAL) { |
219 | se_cmd->prot_pto = true; | 219 | se_cmd->prot_pto = true; |
220 | /* | 220 | /* |
221 | * loopback transport doesn't support | 221 | * loopback transport doesn't support |
222 | * WRITE_GENERATE, READ_STRIP protection | 222 | * WRITE_GENERATE, READ_STRIP protection |
223 | * information operations, go ahead unprotected. | 223 | * information operations, go ahead unprotected. |
224 | */ | 224 | */ |
225 | transfer_length = scsi_bufflen(sc); | 225 | transfer_length = scsi_bufflen(sc); |
226 | } | 226 | } |
227 | 227 | ||
228 | rc = target_submit_cmd_map_sgls(se_cmd, tl_nexus->se_sess, sc->cmnd, | 228 | rc = target_submit_cmd_map_sgls(se_cmd, tl_nexus->se_sess, sc->cmnd, |
229 | &tl_cmd->tl_sense_buf[0], tl_cmd->sc->device->lun, | 229 | &tl_cmd->tl_sense_buf[0], tl_cmd->sc->device->lun, |
230 | transfer_length, tcm_loop_sam_attr(sc), | 230 | transfer_length, tcm_loop_sam_attr(sc), |
231 | sc->sc_data_direction, 0, | 231 | sc->sc_data_direction, 0, |
232 | scsi_sglist(sc), scsi_sg_count(sc), | 232 | scsi_sglist(sc), scsi_sg_count(sc), |
233 | sgl_bidi, sgl_bidi_count, | 233 | sgl_bidi, sgl_bidi_count, |
234 | scsi_prot_sglist(sc), scsi_prot_sg_count(sc)); | 234 | scsi_prot_sglist(sc), scsi_prot_sg_count(sc)); |
235 | if (rc < 0) { | 235 | if (rc < 0) { |
236 | set_host_byte(sc, DID_NO_CONNECT); | 236 | set_host_byte(sc, DID_NO_CONNECT); |
237 | goto out_done; | 237 | goto out_done; |
238 | } | 238 | } |
239 | return; | 239 | return; |
240 | 240 | ||
241 | out_done: | 241 | out_done: |
242 | kmem_cache_free(tcm_loop_cmd_cache, tl_cmd); | ||
242 | sc->scsi_done(sc); | 243 | sc->scsi_done(sc); |
243 | return; | 244 | return; |
244 | } | 245 | } |
245 | 246 | ||
246 | /* | 247 | /* |
247 | * ->queuecommand can be and usually is called from interrupt context, so | 248 | * ->queuecommand can be and usually is called from interrupt context, so |
248 | * defer the actual submission to a workqueue. | 249 | * defer the actual submission to a workqueue. |
249 | */ | 250 | */ |
250 | static int tcm_loop_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *sc) | 251 | static int tcm_loop_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *sc) |
251 | { | 252 | { |
252 | struct tcm_loop_cmd *tl_cmd; | 253 | struct tcm_loop_cmd *tl_cmd; |
253 | 254 | ||
254 | pr_debug("tcm_loop_queuecommand() %d:%d:%d:%d got CDB: 0x%02x" | 255 | pr_debug("tcm_loop_queuecommand() %d:%d:%d:%d got CDB: 0x%02x" |
255 | " scsi_buf_len: %u\n", sc->device->host->host_no, | 256 | " scsi_buf_len: %u\n", sc->device->host->host_no, |
256 | sc->device->id, sc->device->channel, sc->device->lun, | 257 | sc->device->id, sc->device->channel, sc->device->lun, |
257 | sc->cmnd[0], scsi_bufflen(sc)); | 258 | sc->cmnd[0], scsi_bufflen(sc)); |
258 | 259 | ||
259 | tl_cmd = kmem_cache_zalloc(tcm_loop_cmd_cache, GFP_ATOMIC); | 260 | tl_cmd = kmem_cache_zalloc(tcm_loop_cmd_cache, GFP_ATOMIC); |
260 | if (!tl_cmd) { | 261 | if (!tl_cmd) { |
261 | pr_err("Unable to allocate struct tcm_loop_cmd\n"); | 262 | pr_err("Unable to allocate struct tcm_loop_cmd\n"); |
262 | set_host_byte(sc, DID_ERROR); | 263 | set_host_byte(sc, DID_ERROR); |
263 | sc->scsi_done(sc); | 264 | sc->scsi_done(sc); |
264 | return 0; | 265 | return 0; |
265 | } | 266 | } |
266 | 267 | ||
267 | tl_cmd->sc = sc; | 268 | tl_cmd->sc = sc; |
268 | tl_cmd->sc_cmd_tag = sc->tag; | 269 | tl_cmd->sc_cmd_tag = sc->tag; |
269 | INIT_WORK(&tl_cmd->work, tcm_loop_submission_work); | 270 | INIT_WORK(&tl_cmd->work, tcm_loop_submission_work); |
270 | queue_work(tcm_loop_workqueue, &tl_cmd->work); | 271 | queue_work(tcm_loop_workqueue, &tl_cmd->work); |
271 | return 0; | 272 | return 0; |
272 | } | 273 | } |
273 | 274 | ||
274 | /* | 275 | /* |
275 | * Called from SCSI EH process context to issue a LUN_RESET TMR | 276 | * Called from SCSI EH process context to issue a LUN_RESET TMR |
276 | * to struct scsi_device | 277 | * to struct scsi_device |
277 | */ | 278 | */ |
278 | static int tcm_loop_issue_tmr(struct tcm_loop_tpg *tl_tpg, | 279 | static int tcm_loop_issue_tmr(struct tcm_loop_tpg *tl_tpg, |
279 | struct tcm_loop_nexus *tl_nexus, | 280 | struct tcm_loop_nexus *tl_nexus, |
280 | int lun, int task, enum tcm_tmreq_table tmr) | 281 | int lun, int task, enum tcm_tmreq_table tmr) |
281 | { | 282 | { |
282 | struct se_cmd *se_cmd = NULL; | 283 | struct se_cmd *se_cmd = NULL; |
283 | struct se_session *se_sess; | 284 | struct se_session *se_sess; |
284 | struct se_portal_group *se_tpg; | 285 | struct se_portal_group *se_tpg; |
285 | struct tcm_loop_cmd *tl_cmd = NULL; | 286 | struct tcm_loop_cmd *tl_cmd = NULL; |
286 | struct tcm_loop_tmr *tl_tmr = NULL; | 287 | struct tcm_loop_tmr *tl_tmr = NULL; |
287 | int ret = TMR_FUNCTION_FAILED, rc; | 288 | int ret = TMR_FUNCTION_FAILED, rc; |
288 | 289 | ||
289 | tl_cmd = kmem_cache_zalloc(tcm_loop_cmd_cache, GFP_KERNEL); | 290 | tl_cmd = kmem_cache_zalloc(tcm_loop_cmd_cache, GFP_KERNEL); |
290 | if (!tl_cmd) { | 291 | if (!tl_cmd) { |
291 | pr_err("Unable to allocate memory for tl_cmd\n"); | 292 | pr_err("Unable to allocate memory for tl_cmd\n"); |
292 | return ret; | 293 | return ret; |
293 | } | 294 | } |
294 | 295 | ||
295 | tl_tmr = kzalloc(sizeof(struct tcm_loop_tmr), GFP_KERNEL); | 296 | tl_tmr = kzalloc(sizeof(struct tcm_loop_tmr), GFP_KERNEL); |
296 | if (!tl_tmr) { | 297 | if (!tl_tmr) { |
297 | pr_err("Unable to allocate memory for tl_tmr\n"); | 298 | pr_err("Unable to allocate memory for tl_tmr\n"); |
298 | goto release; | 299 | goto release; |
299 | } | 300 | } |
300 | init_waitqueue_head(&tl_tmr->tl_tmr_wait); | 301 | init_waitqueue_head(&tl_tmr->tl_tmr_wait); |
301 | 302 | ||
302 | se_cmd = &tl_cmd->tl_se_cmd; | 303 | se_cmd = &tl_cmd->tl_se_cmd; |
303 | se_tpg = &tl_tpg->tl_se_tpg; | 304 | se_tpg = &tl_tpg->tl_se_tpg; |
304 | se_sess = tl_nexus->se_sess; | 305 | se_sess = tl_nexus->se_sess; |
305 | /* | 306 | /* |
306 | * Initialize struct se_cmd descriptor from target_core_mod infrastructure | 307 | * Initialize struct se_cmd descriptor from target_core_mod infrastructure |
307 | */ | 308 | */ |
308 | transport_init_se_cmd(se_cmd, se_tpg->se_tpg_tfo, se_sess, 0, | 309 | transport_init_se_cmd(se_cmd, se_tpg->se_tpg_tfo, se_sess, 0, |
309 | DMA_NONE, MSG_SIMPLE_TAG, | 310 | DMA_NONE, MSG_SIMPLE_TAG, |
310 | &tl_cmd->tl_sense_buf[0]); | 311 | &tl_cmd->tl_sense_buf[0]); |
311 | 312 | ||
312 | rc = core_tmr_alloc_req(se_cmd, tl_tmr, tmr, GFP_KERNEL); | 313 | rc = core_tmr_alloc_req(se_cmd, tl_tmr, tmr, GFP_KERNEL); |
313 | if (rc < 0) | 314 | if (rc < 0) |
314 | goto release; | 315 | goto release; |
315 | 316 | ||
316 | if (tmr == TMR_ABORT_TASK) | 317 | if (tmr == TMR_ABORT_TASK) |
317 | se_cmd->se_tmr_req->ref_task_tag = task; | 318 | se_cmd->se_tmr_req->ref_task_tag = task; |
318 | 319 | ||
319 | /* | 320 | /* |
320 | * Locate the underlying TCM struct se_lun | 321 | * Locate the underlying TCM struct se_lun |
321 | */ | 322 | */ |
322 | if (transport_lookup_tmr_lun(se_cmd, lun) < 0) { | 323 | if (transport_lookup_tmr_lun(se_cmd, lun) < 0) { |
323 | ret = TMR_LUN_DOES_NOT_EXIST; | 324 | ret = TMR_LUN_DOES_NOT_EXIST; |
324 | goto release; | 325 | goto release; |
325 | } | 326 | } |
326 | /* | 327 | /* |
327 | * Queue the TMR to TCM Core and sleep waiting for | 328 | * Queue the TMR to TCM Core and sleep waiting for |
328 | * tcm_loop_queue_tm_rsp() to wake us up. | 329 | * tcm_loop_queue_tm_rsp() to wake us up. |
329 | */ | 330 | */ |
330 | transport_generic_handle_tmr(se_cmd); | 331 | transport_generic_handle_tmr(se_cmd); |
331 | wait_event(tl_tmr->tl_tmr_wait, atomic_read(&tl_tmr->tmr_complete)); | 332 | wait_event(tl_tmr->tl_tmr_wait, atomic_read(&tl_tmr->tmr_complete)); |
332 | /* | 333 | /* |
333 | * The TMR LUN_RESET has completed, check the response status and | 334 | * The TMR LUN_RESET has completed, check the response status and |
334 | * then release allocations. | 335 | * then release allocations. |
335 | */ | 336 | */ |
336 | ret = se_cmd->se_tmr_req->response; | 337 | ret = se_cmd->se_tmr_req->response; |
337 | release: | 338 | release: |
338 | if (se_cmd) | 339 | if (se_cmd) |
339 | transport_generic_free_cmd(se_cmd, 1); | 340 | transport_generic_free_cmd(se_cmd, 1); |
340 | else | 341 | else |
341 | kmem_cache_free(tcm_loop_cmd_cache, tl_cmd); | 342 | kmem_cache_free(tcm_loop_cmd_cache, tl_cmd); |
342 | kfree(tl_tmr); | 343 | kfree(tl_tmr); |
343 | return ret; | 344 | return ret; |
344 | } | 345 | } |
345 | 346 | ||
346 | static int tcm_loop_abort_task(struct scsi_cmnd *sc) | 347 | static int tcm_loop_abort_task(struct scsi_cmnd *sc) |
347 | { | 348 | { |
348 | struct tcm_loop_hba *tl_hba; | 349 | struct tcm_loop_hba *tl_hba; |
349 | struct tcm_loop_nexus *tl_nexus; | 350 | struct tcm_loop_nexus *tl_nexus; |
350 | struct tcm_loop_tpg *tl_tpg; | 351 | struct tcm_loop_tpg *tl_tpg; |
351 | int ret = FAILED; | 352 | int ret = FAILED; |
352 | 353 | ||
353 | /* | 354 | /* |
354 | * Locate the tcm_loop_hba_t pointer | 355 | * Locate the tcm_loop_hba_t pointer |
355 | */ | 356 | */ |
356 | tl_hba = *(struct tcm_loop_hba **)shost_priv(sc->device->host); | 357 | tl_hba = *(struct tcm_loop_hba **)shost_priv(sc->device->host); |
357 | /* | 358 | /* |
358 | * Locate the tl_nexus and se_sess pointers | 359 | * Locate the tl_nexus and se_sess pointers |
359 | */ | 360 | */ |
360 | tl_nexus = tl_hba->tl_nexus; | 361 | tl_nexus = tl_hba->tl_nexus; |
361 | if (!tl_nexus) { | 362 | if (!tl_nexus) { |
362 | pr_err("Unable to perform device reset without" | 363 | pr_err("Unable to perform device reset without" |
363 | " active I_T Nexus\n"); | 364 | " active I_T Nexus\n"); |
364 | return FAILED; | 365 | return FAILED; |
365 | } | 366 | } |
366 | 367 | ||
367 | /* | 368 | /* |
368 | * Locate the tl_tpg pointer from TargetID in sc->device->id | 369 | * Locate the tl_tpg pointer from TargetID in sc->device->id |
369 | */ | 370 | */ |
370 | tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id]; | 371 | tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id]; |
371 | ret = tcm_loop_issue_tmr(tl_tpg, tl_nexus, sc->device->lun, | 372 | ret = tcm_loop_issue_tmr(tl_tpg, tl_nexus, sc->device->lun, |
372 | sc->tag, TMR_ABORT_TASK); | 373 | sc->tag, TMR_ABORT_TASK); |
373 | return (ret == TMR_FUNCTION_COMPLETE) ? SUCCESS : FAILED; | 374 | return (ret == TMR_FUNCTION_COMPLETE) ? SUCCESS : FAILED; |
374 | } | 375 | } |
375 | 376 | ||
376 | /* | 377 | /* |
377 | * Called from SCSI EH process context to issue a LUN_RESET TMR | 378 | * Called from SCSI EH process context to issue a LUN_RESET TMR |
378 | * to struct scsi_device | 379 | * to struct scsi_device |
379 | */ | 380 | */ |
380 | static int tcm_loop_device_reset(struct scsi_cmnd *sc) | 381 | static int tcm_loop_device_reset(struct scsi_cmnd *sc) |
381 | { | 382 | { |
382 | struct tcm_loop_hba *tl_hba; | 383 | struct tcm_loop_hba *tl_hba; |
383 | struct tcm_loop_nexus *tl_nexus; | 384 | struct tcm_loop_nexus *tl_nexus; |
384 | struct tcm_loop_tpg *tl_tpg; | 385 | struct tcm_loop_tpg *tl_tpg; |
385 | int ret = FAILED; | 386 | int ret = FAILED; |
386 | 387 | ||
387 | /* | 388 | /* |
388 | * Locate the tcm_loop_hba_t pointer | 389 | * Locate the tcm_loop_hba_t pointer |
389 | */ | 390 | */ |
390 | tl_hba = *(struct tcm_loop_hba **)shost_priv(sc->device->host); | 391 | tl_hba = *(struct tcm_loop_hba **)shost_priv(sc->device->host); |
391 | /* | 392 | /* |
392 | * Locate the tl_nexus and se_sess pointers | 393 | * Locate the tl_nexus and se_sess pointers |
393 | */ | 394 | */ |
394 | tl_nexus = tl_hba->tl_nexus; | 395 | tl_nexus = tl_hba->tl_nexus; |
395 | if (!tl_nexus) { | 396 | if (!tl_nexus) { |
396 | pr_err("Unable to perform device reset without" | 397 | pr_err("Unable to perform device reset without" |
397 | " active I_T Nexus\n"); | 398 | " active I_T Nexus\n"); |
398 | return FAILED; | 399 | return FAILED; |
399 | } | 400 | } |
400 | /* | 401 | /* |
401 | * Locate the tl_tpg pointer from TargetID in sc->device->id | 402 | * Locate the tl_tpg pointer from TargetID in sc->device->id |
402 | */ | 403 | */ |
403 | tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id]; | 404 | tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id]; |
404 | ret = tcm_loop_issue_tmr(tl_tpg, tl_nexus, sc->device->lun, | 405 | ret = tcm_loop_issue_tmr(tl_tpg, tl_nexus, sc->device->lun, |
405 | 0, TMR_LUN_RESET); | 406 | 0, TMR_LUN_RESET); |
406 | return (ret == TMR_FUNCTION_COMPLETE) ? SUCCESS : FAILED; | 407 | return (ret == TMR_FUNCTION_COMPLETE) ? SUCCESS : FAILED; |
407 | } | 408 | } |
408 | 409 | ||
409 | static int tcm_loop_target_reset(struct scsi_cmnd *sc) | 410 | static int tcm_loop_target_reset(struct scsi_cmnd *sc) |
410 | { | 411 | { |
411 | struct tcm_loop_hba *tl_hba; | 412 | struct tcm_loop_hba *tl_hba; |
412 | struct tcm_loop_tpg *tl_tpg; | 413 | struct tcm_loop_tpg *tl_tpg; |
413 | 414 | ||
414 | /* | 415 | /* |
415 | * Locate the tcm_loop_hba_t pointer | 416 | * Locate the tcm_loop_hba_t pointer |
416 | */ | 417 | */ |
417 | tl_hba = *(struct tcm_loop_hba **)shost_priv(sc->device->host); | 418 | tl_hba = *(struct tcm_loop_hba **)shost_priv(sc->device->host); |
418 | if (!tl_hba) { | 419 | if (!tl_hba) { |
419 | pr_err("Unable to perform device reset without" | 420 | pr_err("Unable to perform device reset without" |
420 | " active I_T Nexus\n"); | 421 | " active I_T Nexus\n"); |
421 | return FAILED; | 422 | return FAILED; |
422 | } | 423 | } |
423 | /* | 424 | /* |
424 | * Locate the tl_tpg pointer from TargetID in sc->device->id | 425 | * Locate the tl_tpg pointer from TargetID in sc->device->id |
425 | */ | 426 | */ |
426 | tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id]; | 427 | tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id]; |
427 | if (tl_tpg) { | 428 | if (tl_tpg) { |
428 | tl_tpg->tl_transport_status = TCM_TRANSPORT_ONLINE; | 429 | tl_tpg->tl_transport_status = TCM_TRANSPORT_ONLINE; |
429 | return SUCCESS; | 430 | return SUCCESS; |
430 | } | 431 | } |
431 | return FAILED; | 432 | return FAILED; |
432 | } | 433 | } |
433 | 434 | ||
434 | static int tcm_loop_slave_alloc(struct scsi_device *sd) | 435 | static int tcm_loop_slave_alloc(struct scsi_device *sd) |
435 | { | 436 | { |
436 | set_bit(QUEUE_FLAG_BIDI, &sd->request_queue->queue_flags); | 437 | set_bit(QUEUE_FLAG_BIDI, &sd->request_queue->queue_flags); |
437 | return 0; | 438 | return 0; |
438 | } | 439 | } |
439 | 440 | ||
440 | static int tcm_loop_slave_configure(struct scsi_device *sd) | 441 | static int tcm_loop_slave_configure(struct scsi_device *sd) |
441 | { | 442 | { |
442 | if (sd->tagged_supported) { | 443 | if (sd->tagged_supported) { |
443 | scsi_activate_tcq(sd, sd->queue_depth); | 444 | scsi_activate_tcq(sd, sd->queue_depth); |
444 | scsi_adjust_queue_depth(sd, MSG_SIMPLE_TAG, | 445 | scsi_adjust_queue_depth(sd, MSG_SIMPLE_TAG, |
445 | sd->host->cmd_per_lun); | 446 | sd->host->cmd_per_lun); |
446 | } else { | 447 | } else { |
447 | scsi_adjust_queue_depth(sd, 0, | 448 | scsi_adjust_queue_depth(sd, 0, |
448 | sd->host->cmd_per_lun); | 449 | sd->host->cmd_per_lun); |
449 | } | 450 | } |
450 | 451 | ||
451 | return 0; | 452 | return 0; |
452 | } | 453 | } |
453 | 454 | ||
454 | static struct scsi_host_template tcm_loop_driver_template = { | 455 | static struct scsi_host_template tcm_loop_driver_template = { |
455 | .show_info = tcm_loop_show_info, | 456 | .show_info = tcm_loop_show_info, |
456 | .proc_name = "tcm_loopback", | 457 | .proc_name = "tcm_loopback", |
457 | .name = "TCM_Loopback", | 458 | .name = "TCM_Loopback", |
458 | .queuecommand = tcm_loop_queuecommand, | 459 | .queuecommand = tcm_loop_queuecommand, |
459 | .change_queue_depth = tcm_loop_change_queue_depth, | 460 | .change_queue_depth = tcm_loop_change_queue_depth, |
460 | .change_queue_type = tcm_loop_change_queue_type, | 461 | .change_queue_type = tcm_loop_change_queue_type, |
461 | .eh_abort_handler = tcm_loop_abort_task, | 462 | .eh_abort_handler = tcm_loop_abort_task, |
462 | .eh_device_reset_handler = tcm_loop_device_reset, | 463 | .eh_device_reset_handler = tcm_loop_device_reset, |
463 | .eh_target_reset_handler = tcm_loop_target_reset, | 464 | .eh_target_reset_handler = tcm_loop_target_reset, |
464 | .can_queue = 1024, | 465 | .can_queue = 1024, |
465 | .this_id = -1, | 466 | .this_id = -1, |
466 | .sg_tablesize = 256, | 467 | .sg_tablesize = 256, |
467 | .cmd_per_lun = 1024, | 468 | .cmd_per_lun = 1024, |
468 | .max_sectors = 0xFFFF, | 469 | .max_sectors = 0xFFFF, |
469 | .use_clustering = DISABLE_CLUSTERING, | 470 | .use_clustering = DISABLE_CLUSTERING, |
470 | .slave_alloc = tcm_loop_slave_alloc, | 471 | .slave_alloc = tcm_loop_slave_alloc, |
471 | .slave_configure = tcm_loop_slave_configure, | 472 | .slave_configure = tcm_loop_slave_configure, |
472 | .module = THIS_MODULE, | 473 | .module = THIS_MODULE, |
473 | }; | 474 | }; |
474 | 475 | ||
475 | static int tcm_loop_driver_probe(struct device *dev) | 476 | static int tcm_loop_driver_probe(struct device *dev) |
476 | { | 477 | { |
477 | struct tcm_loop_hba *tl_hba; | 478 | struct tcm_loop_hba *tl_hba; |
478 | struct Scsi_Host *sh; | 479 | struct Scsi_Host *sh; |
479 | int error, host_prot; | 480 | int error, host_prot; |
480 | 481 | ||
481 | tl_hba = to_tcm_loop_hba(dev); | 482 | tl_hba = to_tcm_loop_hba(dev); |
482 | 483 | ||
483 | sh = scsi_host_alloc(&tcm_loop_driver_template, | 484 | sh = scsi_host_alloc(&tcm_loop_driver_template, |
484 | sizeof(struct tcm_loop_hba)); | 485 | sizeof(struct tcm_loop_hba)); |
485 | if (!sh) { | 486 | if (!sh) { |
486 | pr_err("Unable to allocate struct scsi_host\n"); | 487 | pr_err("Unable to allocate struct scsi_host\n"); |
487 | return -ENODEV; | 488 | return -ENODEV; |
488 | } | 489 | } |
489 | tl_hba->sh = sh; | 490 | tl_hba->sh = sh; |
490 | 491 | ||
491 | /* | 492 | /* |
492 | * Assign the struct tcm_loop_hba pointer to struct Scsi_Host->hostdata | 493 | * Assign the struct tcm_loop_hba pointer to struct Scsi_Host->hostdata |
493 | */ | 494 | */ |
494 | *((struct tcm_loop_hba **)sh->hostdata) = tl_hba; | 495 | *((struct tcm_loop_hba **)sh->hostdata) = tl_hba; |
495 | /* | 496 | /* |
496 | * Setup single ID, Channel and LUN for now.. | 497 | * Setup single ID, Channel and LUN for now.. |
497 | */ | 498 | */ |
498 | sh->max_id = 2; | 499 | sh->max_id = 2; |
499 | sh->max_lun = 0; | 500 | sh->max_lun = 0; |
500 | sh->max_channel = 0; | 501 | sh->max_channel = 0; |
501 | sh->max_cmd_len = TL_SCSI_MAX_CMD_LEN; | 502 | sh->max_cmd_len = TL_SCSI_MAX_CMD_LEN; |
502 | 503 | ||
503 | host_prot = SHOST_DIF_TYPE1_PROTECTION | SHOST_DIF_TYPE2_PROTECTION | | 504 | host_prot = SHOST_DIF_TYPE1_PROTECTION | SHOST_DIF_TYPE2_PROTECTION | |
504 | SHOST_DIF_TYPE3_PROTECTION | SHOST_DIX_TYPE1_PROTECTION | | 505 | SHOST_DIF_TYPE3_PROTECTION | SHOST_DIX_TYPE1_PROTECTION | |
505 | SHOST_DIX_TYPE2_PROTECTION | SHOST_DIX_TYPE3_PROTECTION; | 506 | SHOST_DIX_TYPE2_PROTECTION | SHOST_DIX_TYPE3_PROTECTION; |
506 | 507 | ||
507 | scsi_host_set_prot(sh, host_prot); | 508 | scsi_host_set_prot(sh, host_prot); |
508 | scsi_host_set_guard(sh, SHOST_DIX_GUARD_CRC); | 509 | scsi_host_set_guard(sh, SHOST_DIX_GUARD_CRC); |
509 | 510 | ||
510 | error = scsi_add_host(sh, &tl_hba->dev); | 511 | error = scsi_add_host(sh, &tl_hba->dev); |
511 | if (error) { | 512 | if (error) { |
512 | pr_err("%s: scsi_add_host failed\n", __func__); | 513 | pr_err("%s: scsi_add_host failed\n", __func__); |
513 | scsi_host_put(sh); | 514 | scsi_host_put(sh); |
514 | return -ENODEV; | 515 | return -ENODEV; |
515 | } | 516 | } |
516 | return 0; | 517 | return 0; |
517 | } | 518 | } |
518 | 519 | ||
519 | static int tcm_loop_driver_remove(struct device *dev) | 520 | static int tcm_loop_driver_remove(struct device *dev) |
520 | { | 521 | { |
521 | struct tcm_loop_hba *tl_hba; | 522 | struct tcm_loop_hba *tl_hba; |
522 | struct Scsi_Host *sh; | 523 | struct Scsi_Host *sh; |
523 | 524 | ||
524 | tl_hba = to_tcm_loop_hba(dev); | 525 | tl_hba = to_tcm_loop_hba(dev); |
525 | sh = tl_hba->sh; | 526 | sh = tl_hba->sh; |
526 | 527 | ||
527 | scsi_remove_host(sh); | 528 | scsi_remove_host(sh); |
528 | scsi_host_put(sh); | 529 | scsi_host_put(sh); |
529 | return 0; | 530 | return 0; |
530 | } | 531 | } |
531 | 532 | ||
532 | static void tcm_loop_release_adapter(struct device *dev) | 533 | static void tcm_loop_release_adapter(struct device *dev) |
533 | { | 534 | { |
534 | struct tcm_loop_hba *tl_hba = to_tcm_loop_hba(dev); | 535 | struct tcm_loop_hba *tl_hba = to_tcm_loop_hba(dev); |
535 | 536 | ||
536 | kfree(tl_hba); | 537 | kfree(tl_hba); |
537 | } | 538 | } |
538 | 539 | ||
539 | /* | 540 | /* |
540 | * Called from tcm_loop_make_scsi_hba() in tcm_loop_configfs.c | 541 | * Called from tcm_loop_make_scsi_hba() in tcm_loop_configfs.c |
541 | */ | 542 | */ |
542 | static int tcm_loop_setup_hba_bus(struct tcm_loop_hba *tl_hba, int tcm_loop_host_id) | 543 | static int tcm_loop_setup_hba_bus(struct tcm_loop_hba *tl_hba, int tcm_loop_host_id) |
543 | { | 544 | { |
544 | int ret; | 545 | int ret; |
545 | 546 | ||
546 | tl_hba->dev.bus = &tcm_loop_lld_bus; | 547 | tl_hba->dev.bus = &tcm_loop_lld_bus; |
547 | tl_hba->dev.parent = tcm_loop_primary; | 548 | tl_hba->dev.parent = tcm_loop_primary; |
548 | tl_hba->dev.release = &tcm_loop_release_adapter; | 549 | tl_hba->dev.release = &tcm_loop_release_adapter; |
549 | dev_set_name(&tl_hba->dev, "tcm_loop_adapter_%d", tcm_loop_host_id); | 550 | dev_set_name(&tl_hba->dev, "tcm_loop_adapter_%d", tcm_loop_host_id); |
550 | 551 | ||
551 | ret = device_register(&tl_hba->dev); | 552 | ret = device_register(&tl_hba->dev); |
552 | if (ret) { | 553 | if (ret) { |
553 | pr_err("device_register() failed for" | 554 | pr_err("device_register() failed for" |
554 | " tl_hba->dev: %d\n", ret); | 555 | " tl_hba->dev: %d\n", ret); |
555 | return -ENODEV; | 556 | return -ENODEV; |
556 | } | 557 | } |
557 | 558 | ||
558 | return 0; | 559 | return 0; |
559 | } | 560 | } |
560 | 561 | ||
561 | /* | 562 | /* |
562 | * Called from tcm_loop_fabric_init() in tcl_loop_fabric.c to load the emulated | 563 | * Called from tcm_loop_fabric_init() in tcl_loop_fabric.c to load the emulated |
563 | * tcm_loop SCSI bus. | 564 | * tcm_loop SCSI bus. |
564 | */ | 565 | */ |
565 | static int tcm_loop_alloc_core_bus(void) | 566 | static int tcm_loop_alloc_core_bus(void) |
566 | { | 567 | { |
567 | int ret; | 568 | int ret; |
568 | 569 | ||
569 | tcm_loop_primary = root_device_register("tcm_loop_0"); | 570 | tcm_loop_primary = root_device_register("tcm_loop_0"); |
570 | if (IS_ERR(tcm_loop_primary)) { | 571 | if (IS_ERR(tcm_loop_primary)) { |
571 | pr_err("Unable to allocate tcm_loop_primary\n"); | 572 | pr_err("Unable to allocate tcm_loop_primary\n"); |
572 | return PTR_ERR(tcm_loop_primary); | 573 | return PTR_ERR(tcm_loop_primary); |
573 | } | 574 | } |
574 | 575 | ||
575 | ret = bus_register(&tcm_loop_lld_bus); | 576 | ret = bus_register(&tcm_loop_lld_bus); |
576 | if (ret) { | 577 | if (ret) { |
577 | pr_err("bus_register() failed for tcm_loop_lld_bus\n"); | 578 | pr_err("bus_register() failed for tcm_loop_lld_bus\n"); |
578 | goto dev_unreg; | 579 | goto dev_unreg; |
579 | } | 580 | } |
580 | 581 | ||
581 | ret = driver_register(&tcm_loop_driverfs); | 582 | ret = driver_register(&tcm_loop_driverfs); |
582 | if (ret) { | 583 | if (ret) { |
583 | pr_err("driver_register() failed for" | 584 | pr_err("driver_register() failed for" |
584 | "tcm_loop_driverfs\n"); | 585 | "tcm_loop_driverfs\n"); |
585 | goto bus_unreg; | 586 | goto bus_unreg; |
586 | } | 587 | } |
587 | 588 | ||
588 | pr_debug("Initialized TCM Loop Core Bus\n"); | 589 | pr_debug("Initialized TCM Loop Core Bus\n"); |
589 | return ret; | 590 | return ret; |
590 | 591 | ||
591 | bus_unreg: | 592 | bus_unreg: |
592 | bus_unregister(&tcm_loop_lld_bus); | 593 | bus_unregister(&tcm_loop_lld_bus); |
593 | dev_unreg: | 594 | dev_unreg: |
594 | root_device_unregister(tcm_loop_primary); | 595 | root_device_unregister(tcm_loop_primary); |
595 | return ret; | 596 | return ret; |
596 | } | 597 | } |
597 | 598 | ||
598 | static void tcm_loop_release_core_bus(void) | 599 | static void tcm_loop_release_core_bus(void) |
599 | { | 600 | { |
600 | driver_unregister(&tcm_loop_driverfs); | 601 | driver_unregister(&tcm_loop_driverfs); |
601 | bus_unregister(&tcm_loop_lld_bus); | 602 | bus_unregister(&tcm_loop_lld_bus); |
602 | root_device_unregister(tcm_loop_primary); | 603 | root_device_unregister(tcm_loop_primary); |
603 | 604 | ||
604 | pr_debug("Releasing TCM Loop Core BUS\n"); | 605 | pr_debug("Releasing TCM Loop Core BUS\n"); |
605 | } | 606 | } |
606 | 607 | ||
607 | static char *tcm_loop_get_fabric_name(void) | 608 | static char *tcm_loop_get_fabric_name(void) |
608 | { | 609 | { |
609 | return "loopback"; | 610 | return "loopback"; |
610 | } | 611 | } |
611 | 612 | ||
612 | static u8 tcm_loop_get_fabric_proto_ident(struct se_portal_group *se_tpg) | 613 | static u8 tcm_loop_get_fabric_proto_ident(struct se_portal_group *se_tpg) |
613 | { | 614 | { |
614 | struct tcm_loop_tpg *tl_tpg = se_tpg->se_tpg_fabric_ptr; | 615 | struct tcm_loop_tpg *tl_tpg = se_tpg->se_tpg_fabric_ptr; |
615 | struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba; | 616 | struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba; |
616 | /* | 617 | /* |
617 | * tl_proto_id is set at tcm_loop_configfs.c:tcm_loop_make_scsi_hba() | 618 | * tl_proto_id is set at tcm_loop_configfs.c:tcm_loop_make_scsi_hba() |
618 | * time based on the protocol dependent prefix of the passed configfs group. | 619 | * time based on the protocol dependent prefix of the passed configfs group. |
619 | * | 620 | * |
620 | * Based upon tl_proto_id, TCM_Loop emulates the requested fabric | 621 | * Based upon tl_proto_id, TCM_Loop emulates the requested fabric |
621 | * ProtocolID using target_core_fabric_lib.c symbols. | 622 | * ProtocolID using target_core_fabric_lib.c symbols. |
622 | */ | 623 | */ |
623 | switch (tl_hba->tl_proto_id) { | 624 | switch (tl_hba->tl_proto_id) { |
624 | case SCSI_PROTOCOL_SAS: | 625 | case SCSI_PROTOCOL_SAS: |
625 | return sas_get_fabric_proto_ident(se_tpg); | 626 | return sas_get_fabric_proto_ident(se_tpg); |
626 | case SCSI_PROTOCOL_FCP: | 627 | case SCSI_PROTOCOL_FCP: |
627 | return fc_get_fabric_proto_ident(se_tpg); | 628 | return fc_get_fabric_proto_ident(se_tpg); |
628 | case SCSI_PROTOCOL_ISCSI: | 629 | case SCSI_PROTOCOL_ISCSI: |
629 | return iscsi_get_fabric_proto_ident(se_tpg); | 630 | return iscsi_get_fabric_proto_ident(se_tpg); |
630 | default: | 631 | default: |
631 | pr_err("Unknown tl_proto_id: 0x%02x, using" | 632 | pr_err("Unknown tl_proto_id: 0x%02x, using" |
632 | " SAS emulation\n", tl_hba->tl_proto_id); | 633 | " SAS emulation\n", tl_hba->tl_proto_id); |
633 | break; | 634 | break; |
634 | } | 635 | } |
635 | 636 | ||
636 | return sas_get_fabric_proto_ident(se_tpg); | 637 | return sas_get_fabric_proto_ident(se_tpg); |
637 | } | 638 | } |
638 | 639 | ||
639 | static char *tcm_loop_get_endpoint_wwn(struct se_portal_group *se_tpg) | 640 | static char *tcm_loop_get_endpoint_wwn(struct se_portal_group *se_tpg) |
640 | { | 641 | { |
641 | struct tcm_loop_tpg *tl_tpg = se_tpg->se_tpg_fabric_ptr; | 642 | struct tcm_loop_tpg *tl_tpg = se_tpg->se_tpg_fabric_ptr; |
642 | /* | 643 | /* |
643 | * Return the passed NAA identifier for the SAS Target Port | 644 | * Return the passed NAA identifier for the SAS Target Port |
644 | */ | 645 | */ |
645 | return &tl_tpg->tl_hba->tl_wwn_address[0]; | 646 | return &tl_tpg->tl_hba->tl_wwn_address[0]; |
646 | } | 647 | } |
647 | 648 | ||
648 | static u16 tcm_loop_get_tag(struct se_portal_group *se_tpg) | 649 | static u16 tcm_loop_get_tag(struct se_portal_group *se_tpg) |
649 | { | 650 | { |
650 | struct tcm_loop_tpg *tl_tpg = se_tpg->se_tpg_fabric_ptr; | 651 | struct tcm_loop_tpg *tl_tpg = se_tpg->se_tpg_fabric_ptr; |
651 | /* | 652 | /* |
652 | * This Tag is used when forming SCSI Name identifier in EVPD=1 0x83 | 653 | * This Tag is used when forming SCSI Name identifier in EVPD=1 0x83 |
653 | * to represent the SCSI Target Port. | 654 | * to represent the SCSI Target Port. |
654 | */ | 655 | */ |
655 | return tl_tpg->tl_tpgt; | 656 | return tl_tpg->tl_tpgt; |
656 | } | 657 | } |
657 | 658 | ||
658 | static u32 tcm_loop_get_default_depth(struct se_portal_group *se_tpg) | 659 | static u32 tcm_loop_get_default_depth(struct se_portal_group *se_tpg) |
659 | { | 660 | { |
660 | return 1; | 661 | return 1; |
661 | } | 662 | } |
662 | 663 | ||
663 | static u32 tcm_loop_get_pr_transport_id( | 664 | static u32 tcm_loop_get_pr_transport_id( |
664 | struct se_portal_group *se_tpg, | 665 | struct se_portal_group *se_tpg, |
665 | struct se_node_acl *se_nacl, | 666 | struct se_node_acl *se_nacl, |
666 | struct t10_pr_registration *pr_reg, | 667 | struct t10_pr_registration *pr_reg, |
667 | int *format_code, | 668 | int *format_code, |
668 | unsigned char *buf) | 669 | unsigned char *buf) |
669 | { | 670 | { |
670 | struct tcm_loop_tpg *tl_tpg = se_tpg->se_tpg_fabric_ptr; | 671 | struct tcm_loop_tpg *tl_tpg = se_tpg->se_tpg_fabric_ptr; |
671 | struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba; | 672 | struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba; |
672 | 673 | ||
673 | switch (tl_hba->tl_proto_id) { | 674 | switch (tl_hba->tl_proto_id) { |
674 | case SCSI_PROTOCOL_SAS: | 675 | case SCSI_PROTOCOL_SAS: |
675 | return sas_get_pr_transport_id(se_tpg, se_nacl, pr_reg, | 676 | return sas_get_pr_transport_id(se_tpg, se_nacl, pr_reg, |
676 | format_code, buf); | 677 | format_code, buf); |
677 | case SCSI_PROTOCOL_FCP: | 678 | case SCSI_PROTOCOL_FCP: |
678 | return fc_get_pr_transport_id(se_tpg, se_nacl, pr_reg, | 679 | return fc_get_pr_transport_id(se_tpg, se_nacl, pr_reg, |
679 | format_code, buf); | 680 | format_code, buf); |
680 | case SCSI_PROTOCOL_ISCSI: | 681 | case SCSI_PROTOCOL_ISCSI: |
681 | return iscsi_get_pr_transport_id(se_tpg, se_nacl, pr_reg, | 682 | return iscsi_get_pr_transport_id(se_tpg, se_nacl, pr_reg, |
682 | format_code, buf); | 683 | format_code, buf); |
683 | default: | 684 | default: |
684 | pr_err("Unknown tl_proto_id: 0x%02x, using" | 685 | pr_err("Unknown tl_proto_id: 0x%02x, using" |
685 | " SAS emulation\n", tl_hba->tl_proto_id); | 686 | " SAS emulation\n", tl_hba->tl_proto_id); |
686 | break; | 687 | break; |
687 | } | 688 | } |
688 | 689 | ||
689 | return sas_get_pr_transport_id(se_tpg, se_nacl, pr_reg, | 690 | return sas_get_pr_transport_id(se_tpg, se_nacl, pr_reg, |
690 | format_code, buf); | 691 | format_code, buf); |
691 | } | 692 | } |
692 | 693 | ||
693 | static u32 tcm_loop_get_pr_transport_id_len( | 694 | static u32 tcm_loop_get_pr_transport_id_len( |
694 | struct se_portal_group *se_tpg, | 695 | struct se_portal_group *se_tpg, |
695 | struct se_node_acl *se_nacl, | 696 | struct se_node_acl *se_nacl, |
696 | struct t10_pr_registration *pr_reg, | 697 | struct t10_pr_registration *pr_reg, |
697 | int *format_code) | 698 | int *format_code) |
698 | { | 699 | { |
699 | struct tcm_loop_tpg *tl_tpg = se_tpg->se_tpg_fabric_ptr; | 700 | struct tcm_loop_tpg *tl_tpg = se_tpg->se_tpg_fabric_ptr; |
700 | struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba; | 701 | struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba; |
701 | 702 | ||
702 | switch (tl_hba->tl_proto_id) { | 703 | switch (tl_hba->tl_proto_id) { |
703 | case SCSI_PROTOCOL_SAS: | 704 | case SCSI_PROTOCOL_SAS: |
704 | return sas_get_pr_transport_id_len(se_tpg, se_nacl, pr_reg, | 705 | return sas_get_pr_transport_id_len(se_tpg, se_nacl, pr_reg, |
705 | format_code); | 706 | format_code); |
706 | case SCSI_PROTOCOL_FCP: | 707 | case SCSI_PROTOCOL_FCP: |
707 | return fc_get_pr_transport_id_len(se_tpg, se_nacl, pr_reg, | 708 | return fc_get_pr_transport_id_len(se_tpg, se_nacl, pr_reg, |
708 | format_code); | 709 | format_code); |
709 | case SCSI_PROTOCOL_ISCSI: | 710 | case SCSI_PROTOCOL_ISCSI: |
710 | return iscsi_get_pr_transport_id_len(se_tpg, se_nacl, pr_reg, | 711 | return iscsi_get_pr_transport_id_len(se_tpg, se_nacl, pr_reg, |
711 | format_code); | 712 | format_code); |
712 | default: | 713 | default: |
713 | pr_err("Unknown tl_proto_id: 0x%02x, using" | 714 | pr_err("Unknown tl_proto_id: 0x%02x, using" |
714 | " SAS emulation\n", tl_hba->tl_proto_id); | 715 | " SAS emulation\n", tl_hba->tl_proto_id); |
715 | break; | 716 | break; |
716 | } | 717 | } |
717 | 718 | ||
718 | return sas_get_pr_transport_id_len(se_tpg, se_nacl, pr_reg, | 719 | return sas_get_pr_transport_id_len(se_tpg, se_nacl, pr_reg, |
719 | format_code); | 720 | format_code); |
720 | } | 721 | } |
721 | 722 | ||
722 | /* | 723 | /* |
723 | * Used for handling SCSI fabric dependent TransportIDs in SPC-3 and above | 724 | * Used for handling SCSI fabric dependent TransportIDs in SPC-3 and above |
724 | * Persistent Reservation SPEC_I_PT=1 and PROUT REGISTER_AND_MOVE operations. | 725 | * Persistent Reservation SPEC_I_PT=1 and PROUT REGISTER_AND_MOVE operations. |
725 | */ | 726 | */ |
726 | static char *tcm_loop_parse_pr_out_transport_id( | 727 | static char *tcm_loop_parse_pr_out_transport_id( |
727 | struct se_portal_group *se_tpg, | 728 | struct se_portal_group *se_tpg, |
728 | const char *buf, | 729 | const char *buf, |
729 | u32 *out_tid_len, | 730 | u32 *out_tid_len, |
730 | char **port_nexus_ptr) | 731 | char **port_nexus_ptr) |
731 | { | 732 | { |
732 | struct tcm_loop_tpg *tl_tpg = se_tpg->se_tpg_fabric_ptr; | 733 | struct tcm_loop_tpg *tl_tpg = se_tpg->se_tpg_fabric_ptr; |
733 | struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba; | 734 | struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba; |
734 | 735 | ||
735 | switch (tl_hba->tl_proto_id) { | 736 | switch (tl_hba->tl_proto_id) { |
736 | case SCSI_PROTOCOL_SAS: | 737 | case SCSI_PROTOCOL_SAS: |
737 | return sas_parse_pr_out_transport_id(se_tpg, buf, out_tid_len, | 738 | return sas_parse_pr_out_transport_id(se_tpg, buf, out_tid_len, |
738 | port_nexus_ptr); | 739 | port_nexus_ptr); |
739 | case SCSI_PROTOCOL_FCP: | 740 | case SCSI_PROTOCOL_FCP: |
740 | return fc_parse_pr_out_transport_id(se_tpg, buf, out_tid_len, | 741 | return fc_parse_pr_out_transport_id(se_tpg, buf, out_tid_len, |
741 | port_nexus_ptr); | 742 | port_nexus_ptr); |
742 | case SCSI_PROTOCOL_ISCSI: | 743 | case SCSI_PROTOCOL_ISCSI: |
743 | return iscsi_parse_pr_out_transport_id(se_tpg, buf, out_tid_len, | 744 | return iscsi_parse_pr_out_transport_id(se_tpg, buf, out_tid_len, |
744 | port_nexus_ptr); | 745 | port_nexus_ptr); |
745 | default: | 746 | default: |
746 | pr_err("Unknown tl_proto_id: 0x%02x, using" | 747 | pr_err("Unknown tl_proto_id: 0x%02x, using" |
747 | " SAS emulation\n", tl_hba->tl_proto_id); | 748 | " SAS emulation\n", tl_hba->tl_proto_id); |
748 | break; | 749 | break; |
749 | } | 750 | } |
750 | 751 | ||
751 | return sas_parse_pr_out_transport_id(se_tpg, buf, out_tid_len, | 752 | return sas_parse_pr_out_transport_id(se_tpg, buf, out_tid_len, |
752 | port_nexus_ptr); | 753 | port_nexus_ptr); |
753 | } | 754 | } |
754 | 755 | ||
755 | /* | 756 | /* |
756 | * Returning (1) here allows for target_core_mod struct se_node_acl to be generated | 757 | * Returning (1) here allows for target_core_mod struct se_node_acl to be generated |
757 | * based upon the incoming fabric dependent SCSI Initiator Port | 758 | * based upon the incoming fabric dependent SCSI Initiator Port |
758 | */ | 759 | */ |
759 | static int tcm_loop_check_demo_mode(struct se_portal_group *se_tpg) | 760 | static int tcm_loop_check_demo_mode(struct se_portal_group *se_tpg) |
760 | { | 761 | { |
761 | return 1; | 762 | return 1; |
762 | } | 763 | } |
763 | 764 | ||
764 | static int tcm_loop_check_demo_mode_cache(struct se_portal_group *se_tpg) | 765 | static int tcm_loop_check_demo_mode_cache(struct se_portal_group *se_tpg) |
765 | { | 766 | { |
766 | return 0; | 767 | return 0; |
767 | } | 768 | } |
768 | 769 | ||
769 | /* | 770 | /* |
770 | * Allow I_T Nexus full READ-WRITE access without explict Initiator Node ACLs for | 771 | * Allow I_T Nexus full READ-WRITE access without explict Initiator Node ACLs for |
771 | * local virtual Linux/SCSI LLD passthrough into VM hypervisor guest | 772 | * local virtual Linux/SCSI LLD passthrough into VM hypervisor guest |
772 | */ | 773 | */ |
773 | static int tcm_loop_check_demo_mode_write_protect(struct se_portal_group *se_tpg) | 774 | static int tcm_loop_check_demo_mode_write_protect(struct se_portal_group *se_tpg) |
774 | { | 775 | { |
775 | return 0; | 776 | return 0; |
776 | } | 777 | } |
777 | 778 | ||
778 | /* | 779 | /* |
779 | * Because TCM_Loop does not use explict ACLs and MappedLUNs, this will | 780 | * Because TCM_Loop does not use explict ACLs and MappedLUNs, this will |
780 | * never be called for TCM_Loop by target_core_fabric_configfs.c code. | 781 | * never be called for TCM_Loop by target_core_fabric_configfs.c code. |
781 | * It has been added here as a nop for target_fabric_tf_ops_check() | 782 | * It has been added here as a nop for target_fabric_tf_ops_check() |
782 | */ | 783 | */ |
783 | static int tcm_loop_check_prod_mode_write_protect(struct se_portal_group *se_tpg) | 784 | static int tcm_loop_check_prod_mode_write_protect(struct se_portal_group *se_tpg) |
784 | { | 785 | { |
785 | return 0; | 786 | return 0; |
786 | } | 787 | } |
787 | 788 | ||
788 | static struct se_node_acl *tcm_loop_tpg_alloc_fabric_acl( | 789 | static struct se_node_acl *tcm_loop_tpg_alloc_fabric_acl( |
789 | struct se_portal_group *se_tpg) | 790 | struct se_portal_group *se_tpg) |
790 | { | 791 | { |
791 | struct tcm_loop_nacl *tl_nacl; | 792 | struct tcm_loop_nacl *tl_nacl; |
792 | 793 | ||
793 | tl_nacl = kzalloc(sizeof(struct tcm_loop_nacl), GFP_KERNEL); | 794 | tl_nacl = kzalloc(sizeof(struct tcm_loop_nacl), GFP_KERNEL); |
794 | if (!tl_nacl) { | 795 | if (!tl_nacl) { |
795 | pr_err("Unable to allocate struct tcm_loop_nacl\n"); | 796 | pr_err("Unable to allocate struct tcm_loop_nacl\n"); |
796 | return NULL; | 797 | return NULL; |
797 | } | 798 | } |
798 | 799 | ||
799 | return &tl_nacl->se_node_acl; | 800 | return &tl_nacl->se_node_acl; |
800 | } | 801 | } |
801 | 802 | ||
802 | static void tcm_loop_tpg_release_fabric_acl( | 803 | static void tcm_loop_tpg_release_fabric_acl( |
803 | struct se_portal_group *se_tpg, | 804 | struct se_portal_group *se_tpg, |
804 | struct se_node_acl *se_nacl) | 805 | struct se_node_acl *se_nacl) |
805 | { | 806 | { |
806 | struct tcm_loop_nacl *tl_nacl = container_of(se_nacl, | 807 | struct tcm_loop_nacl *tl_nacl = container_of(se_nacl, |
807 | struct tcm_loop_nacl, se_node_acl); | 808 | struct tcm_loop_nacl, se_node_acl); |
808 | 809 | ||
809 | kfree(tl_nacl); | 810 | kfree(tl_nacl); |
810 | } | 811 | } |
811 | 812 | ||
812 | static u32 tcm_loop_get_inst_index(struct se_portal_group *se_tpg) | 813 | static u32 tcm_loop_get_inst_index(struct se_portal_group *se_tpg) |
813 | { | 814 | { |
814 | return 1; | 815 | return 1; |
815 | } | 816 | } |
816 | 817 | ||
817 | static u32 tcm_loop_sess_get_index(struct se_session *se_sess) | 818 | static u32 tcm_loop_sess_get_index(struct se_session *se_sess) |
818 | { | 819 | { |
819 | return 1; | 820 | return 1; |
820 | } | 821 | } |
821 | 822 | ||
822 | static void tcm_loop_set_default_node_attributes(struct se_node_acl *se_acl) | 823 | static void tcm_loop_set_default_node_attributes(struct se_node_acl *se_acl) |
823 | { | 824 | { |
824 | return; | 825 | return; |
825 | } | 826 | } |
826 | 827 | ||
827 | static u32 tcm_loop_get_task_tag(struct se_cmd *se_cmd) | 828 | static u32 tcm_loop_get_task_tag(struct se_cmd *se_cmd) |
828 | { | 829 | { |
829 | struct tcm_loop_cmd *tl_cmd = container_of(se_cmd, | 830 | struct tcm_loop_cmd *tl_cmd = container_of(se_cmd, |
830 | struct tcm_loop_cmd, tl_se_cmd); | 831 | struct tcm_loop_cmd, tl_se_cmd); |
831 | 832 | ||
832 | return tl_cmd->sc_cmd_tag; | 833 | return tl_cmd->sc_cmd_tag; |
833 | } | 834 | } |
834 | 835 | ||
835 | static int tcm_loop_get_cmd_state(struct se_cmd *se_cmd) | 836 | static int tcm_loop_get_cmd_state(struct se_cmd *se_cmd) |
836 | { | 837 | { |
837 | struct tcm_loop_cmd *tl_cmd = container_of(se_cmd, | 838 | struct tcm_loop_cmd *tl_cmd = container_of(se_cmd, |
838 | struct tcm_loop_cmd, tl_se_cmd); | 839 | struct tcm_loop_cmd, tl_se_cmd); |
839 | 840 | ||
840 | return tl_cmd->sc_cmd_state; | 841 | return tl_cmd->sc_cmd_state; |
841 | } | 842 | } |
842 | 843 | ||
843 | static int tcm_loop_shutdown_session(struct se_session *se_sess) | 844 | static int tcm_loop_shutdown_session(struct se_session *se_sess) |
844 | { | 845 | { |
845 | return 0; | 846 | return 0; |
846 | } | 847 | } |
847 | 848 | ||
848 | static void tcm_loop_close_session(struct se_session *se_sess) | 849 | static void tcm_loop_close_session(struct se_session *se_sess) |
849 | { | 850 | { |
850 | return; | 851 | return; |
851 | }; | 852 | }; |
852 | 853 | ||
853 | static int tcm_loop_write_pending(struct se_cmd *se_cmd) | 854 | static int tcm_loop_write_pending(struct se_cmd *se_cmd) |
854 | { | 855 | { |
855 | /* | 856 | /* |
856 | * Since Linux/SCSI has already sent down a struct scsi_cmnd | 857 | * Since Linux/SCSI has already sent down a struct scsi_cmnd |
857 | * sc->sc_data_direction of DMA_TO_DEVICE with struct scatterlist array | 858 | * sc->sc_data_direction of DMA_TO_DEVICE with struct scatterlist array |
858 | * memory, and memory has already been mapped to struct se_cmd->t_mem_list | 859 | * memory, and memory has already been mapped to struct se_cmd->t_mem_list |
859 | * format with transport_generic_map_mem_to_cmd(). | 860 | * format with transport_generic_map_mem_to_cmd(). |
860 | * | 861 | * |
861 | * We now tell TCM to add this WRITE CDB directly into the TCM storage | 862 | * We now tell TCM to add this WRITE CDB directly into the TCM storage |
862 | * object execution queue. | 863 | * object execution queue. |
863 | */ | 864 | */ |
864 | target_execute_cmd(se_cmd); | 865 | target_execute_cmd(se_cmd); |
865 | return 0; | 866 | return 0; |
866 | } | 867 | } |
867 | 868 | ||
868 | static int tcm_loop_write_pending_status(struct se_cmd *se_cmd) | 869 | static int tcm_loop_write_pending_status(struct se_cmd *se_cmd) |
869 | { | 870 | { |
870 | return 0; | 871 | return 0; |
871 | } | 872 | } |
872 | 873 | ||
873 | static int tcm_loop_queue_data_in(struct se_cmd *se_cmd) | 874 | static int tcm_loop_queue_data_in(struct se_cmd *se_cmd) |
874 | { | 875 | { |
875 | struct tcm_loop_cmd *tl_cmd = container_of(se_cmd, | 876 | struct tcm_loop_cmd *tl_cmd = container_of(se_cmd, |
876 | struct tcm_loop_cmd, tl_se_cmd); | 877 | struct tcm_loop_cmd, tl_se_cmd); |
877 | struct scsi_cmnd *sc = tl_cmd->sc; | 878 | struct scsi_cmnd *sc = tl_cmd->sc; |
878 | 879 | ||
879 | pr_debug("tcm_loop_queue_data_in() called for scsi_cmnd: %p" | 880 | pr_debug("tcm_loop_queue_data_in() called for scsi_cmnd: %p" |
880 | " cdb: 0x%02x\n", sc, sc->cmnd[0]); | 881 | " cdb: 0x%02x\n", sc, sc->cmnd[0]); |
881 | 882 | ||
882 | sc->result = SAM_STAT_GOOD; | 883 | sc->result = SAM_STAT_GOOD; |
883 | set_host_byte(sc, DID_OK); | 884 | set_host_byte(sc, DID_OK); |
884 | if ((se_cmd->se_cmd_flags & SCF_OVERFLOW_BIT) || | 885 | if ((se_cmd->se_cmd_flags & SCF_OVERFLOW_BIT) || |
885 | (se_cmd->se_cmd_flags & SCF_UNDERFLOW_BIT)) | 886 | (se_cmd->se_cmd_flags & SCF_UNDERFLOW_BIT)) |
886 | scsi_set_resid(sc, se_cmd->residual_count); | 887 | scsi_set_resid(sc, se_cmd->residual_count); |
887 | sc->scsi_done(sc); | 888 | sc->scsi_done(sc); |
888 | return 0; | 889 | return 0; |
889 | } | 890 | } |
890 | 891 | ||
891 | static int tcm_loop_queue_status(struct se_cmd *se_cmd) | 892 | static int tcm_loop_queue_status(struct se_cmd *se_cmd) |
892 | { | 893 | { |
893 | struct tcm_loop_cmd *tl_cmd = container_of(se_cmd, | 894 | struct tcm_loop_cmd *tl_cmd = container_of(se_cmd, |
894 | struct tcm_loop_cmd, tl_se_cmd); | 895 | struct tcm_loop_cmd, tl_se_cmd); |
895 | struct scsi_cmnd *sc = tl_cmd->sc; | 896 | struct scsi_cmnd *sc = tl_cmd->sc; |
896 | 897 | ||
897 | pr_debug("tcm_loop_queue_status() called for scsi_cmnd: %p" | 898 | pr_debug("tcm_loop_queue_status() called for scsi_cmnd: %p" |
898 | " cdb: 0x%02x\n", sc, sc->cmnd[0]); | 899 | " cdb: 0x%02x\n", sc, sc->cmnd[0]); |
899 | 900 | ||
900 | if (se_cmd->sense_buffer && | 901 | if (se_cmd->sense_buffer && |
901 | ((se_cmd->se_cmd_flags & SCF_TRANSPORT_TASK_SENSE) || | 902 | ((se_cmd->se_cmd_flags & SCF_TRANSPORT_TASK_SENSE) || |
902 | (se_cmd->se_cmd_flags & SCF_EMULATED_TASK_SENSE))) { | 903 | (se_cmd->se_cmd_flags & SCF_EMULATED_TASK_SENSE))) { |
903 | 904 | ||
904 | memcpy(sc->sense_buffer, se_cmd->sense_buffer, | 905 | memcpy(sc->sense_buffer, se_cmd->sense_buffer, |
905 | SCSI_SENSE_BUFFERSIZE); | 906 | SCSI_SENSE_BUFFERSIZE); |
906 | sc->result = SAM_STAT_CHECK_CONDITION; | 907 | sc->result = SAM_STAT_CHECK_CONDITION; |
907 | set_driver_byte(sc, DRIVER_SENSE); | 908 | set_driver_byte(sc, DRIVER_SENSE); |
908 | } else | 909 | } else |
909 | sc->result = se_cmd->scsi_status; | 910 | sc->result = se_cmd->scsi_status; |
910 | 911 | ||
911 | set_host_byte(sc, DID_OK); | 912 | set_host_byte(sc, DID_OK); |
912 | if ((se_cmd->se_cmd_flags & SCF_OVERFLOW_BIT) || | 913 | if ((se_cmd->se_cmd_flags & SCF_OVERFLOW_BIT) || |
913 | (se_cmd->se_cmd_flags & SCF_UNDERFLOW_BIT)) | 914 | (se_cmd->se_cmd_flags & SCF_UNDERFLOW_BIT)) |
914 | scsi_set_resid(sc, se_cmd->residual_count); | 915 | scsi_set_resid(sc, se_cmd->residual_count); |
915 | sc->scsi_done(sc); | 916 | sc->scsi_done(sc); |
916 | return 0; | 917 | return 0; |
917 | } | 918 | } |
918 | 919 | ||
919 | static void tcm_loop_queue_tm_rsp(struct se_cmd *se_cmd) | 920 | static void tcm_loop_queue_tm_rsp(struct se_cmd *se_cmd) |
920 | { | 921 | { |
921 | struct se_tmr_req *se_tmr = se_cmd->se_tmr_req; | 922 | struct se_tmr_req *se_tmr = se_cmd->se_tmr_req; |
922 | struct tcm_loop_tmr *tl_tmr = se_tmr->fabric_tmr_ptr; | 923 | struct tcm_loop_tmr *tl_tmr = se_tmr->fabric_tmr_ptr; |
923 | /* | 924 | /* |
924 | * The SCSI EH thread will be sleeping on se_tmr->tl_tmr_wait, go ahead | 925 | * The SCSI EH thread will be sleeping on se_tmr->tl_tmr_wait, go ahead |
925 | * and wake up the wait_queue_head_t in tcm_loop_device_reset() | 926 | * and wake up the wait_queue_head_t in tcm_loop_device_reset() |
926 | */ | 927 | */ |
927 | atomic_set(&tl_tmr->tmr_complete, 1); | 928 | atomic_set(&tl_tmr->tmr_complete, 1); |
928 | wake_up(&tl_tmr->tl_tmr_wait); | 929 | wake_up(&tl_tmr->tl_tmr_wait); |
929 | } | 930 | } |
930 | 931 | ||
931 | static void tcm_loop_aborted_task(struct se_cmd *se_cmd) | 932 | static void tcm_loop_aborted_task(struct se_cmd *se_cmd) |
932 | { | 933 | { |
933 | return; | 934 | return; |
934 | } | 935 | } |
935 | 936 | ||
936 | static char *tcm_loop_dump_proto_id(struct tcm_loop_hba *tl_hba) | 937 | static char *tcm_loop_dump_proto_id(struct tcm_loop_hba *tl_hba) |
937 | { | 938 | { |
938 | switch (tl_hba->tl_proto_id) { | 939 | switch (tl_hba->tl_proto_id) { |
939 | case SCSI_PROTOCOL_SAS: | 940 | case SCSI_PROTOCOL_SAS: |
940 | return "SAS"; | 941 | return "SAS"; |
941 | case SCSI_PROTOCOL_FCP: | 942 | case SCSI_PROTOCOL_FCP: |
942 | return "FCP"; | 943 | return "FCP"; |
943 | case SCSI_PROTOCOL_ISCSI: | 944 | case SCSI_PROTOCOL_ISCSI: |
944 | return "iSCSI"; | 945 | return "iSCSI"; |
945 | default: | 946 | default: |
946 | break; | 947 | break; |
947 | } | 948 | } |
948 | 949 | ||
949 | return "Unknown"; | 950 | return "Unknown"; |
950 | } | 951 | } |
951 | 952 | ||
952 | /* Start items for tcm_loop_port_cit */ | 953 | /* Start items for tcm_loop_port_cit */ |
953 | 954 | ||
954 | static int tcm_loop_port_link( | 955 | static int tcm_loop_port_link( |
955 | struct se_portal_group *se_tpg, | 956 | struct se_portal_group *se_tpg, |
956 | struct se_lun *lun) | 957 | struct se_lun *lun) |
957 | { | 958 | { |
958 | struct tcm_loop_tpg *tl_tpg = container_of(se_tpg, | 959 | struct tcm_loop_tpg *tl_tpg = container_of(se_tpg, |
959 | struct tcm_loop_tpg, tl_se_tpg); | 960 | struct tcm_loop_tpg, tl_se_tpg); |
960 | struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba; | 961 | struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba; |
961 | 962 | ||
962 | atomic_inc(&tl_tpg->tl_tpg_port_count); | 963 | atomic_inc(&tl_tpg->tl_tpg_port_count); |
963 | smp_mb__after_atomic(); | 964 | smp_mb__after_atomic(); |
964 | /* | 965 | /* |
965 | * Add Linux/SCSI struct scsi_device by HCTL | 966 | * Add Linux/SCSI struct scsi_device by HCTL |
966 | */ | 967 | */ |
967 | scsi_add_device(tl_hba->sh, 0, tl_tpg->tl_tpgt, lun->unpacked_lun); | 968 | scsi_add_device(tl_hba->sh, 0, tl_tpg->tl_tpgt, lun->unpacked_lun); |
968 | 969 | ||
969 | pr_debug("TCM_Loop_ConfigFS: Port Link Successful\n"); | 970 | pr_debug("TCM_Loop_ConfigFS: Port Link Successful\n"); |
970 | return 0; | 971 | return 0; |
971 | } | 972 | } |
972 | 973 | ||
973 | static void tcm_loop_port_unlink( | 974 | static void tcm_loop_port_unlink( |
974 | struct se_portal_group *se_tpg, | 975 | struct se_portal_group *se_tpg, |
975 | struct se_lun *se_lun) | 976 | struct se_lun *se_lun) |
976 | { | 977 | { |
977 | struct scsi_device *sd; | 978 | struct scsi_device *sd; |
978 | struct tcm_loop_hba *tl_hba; | 979 | struct tcm_loop_hba *tl_hba; |
979 | struct tcm_loop_tpg *tl_tpg; | 980 | struct tcm_loop_tpg *tl_tpg; |
980 | 981 | ||
981 | tl_tpg = container_of(se_tpg, struct tcm_loop_tpg, tl_se_tpg); | 982 | tl_tpg = container_of(se_tpg, struct tcm_loop_tpg, tl_se_tpg); |
982 | tl_hba = tl_tpg->tl_hba; | 983 | tl_hba = tl_tpg->tl_hba; |
983 | 984 | ||
984 | sd = scsi_device_lookup(tl_hba->sh, 0, tl_tpg->tl_tpgt, | 985 | sd = scsi_device_lookup(tl_hba->sh, 0, tl_tpg->tl_tpgt, |
985 | se_lun->unpacked_lun); | 986 | se_lun->unpacked_lun); |
986 | if (!sd) { | 987 | if (!sd) { |
987 | pr_err("Unable to locate struct scsi_device for %d:%d:" | 988 | pr_err("Unable to locate struct scsi_device for %d:%d:" |
988 | "%d\n", 0, tl_tpg->tl_tpgt, se_lun->unpacked_lun); | 989 | "%d\n", 0, tl_tpg->tl_tpgt, se_lun->unpacked_lun); |
989 | return; | 990 | return; |
990 | } | 991 | } |
991 | /* | 992 | /* |
992 | * Remove Linux/SCSI struct scsi_device by HCTL | 993 | * Remove Linux/SCSI struct scsi_device by HCTL |
993 | */ | 994 | */ |
994 | scsi_remove_device(sd); | 995 | scsi_remove_device(sd); |
995 | scsi_device_put(sd); | 996 | scsi_device_put(sd); |
996 | 997 | ||
997 | atomic_dec(&tl_tpg->tl_tpg_port_count); | 998 | atomic_dec(&tl_tpg->tl_tpg_port_count); |
998 | smp_mb__after_atomic(); | 999 | smp_mb__after_atomic(); |
999 | 1000 | ||
1000 | pr_debug("TCM_Loop_ConfigFS: Port Unlink Successful\n"); | 1001 | pr_debug("TCM_Loop_ConfigFS: Port Unlink Successful\n"); |
1001 | } | 1002 | } |
1002 | 1003 | ||
1003 | /* End items for tcm_loop_port_cit */ | 1004 | /* End items for tcm_loop_port_cit */ |
1004 | 1005 | ||
1005 | /* Start items for tcm_loop_nexus_cit */ | 1006 | /* Start items for tcm_loop_nexus_cit */ |
1006 | 1007 | ||
1007 | static int tcm_loop_make_nexus( | 1008 | static int tcm_loop_make_nexus( |
1008 | struct tcm_loop_tpg *tl_tpg, | 1009 | struct tcm_loop_tpg *tl_tpg, |
1009 | const char *name) | 1010 | const char *name) |
1010 | { | 1011 | { |
1011 | struct se_portal_group *se_tpg; | 1012 | struct se_portal_group *se_tpg; |
1012 | struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba; | 1013 | struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba; |
1013 | struct tcm_loop_nexus *tl_nexus; | 1014 | struct tcm_loop_nexus *tl_nexus; |
1014 | int ret = -ENOMEM; | 1015 | int ret = -ENOMEM; |
1015 | 1016 | ||
1016 | if (tl_tpg->tl_hba->tl_nexus) { | 1017 | if (tl_tpg->tl_hba->tl_nexus) { |
1017 | pr_debug("tl_tpg->tl_hba->tl_nexus already exists\n"); | 1018 | pr_debug("tl_tpg->tl_hba->tl_nexus already exists\n"); |
1018 | return -EEXIST; | 1019 | return -EEXIST; |
1019 | } | 1020 | } |
1020 | se_tpg = &tl_tpg->tl_se_tpg; | 1021 | se_tpg = &tl_tpg->tl_se_tpg; |
1021 | 1022 | ||
1022 | tl_nexus = kzalloc(sizeof(struct tcm_loop_nexus), GFP_KERNEL); | 1023 | tl_nexus = kzalloc(sizeof(struct tcm_loop_nexus), GFP_KERNEL); |
1023 | if (!tl_nexus) { | 1024 | if (!tl_nexus) { |
1024 | pr_err("Unable to allocate struct tcm_loop_nexus\n"); | 1025 | pr_err("Unable to allocate struct tcm_loop_nexus\n"); |
1025 | return -ENOMEM; | 1026 | return -ENOMEM; |
1026 | } | 1027 | } |
1027 | /* | 1028 | /* |
1028 | * Initialize the struct se_session pointer | 1029 | * Initialize the struct se_session pointer |
1029 | */ | 1030 | */ |
1030 | tl_nexus->se_sess = transport_init_session(TARGET_PROT_ALL); | 1031 | tl_nexus->se_sess = transport_init_session(TARGET_PROT_ALL); |
1031 | if (IS_ERR(tl_nexus->se_sess)) { | 1032 | if (IS_ERR(tl_nexus->se_sess)) { |
1032 | ret = PTR_ERR(tl_nexus->se_sess); | 1033 | ret = PTR_ERR(tl_nexus->se_sess); |
1033 | goto out; | 1034 | goto out; |
1034 | } | 1035 | } |
1035 | /* | 1036 | /* |
1036 | * Since we are running in 'demo mode' this call with generate a | 1037 | * Since we are running in 'demo mode' this call with generate a |
1037 | * struct se_node_acl for the tcm_loop struct se_portal_group with the SCSI | 1038 | * struct se_node_acl for the tcm_loop struct se_portal_group with the SCSI |
1038 | * Initiator port name of the passed configfs group 'name'. | 1039 | * Initiator port name of the passed configfs group 'name'. |
1039 | */ | 1040 | */ |
1040 | tl_nexus->se_sess->se_node_acl = core_tpg_check_initiator_node_acl( | 1041 | tl_nexus->se_sess->se_node_acl = core_tpg_check_initiator_node_acl( |
1041 | se_tpg, (unsigned char *)name); | 1042 | se_tpg, (unsigned char *)name); |
1042 | if (!tl_nexus->se_sess->se_node_acl) { | 1043 | if (!tl_nexus->se_sess->se_node_acl) { |
1043 | transport_free_session(tl_nexus->se_sess); | 1044 | transport_free_session(tl_nexus->se_sess); |
1044 | goto out; | 1045 | goto out; |
1045 | } | 1046 | } |
1046 | /* | 1047 | /* |
1047 | * Now, register the SAS I_T Nexus as active with the call to | 1048 | * Now, register the SAS I_T Nexus as active with the call to |
1048 | * transport_register_session() | 1049 | * transport_register_session() |
1049 | */ | 1050 | */ |
1050 | __transport_register_session(se_tpg, tl_nexus->se_sess->se_node_acl, | 1051 | __transport_register_session(se_tpg, tl_nexus->se_sess->se_node_acl, |
1051 | tl_nexus->se_sess, tl_nexus); | 1052 | tl_nexus->se_sess, tl_nexus); |
1052 | tl_tpg->tl_hba->tl_nexus = tl_nexus; | 1053 | tl_tpg->tl_hba->tl_nexus = tl_nexus; |
1053 | pr_debug("TCM_Loop_ConfigFS: Established I_T Nexus to emulated" | 1054 | pr_debug("TCM_Loop_ConfigFS: Established I_T Nexus to emulated" |
1054 | " %s Initiator Port: %s\n", tcm_loop_dump_proto_id(tl_hba), | 1055 | " %s Initiator Port: %s\n", tcm_loop_dump_proto_id(tl_hba), |
1055 | name); | 1056 | name); |
1056 | return 0; | 1057 | return 0; |
1057 | 1058 | ||
1058 | out: | 1059 | out: |
1059 | kfree(tl_nexus); | 1060 | kfree(tl_nexus); |
1060 | return ret; | 1061 | return ret; |
1061 | } | 1062 | } |
1062 | 1063 | ||
1063 | static int tcm_loop_drop_nexus( | 1064 | static int tcm_loop_drop_nexus( |
1064 | struct tcm_loop_tpg *tpg) | 1065 | struct tcm_loop_tpg *tpg) |
1065 | { | 1066 | { |
1066 | struct se_session *se_sess; | 1067 | struct se_session *se_sess; |
1067 | struct tcm_loop_nexus *tl_nexus; | 1068 | struct tcm_loop_nexus *tl_nexus; |
1068 | struct tcm_loop_hba *tl_hba = tpg->tl_hba; | 1069 | struct tcm_loop_hba *tl_hba = tpg->tl_hba; |
1069 | 1070 | ||
1070 | if (!tl_hba) | 1071 | if (!tl_hba) |
1071 | return -ENODEV; | 1072 | return -ENODEV; |
1072 | 1073 | ||
1073 | tl_nexus = tl_hba->tl_nexus; | 1074 | tl_nexus = tl_hba->tl_nexus; |
1074 | if (!tl_nexus) | 1075 | if (!tl_nexus) |
1075 | return -ENODEV; | 1076 | return -ENODEV; |
1076 | 1077 | ||
1077 | se_sess = tl_nexus->se_sess; | 1078 | se_sess = tl_nexus->se_sess; |
1078 | if (!se_sess) | 1079 | if (!se_sess) |
1079 | return -ENODEV; | 1080 | return -ENODEV; |
1080 | 1081 | ||
1081 | if (atomic_read(&tpg->tl_tpg_port_count)) { | 1082 | if (atomic_read(&tpg->tl_tpg_port_count)) { |
1082 | pr_err("Unable to remove TCM_Loop I_T Nexus with" | 1083 | pr_err("Unable to remove TCM_Loop I_T Nexus with" |
1083 | " active TPG port count: %d\n", | 1084 | " active TPG port count: %d\n", |
1084 | atomic_read(&tpg->tl_tpg_port_count)); | 1085 | atomic_read(&tpg->tl_tpg_port_count)); |
1085 | return -EPERM; | 1086 | return -EPERM; |
1086 | } | 1087 | } |
1087 | 1088 | ||
1088 | pr_debug("TCM_Loop_ConfigFS: Removing I_T Nexus to emulated" | 1089 | pr_debug("TCM_Loop_ConfigFS: Removing I_T Nexus to emulated" |
1089 | " %s Initiator Port: %s\n", tcm_loop_dump_proto_id(tl_hba), | 1090 | " %s Initiator Port: %s\n", tcm_loop_dump_proto_id(tl_hba), |
1090 | tl_nexus->se_sess->se_node_acl->initiatorname); | 1091 | tl_nexus->se_sess->se_node_acl->initiatorname); |
1091 | /* | 1092 | /* |
1092 | * Release the SCSI I_T Nexus to the emulated SAS Target Port | 1093 | * Release the SCSI I_T Nexus to the emulated SAS Target Port |
1093 | */ | 1094 | */ |
1094 | transport_deregister_session(tl_nexus->se_sess); | 1095 | transport_deregister_session(tl_nexus->se_sess); |
1095 | tpg->tl_hba->tl_nexus = NULL; | 1096 | tpg->tl_hba->tl_nexus = NULL; |
1096 | kfree(tl_nexus); | 1097 | kfree(tl_nexus); |
1097 | return 0; | 1098 | return 0; |
1098 | } | 1099 | } |
1099 | 1100 | ||
1100 | /* End items for tcm_loop_nexus_cit */ | 1101 | /* End items for tcm_loop_nexus_cit */ |
1101 | 1102 | ||
1102 | static ssize_t tcm_loop_tpg_show_nexus( | 1103 | static ssize_t tcm_loop_tpg_show_nexus( |
1103 | struct se_portal_group *se_tpg, | 1104 | struct se_portal_group *se_tpg, |
1104 | char *page) | 1105 | char *page) |
1105 | { | 1106 | { |
1106 | struct tcm_loop_tpg *tl_tpg = container_of(se_tpg, | 1107 | struct tcm_loop_tpg *tl_tpg = container_of(se_tpg, |
1107 | struct tcm_loop_tpg, tl_se_tpg); | 1108 | struct tcm_loop_tpg, tl_se_tpg); |
1108 | struct tcm_loop_nexus *tl_nexus; | 1109 | struct tcm_loop_nexus *tl_nexus; |
1109 | ssize_t ret; | 1110 | ssize_t ret; |
1110 | 1111 | ||
1111 | tl_nexus = tl_tpg->tl_hba->tl_nexus; | 1112 | tl_nexus = tl_tpg->tl_hba->tl_nexus; |
1112 | if (!tl_nexus) | 1113 | if (!tl_nexus) |
1113 | return -ENODEV; | 1114 | return -ENODEV; |
1114 | 1115 | ||
1115 | ret = snprintf(page, PAGE_SIZE, "%s\n", | 1116 | ret = snprintf(page, PAGE_SIZE, "%s\n", |
1116 | tl_nexus->se_sess->se_node_acl->initiatorname); | 1117 | tl_nexus->se_sess->se_node_acl->initiatorname); |
1117 | 1118 | ||
1118 | return ret; | 1119 | return ret; |
1119 | } | 1120 | } |
1120 | 1121 | ||
1121 | static ssize_t tcm_loop_tpg_store_nexus( | 1122 | static ssize_t tcm_loop_tpg_store_nexus( |
1122 | struct se_portal_group *se_tpg, | 1123 | struct se_portal_group *se_tpg, |
1123 | const char *page, | 1124 | const char *page, |
1124 | size_t count) | 1125 | size_t count) |
1125 | { | 1126 | { |
1126 | struct tcm_loop_tpg *tl_tpg = container_of(se_tpg, | 1127 | struct tcm_loop_tpg *tl_tpg = container_of(se_tpg, |
1127 | struct tcm_loop_tpg, tl_se_tpg); | 1128 | struct tcm_loop_tpg, tl_se_tpg); |
1128 | struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba; | 1129 | struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba; |
1129 | unsigned char i_port[TL_WWN_ADDR_LEN], *ptr, *port_ptr; | 1130 | unsigned char i_port[TL_WWN_ADDR_LEN], *ptr, *port_ptr; |
1130 | int ret; | 1131 | int ret; |
1131 | /* | 1132 | /* |
1132 | * Shutdown the active I_T nexus if 'NULL' is passed.. | 1133 | * Shutdown the active I_T nexus if 'NULL' is passed.. |
1133 | */ | 1134 | */ |
1134 | if (!strncmp(page, "NULL", 4)) { | 1135 | if (!strncmp(page, "NULL", 4)) { |
1135 | ret = tcm_loop_drop_nexus(tl_tpg); | 1136 | ret = tcm_loop_drop_nexus(tl_tpg); |
1136 | return (!ret) ? count : ret; | 1137 | return (!ret) ? count : ret; |
1137 | } | 1138 | } |
1138 | /* | 1139 | /* |
1139 | * Otherwise make sure the passed virtual Initiator port WWN matches | 1140 | * Otherwise make sure the passed virtual Initiator port WWN matches |
1140 | * the fabric protocol_id set in tcm_loop_make_scsi_hba(), and call | 1141 | * the fabric protocol_id set in tcm_loop_make_scsi_hba(), and call |
1141 | * tcm_loop_make_nexus() | 1142 | * tcm_loop_make_nexus() |
1142 | */ | 1143 | */ |
1143 | if (strlen(page) >= TL_WWN_ADDR_LEN) { | 1144 | if (strlen(page) >= TL_WWN_ADDR_LEN) { |
1144 | pr_err("Emulated NAA Sas Address: %s, exceeds" | 1145 | pr_err("Emulated NAA Sas Address: %s, exceeds" |
1145 | " max: %d\n", page, TL_WWN_ADDR_LEN); | 1146 | " max: %d\n", page, TL_WWN_ADDR_LEN); |
1146 | return -EINVAL; | 1147 | return -EINVAL; |
1147 | } | 1148 | } |
1148 | snprintf(&i_port[0], TL_WWN_ADDR_LEN, "%s", page); | 1149 | snprintf(&i_port[0], TL_WWN_ADDR_LEN, "%s", page); |
1149 | 1150 | ||
1150 | ptr = strstr(i_port, "naa."); | 1151 | ptr = strstr(i_port, "naa."); |
1151 | if (ptr) { | 1152 | if (ptr) { |
1152 | if (tl_hba->tl_proto_id != SCSI_PROTOCOL_SAS) { | 1153 | if (tl_hba->tl_proto_id != SCSI_PROTOCOL_SAS) { |
1153 | pr_err("Passed SAS Initiator Port %s does not" | 1154 | pr_err("Passed SAS Initiator Port %s does not" |
1154 | " match target port protoid: %s\n", i_port, | 1155 | " match target port protoid: %s\n", i_port, |
1155 | tcm_loop_dump_proto_id(tl_hba)); | 1156 | tcm_loop_dump_proto_id(tl_hba)); |
1156 | return -EINVAL; | 1157 | return -EINVAL; |
1157 | } | 1158 | } |
1158 | port_ptr = &i_port[0]; | 1159 | port_ptr = &i_port[0]; |
1159 | goto check_newline; | 1160 | goto check_newline; |
1160 | } | 1161 | } |
1161 | ptr = strstr(i_port, "fc."); | 1162 | ptr = strstr(i_port, "fc."); |
1162 | if (ptr) { | 1163 | if (ptr) { |
1163 | if (tl_hba->tl_proto_id != SCSI_PROTOCOL_FCP) { | 1164 | if (tl_hba->tl_proto_id != SCSI_PROTOCOL_FCP) { |
1164 | pr_err("Passed FCP Initiator Port %s does not" | 1165 | pr_err("Passed FCP Initiator Port %s does not" |
1165 | " match target port protoid: %s\n", i_port, | 1166 | " match target port protoid: %s\n", i_port, |
1166 | tcm_loop_dump_proto_id(tl_hba)); | 1167 | tcm_loop_dump_proto_id(tl_hba)); |
1167 | return -EINVAL; | 1168 | return -EINVAL; |
1168 | } | 1169 | } |
1169 | port_ptr = &i_port[3]; /* Skip over "fc." */ | 1170 | port_ptr = &i_port[3]; /* Skip over "fc." */ |
1170 | goto check_newline; | 1171 | goto check_newline; |
1171 | } | 1172 | } |
1172 | ptr = strstr(i_port, "iqn."); | 1173 | ptr = strstr(i_port, "iqn."); |
1173 | if (ptr) { | 1174 | if (ptr) { |
1174 | if (tl_hba->tl_proto_id != SCSI_PROTOCOL_ISCSI) { | 1175 | if (tl_hba->tl_proto_id != SCSI_PROTOCOL_ISCSI) { |
1175 | pr_err("Passed iSCSI Initiator Port %s does not" | 1176 | pr_err("Passed iSCSI Initiator Port %s does not" |
1176 | " match target port protoid: %s\n", i_port, | 1177 | " match target port protoid: %s\n", i_port, |
1177 | tcm_loop_dump_proto_id(tl_hba)); | 1178 | tcm_loop_dump_proto_id(tl_hba)); |
1178 | return -EINVAL; | 1179 | return -EINVAL; |
1179 | } | 1180 | } |
1180 | port_ptr = &i_port[0]; | 1181 | port_ptr = &i_port[0]; |
1181 | goto check_newline; | 1182 | goto check_newline; |
1182 | } | 1183 | } |
1183 | pr_err("Unable to locate prefix for emulated Initiator Port:" | 1184 | pr_err("Unable to locate prefix for emulated Initiator Port:" |
1184 | " %s\n", i_port); | 1185 | " %s\n", i_port); |
1185 | return -EINVAL; | 1186 | return -EINVAL; |
1186 | /* | 1187 | /* |
1187 | * Clear any trailing newline for the NAA WWN | 1188 | * Clear any trailing newline for the NAA WWN |
1188 | */ | 1189 | */ |
1189 | check_newline: | 1190 | check_newline: |
1190 | if (i_port[strlen(i_port)-1] == '\n') | 1191 | if (i_port[strlen(i_port)-1] == '\n') |
1191 | i_port[strlen(i_port)-1] = '\0'; | 1192 | i_port[strlen(i_port)-1] = '\0'; |
1192 | 1193 | ||
1193 | ret = tcm_loop_make_nexus(tl_tpg, port_ptr); | 1194 | ret = tcm_loop_make_nexus(tl_tpg, port_ptr); |
1194 | if (ret < 0) | 1195 | if (ret < 0) |
1195 | return ret; | 1196 | return ret; |
1196 | 1197 | ||
1197 | return count; | 1198 | return count; |
1198 | } | 1199 | } |
1199 | 1200 | ||
1200 | TF_TPG_BASE_ATTR(tcm_loop, nexus, S_IRUGO | S_IWUSR); | 1201 | TF_TPG_BASE_ATTR(tcm_loop, nexus, S_IRUGO | S_IWUSR); |
1201 | 1202 | ||
1202 | static ssize_t tcm_loop_tpg_show_transport_status( | 1203 | static ssize_t tcm_loop_tpg_show_transport_status( |
1203 | struct se_portal_group *se_tpg, | 1204 | struct se_portal_group *se_tpg, |
1204 | char *page) | 1205 | char *page) |
1205 | { | 1206 | { |
1206 | struct tcm_loop_tpg *tl_tpg = container_of(se_tpg, | 1207 | struct tcm_loop_tpg *tl_tpg = container_of(se_tpg, |
1207 | struct tcm_loop_tpg, tl_se_tpg); | 1208 | struct tcm_loop_tpg, tl_se_tpg); |
1208 | const char *status = NULL; | 1209 | const char *status = NULL; |
1209 | ssize_t ret = -EINVAL; | 1210 | ssize_t ret = -EINVAL; |
1210 | 1211 | ||
1211 | switch (tl_tpg->tl_transport_status) { | 1212 | switch (tl_tpg->tl_transport_status) { |
1212 | case TCM_TRANSPORT_ONLINE: | 1213 | case TCM_TRANSPORT_ONLINE: |
1213 | status = "online"; | 1214 | status = "online"; |
1214 | break; | 1215 | break; |
1215 | case TCM_TRANSPORT_OFFLINE: | 1216 | case TCM_TRANSPORT_OFFLINE: |
1216 | status = "offline"; | 1217 | status = "offline"; |
1217 | break; | 1218 | break; |
1218 | default: | 1219 | default: |
1219 | break; | 1220 | break; |
1220 | } | 1221 | } |
1221 | 1222 | ||
1222 | if (status) | 1223 | if (status) |
1223 | ret = snprintf(page, PAGE_SIZE, "%s\n", status); | 1224 | ret = snprintf(page, PAGE_SIZE, "%s\n", status); |
1224 | 1225 | ||
1225 | return ret; | 1226 | return ret; |
1226 | } | 1227 | } |
1227 | 1228 | ||
1228 | static ssize_t tcm_loop_tpg_store_transport_status( | 1229 | static ssize_t tcm_loop_tpg_store_transport_status( |
1229 | struct se_portal_group *se_tpg, | 1230 | struct se_portal_group *se_tpg, |
1230 | const char *page, | 1231 | const char *page, |
1231 | size_t count) | 1232 | size_t count) |
1232 | { | 1233 | { |
1233 | struct tcm_loop_tpg *tl_tpg = container_of(se_tpg, | 1234 | struct tcm_loop_tpg *tl_tpg = container_of(se_tpg, |
1234 | struct tcm_loop_tpg, tl_se_tpg); | 1235 | struct tcm_loop_tpg, tl_se_tpg); |
1235 | 1236 | ||
1236 | if (!strncmp(page, "online", 6)) { | 1237 | if (!strncmp(page, "online", 6)) { |
1237 | tl_tpg->tl_transport_status = TCM_TRANSPORT_ONLINE; | 1238 | tl_tpg->tl_transport_status = TCM_TRANSPORT_ONLINE; |
1238 | return count; | 1239 | return count; |
1239 | } | 1240 | } |
1240 | if (!strncmp(page, "offline", 7)) { | 1241 | if (!strncmp(page, "offline", 7)) { |
1241 | tl_tpg->tl_transport_status = TCM_TRANSPORT_OFFLINE; | 1242 | tl_tpg->tl_transport_status = TCM_TRANSPORT_OFFLINE; |
1242 | return count; | 1243 | return count; |
1243 | } | 1244 | } |
1244 | return -EINVAL; | 1245 | return -EINVAL; |
1245 | } | 1246 | } |
1246 | 1247 | ||
1247 | TF_TPG_BASE_ATTR(tcm_loop, transport_status, S_IRUGO | S_IWUSR); | 1248 | TF_TPG_BASE_ATTR(tcm_loop, transport_status, S_IRUGO | S_IWUSR); |
1248 | 1249 | ||
1249 | static struct configfs_attribute *tcm_loop_tpg_attrs[] = { | 1250 | static struct configfs_attribute *tcm_loop_tpg_attrs[] = { |
1250 | &tcm_loop_tpg_nexus.attr, | 1251 | &tcm_loop_tpg_nexus.attr, |
1251 | &tcm_loop_tpg_transport_status.attr, | 1252 | &tcm_loop_tpg_transport_status.attr, |
1252 | NULL, | 1253 | NULL, |
1253 | }; | 1254 | }; |
1254 | 1255 | ||
1255 | /* Start items for tcm_loop_naa_cit */ | 1256 | /* Start items for tcm_loop_naa_cit */ |
1256 | 1257 | ||
1257 | static struct se_portal_group *tcm_loop_make_naa_tpg( | 1258 | static struct se_portal_group *tcm_loop_make_naa_tpg( |
1258 | struct se_wwn *wwn, | 1259 | struct se_wwn *wwn, |
1259 | struct config_group *group, | 1260 | struct config_group *group, |
1260 | const char *name) | 1261 | const char *name) |
1261 | { | 1262 | { |
1262 | struct tcm_loop_hba *tl_hba = container_of(wwn, | 1263 | struct tcm_loop_hba *tl_hba = container_of(wwn, |
1263 | struct tcm_loop_hba, tl_hba_wwn); | 1264 | struct tcm_loop_hba, tl_hba_wwn); |
1264 | struct tcm_loop_tpg *tl_tpg; | 1265 | struct tcm_loop_tpg *tl_tpg; |
1265 | char *tpgt_str, *end_ptr; | 1266 | char *tpgt_str, *end_ptr; |
1266 | int ret; | 1267 | int ret; |
1267 | unsigned short int tpgt; | 1268 | unsigned short int tpgt; |
1268 | 1269 | ||
1269 | tpgt_str = strstr(name, "tpgt_"); | 1270 | tpgt_str = strstr(name, "tpgt_"); |
1270 | if (!tpgt_str) { | 1271 | if (!tpgt_str) { |
1271 | pr_err("Unable to locate \"tpgt_#\" directory" | 1272 | pr_err("Unable to locate \"tpgt_#\" directory" |
1272 | " group\n"); | 1273 | " group\n"); |
1273 | return ERR_PTR(-EINVAL); | 1274 | return ERR_PTR(-EINVAL); |
1274 | } | 1275 | } |
1275 | tpgt_str += 5; /* Skip ahead of "tpgt_" */ | 1276 | tpgt_str += 5; /* Skip ahead of "tpgt_" */ |
1276 | tpgt = (unsigned short int) simple_strtoul(tpgt_str, &end_ptr, 0); | 1277 | tpgt = (unsigned short int) simple_strtoul(tpgt_str, &end_ptr, 0); |
1277 | 1278 | ||
1278 | if (tpgt >= TL_TPGS_PER_HBA) { | 1279 | if (tpgt >= TL_TPGS_PER_HBA) { |
1279 | pr_err("Passed tpgt: %hu exceeds TL_TPGS_PER_HBA:" | 1280 | pr_err("Passed tpgt: %hu exceeds TL_TPGS_PER_HBA:" |
1280 | " %u\n", tpgt, TL_TPGS_PER_HBA); | 1281 | " %u\n", tpgt, TL_TPGS_PER_HBA); |
1281 | return ERR_PTR(-EINVAL); | 1282 | return ERR_PTR(-EINVAL); |
1282 | } | 1283 | } |
1283 | tl_tpg = &tl_hba->tl_hba_tpgs[tpgt]; | 1284 | tl_tpg = &tl_hba->tl_hba_tpgs[tpgt]; |
1284 | tl_tpg->tl_hba = tl_hba; | 1285 | tl_tpg->tl_hba = tl_hba; |
1285 | tl_tpg->tl_tpgt = tpgt; | 1286 | tl_tpg->tl_tpgt = tpgt; |
1286 | /* | 1287 | /* |
1287 | * Register the tl_tpg as a emulated SAS TCM Target Endpoint | 1288 | * Register the tl_tpg as a emulated SAS TCM Target Endpoint |
1288 | */ | 1289 | */ |
1289 | ret = core_tpg_register(&tcm_loop_fabric_configfs->tf_ops, | 1290 | ret = core_tpg_register(&tcm_loop_fabric_configfs->tf_ops, |
1290 | wwn, &tl_tpg->tl_se_tpg, tl_tpg, | 1291 | wwn, &tl_tpg->tl_se_tpg, tl_tpg, |
1291 | TRANSPORT_TPG_TYPE_NORMAL); | 1292 | TRANSPORT_TPG_TYPE_NORMAL); |
1292 | if (ret < 0) | 1293 | if (ret < 0) |
1293 | return ERR_PTR(-ENOMEM); | 1294 | return ERR_PTR(-ENOMEM); |
1294 | 1295 | ||
1295 | pr_debug("TCM_Loop_ConfigFS: Allocated Emulated %s" | 1296 | pr_debug("TCM_Loop_ConfigFS: Allocated Emulated %s" |
1296 | " Target Port %s,t,0x%04x\n", tcm_loop_dump_proto_id(tl_hba), | 1297 | " Target Port %s,t,0x%04x\n", tcm_loop_dump_proto_id(tl_hba), |
1297 | config_item_name(&wwn->wwn_group.cg_item), tpgt); | 1298 | config_item_name(&wwn->wwn_group.cg_item), tpgt); |
1298 | 1299 | ||
1299 | return &tl_tpg->tl_se_tpg; | 1300 | return &tl_tpg->tl_se_tpg; |
1300 | } | 1301 | } |
1301 | 1302 | ||
1302 | static void tcm_loop_drop_naa_tpg( | 1303 | static void tcm_loop_drop_naa_tpg( |
1303 | struct se_portal_group *se_tpg) | 1304 | struct se_portal_group *se_tpg) |
1304 | { | 1305 | { |
1305 | struct se_wwn *wwn = se_tpg->se_tpg_wwn; | 1306 | struct se_wwn *wwn = se_tpg->se_tpg_wwn; |
1306 | struct tcm_loop_tpg *tl_tpg = container_of(se_tpg, | 1307 | struct tcm_loop_tpg *tl_tpg = container_of(se_tpg, |
1307 | struct tcm_loop_tpg, tl_se_tpg); | 1308 | struct tcm_loop_tpg, tl_se_tpg); |
1308 | struct tcm_loop_hba *tl_hba; | 1309 | struct tcm_loop_hba *tl_hba; |
1309 | unsigned short tpgt; | 1310 | unsigned short tpgt; |
1310 | 1311 | ||
1311 | tl_hba = tl_tpg->tl_hba; | 1312 | tl_hba = tl_tpg->tl_hba; |
1312 | tpgt = tl_tpg->tl_tpgt; | 1313 | tpgt = tl_tpg->tl_tpgt; |
1313 | /* | 1314 | /* |
1314 | * Release the I_T Nexus for the Virtual SAS link if present | 1315 | * Release the I_T Nexus for the Virtual SAS link if present |
1315 | */ | 1316 | */ |
1316 | tcm_loop_drop_nexus(tl_tpg); | 1317 | tcm_loop_drop_nexus(tl_tpg); |
1317 | /* | 1318 | /* |
1318 | * Deregister the tl_tpg as a emulated SAS TCM Target Endpoint | 1319 | * Deregister the tl_tpg as a emulated SAS TCM Target Endpoint |
1319 | */ | 1320 | */ |
1320 | core_tpg_deregister(se_tpg); | 1321 | core_tpg_deregister(se_tpg); |
1321 | 1322 | ||
1322 | tl_tpg->tl_hba = NULL; | 1323 | tl_tpg->tl_hba = NULL; |
1323 | tl_tpg->tl_tpgt = 0; | 1324 | tl_tpg->tl_tpgt = 0; |
1324 | 1325 | ||
1325 | pr_debug("TCM_Loop_ConfigFS: Deallocated Emulated %s" | 1326 | pr_debug("TCM_Loop_ConfigFS: Deallocated Emulated %s" |
1326 | " Target Port %s,t,0x%04x\n", tcm_loop_dump_proto_id(tl_hba), | 1327 | " Target Port %s,t,0x%04x\n", tcm_loop_dump_proto_id(tl_hba), |
1327 | config_item_name(&wwn->wwn_group.cg_item), tpgt); | 1328 | config_item_name(&wwn->wwn_group.cg_item), tpgt); |
1328 | } | 1329 | } |
1329 | 1330 | ||
1330 | /* End items for tcm_loop_naa_cit */ | 1331 | /* End items for tcm_loop_naa_cit */ |
1331 | 1332 | ||
1332 | /* Start items for tcm_loop_cit */ | 1333 | /* Start items for tcm_loop_cit */ |
1333 | 1334 | ||
1334 | static struct se_wwn *tcm_loop_make_scsi_hba( | 1335 | static struct se_wwn *tcm_loop_make_scsi_hba( |
1335 | struct target_fabric_configfs *tf, | 1336 | struct target_fabric_configfs *tf, |
1336 | struct config_group *group, | 1337 | struct config_group *group, |
1337 | const char *name) | 1338 | const char *name) |
1338 | { | 1339 | { |
1339 | struct tcm_loop_hba *tl_hba; | 1340 | struct tcm_loop_hba *tl_hba; |
1340 | struct Scsi_Host *sh; | 1341 | struct Scsi_Host *sh; |
1341 | char *ptr; | 1342 | char *ptr; |
1342 | int ret, off = 0; | 1343 | int ret, off = 0; |
1343 | 1344 | ||
1344 | tl_hba = kzalloc(sizeof(struct tcm_loop_hba), GFP_KERNEL); | 1345 | tl_hba = kzalloc(sizeof(struct tcm_loop_hba), GFP_KERNEL); |
1345 | if (!tl_hba) { | 1346 | if (!tl_hba) { |
1346 | pr_err("Unable to allocate struct tcm_loop_hba\n"); | 1347 | pr_err("Unable to allocate struct tcm_loop_hba\n"); |
1347 | return ERR_PTR(-ENOMEM); | 1348 | return ERR_PTR(-ENOMEM); |
1348 | } | 1349 | } |
1349 | /* | 1350 | /* |
1350 | * Determine the emulated Protocol Identifier and Target Port Name | 1351 | * Determine the emulated Protocol Identifier and Target Port Name |
1351 | * based on the incoming configfs directory name. | 1352 | * based on the incoming configfs directory name. |
1352 | */ | 1353 | */ |
1353 | ptr = strstr(name, "naa."); | 1354 | ptr = strstr(name, "naa."); |
1354 | if (ptr) { | 1355 | if (ptr) { |
1355 | tl_hba->tl_proto_id = SCSI_PROTOCOL_SAS; | 1356 | tl_hba->tl_proto_id = SCSI_PROTOCOL_SAS; |
1356 | goto check_len; | 1357 | goto check_len; |
1357 | } | 1358 | } |
1358 | ptr = strstr(name, "fc."); | 1359 | ptr = strstr(name, "fc."); |
1359 | if (ptr) { | 1360 | if (ptr) { |
1360 | tl_hba->tl_proto_id = SCSI_PROTOCOL_FCP; | 1361 | tl_hba->tl_proto_id = SCSI_PROTOCOL_FCP; |
1361 | off = 3; /* Skip over "fc." */ | 1362 | off = 3; /* Skip over "fc." */ |
1362 | goto check_len; | 1363 | goto check_len; |
1363 | } | 1364 | } |
1364 | ptr = strstr(name, "iqn."); | 1365 | ptr = strstr(name, "iqn."); |
1365 | if (!ptr) { | 1366 | if (!ptr) { |
1366 | pr_err("Unable to locate prefix for emulated Target " | 1367 | pr_err("Unable to locate prefix for emulated Target " |
1367 | "Port: %s\n", name); | 1368 | "Port: %s\n", name); |
1368 | ret = -EINVAL; | 1369 | ret = -EINVAL; |
1369 | goto out; | 1370 | goto out; |
1370 | } | 1371 | } |
1371 | tl_hba->tl_proto_id = SCSI_PROTOCOL_ISCSI; | 1372 | tl_hba->tl_proto_id = SCSI_PROTOCOL_ISCSI; |
1372 | 1373 | ||
1373 | check_len: | 1374 | check_len: |
1374 | if (strlen(name) >= TL_WWN_ADDR_LEN) { | 1375 | if (strlen(name) >= TL_WWN_ADDR_LEN) { |
1375 | pr_err("Emulated NAA %s Address: %s, exceeds" | 1376 | pr_err("Emulated NAA %s Address: %s, exceeds" |
1376 | " max: %d\n", name, tcm_loop_dump_proto_id(tl_hba), | 1377 | " max: %d\n", name, tcm_loop_dump_proto_id(tl_hba), |
1377 | TL_WWN_ADDR_LEN); | 1378 | TL_WWN_ADDR_LEN); |
1378 | ret = -EINVAL; | 1379 | ret = -EINVAL; |
1379 | goto out; | 1380 | goto out; |
1380 | } | 1381 | } |
1381 | snprintf(&tl_hba->tl_wwn_address[0], TL_WWN_ADDR_LEN, "%s", &name[off]); | 1382 | snprintf(&tl_hba->tl_wwn_address[0], TL_WWN_ADDR_LEN, "%s", &name[off]); |
1382 | 1383 | ||
1383 | /* | 1384 | /* |
1384 | * Call device_register(tl_hba->dev) to register the emulated | 1385 | * Call device_register(tl_hba->dev) to register the emulated |
1385 | * Linux/SCSI LLD of type struct Scsi_Host at tl_hba->sh after | 1386 | * Linux/SCSI LLD of type struct Scsi_Host at tl_hba->sh after |
1386 | * device_register() callbacks in tcm_loop_driver_probe() | 1387 | * device_register() callbacks in tcm_loop_driver_probe() |
1387 | */ | 1388 | */ |
1388 | ret = tcm_loop_setup_hba_bus(tl_hba, tcm_loop_hba_no_cnt); | 1389 | ret = tcm_loop_setup_hba_bus(tl_hba, tcm_loop_hba_no_cnt); |
1389 | if (ret) | 1390 | if (ret) |
1390 | goto out; | 1391 | goto out; |
1391 | 1392 | ||
1392 | sh = tl_hba->sh; | 1393 | sh = tl_hba->sh; |
1393 | tcm_loop_hba_no_cnt++; | 1394 | tcm_loop_hba_no_cnt++; |
1394 | pr_debug("TCM_Loop_ConfigFS: Allocated emulated Target" | 1395 | pr_debug("TCM_Loop_ConfigFS: Allocated emulated Target" |
1395 | " %s Address: %s at Linux/SCSI Host ID: %d\n", | 1396 | " %s Address: %s at Linux/SCSI Host ID: %d\n", |
1396 | tcm_loop_dump_proto_id(tl_hba), name, sh->host_no); | 1397 | tcm_loop_dump_proto_id(tl_hba), name, sh->host_no); |
1397 | 1398 | ||
1398 | return &tl_hba->tl_hba_wwn; | 1399 | return &tl_hba->tl_hba_wwn; |
1399 | out: | 1400 | out: |
1400 | kfree(tl_hba); | 1401 | kfree(tl_hba); |
1401 | return ERR_PTR(ret); | 1402 | return ERR_PTR(ret); |
1402 | } | 1403 | } |
1403 | 1404 | ||
1404 | static void tcm_loop_drop_scsi_hba( | 1405 | static void tcm_loop_drop_scsi_hba( |
1405 | struct se_wwn *wwn) | 1406 | struct se_wwn *wwn) |
1406 | { | 1407 | { |
1407 | struct tcm_loop_hba *tl_hba = container_of(wwn, | 1408 | struct tcm_loop_hba *tl_hba = container_of(wwn, |
1408 | struct tcm_loop_hba, tl_hba_wwn); | 1409 | struct tcm_loop_hba, tl_hba_wwn); |
1409 | 1410 | ||
1410 | pr_debug("TCM_Loop_ConfigFS: Deallocating emulated Target" | 1411 | pr_debug("TCM_Loop_ConfigFS: Deallocating emulated Target" |
1411 | " SAS Address: %s at Linux/SCSI Host ID: %d\n", | 1412 | " SAS Address: %s at Linux/SCSI Host ID: %d\n", |
1412 | tl_hba->tl_wwn_address, tl_hba->sh->host_no); | 1413 | tl_hba->tl_wwn_address, tl_hba->sh->host_no); |
1413 | /* | 1414 | /* |
1414 | * Call device_unregister() on the original tl_hba->dev. | 1415 | * Call device_unregister() on the original tl_hba->dev. |
1415 | * tcm_loop_fabric_scsi.c:tcm_loop_release_adapter() will | 1416 | * tcm_loop_fabric_scsi.c:tcm_loop_release_adapter() will |
1416 | * release *tl_hba; | 1417 | * release *tl_hba; |
1417 | */ | 1418 | */ |
1418 | device_unregister(&tl_hba->dev); | 1419 | device_unregister(&tl_hba->dev); |
1419 | } | 1420 | } |
1420 | 1421 | ||
1421 | /* Start items for tcm_loop_cit */ | 1422 | /* Start items for tcm_loop_cit */ |
1422 | static ssize_t tcm_loop_wwn_show_attr_version( | 1423 | static ssize_t tcm_loop_wwn_show_attr_version( |
1423 | struct target_fabric_configfs *tf, | 1424 | struct target_fabric_configfs *tf, |
1424 | char *page) | 1425 | char *page) |
1425 | { | 1426 | { |
1426 | return sprintf(page, "TCM Loopback Fabric module %s\n", TCM_LOOP_VERSION); | 1427 | return sprintf(page, "TCM Loopback Fabric module %s\n", TCM_LOOP_VERSION); |
1427 | } | 1428 | } |
1428 | 1429 | ||
1429 | TF_WWN_ATTR_RO(tcm_loop, version); | 1430 | TF_WWN_ATTR_RO(tcm_loop, version); |
1430 | 1431 | ||
1431 | static struct configfs_attribute *tcm_loop_wwn_attrs[] = { | 1432 | static struct configfs_attribute *tcm_loop_wwn_attrs[] = { |
1432 | &tcm_loop_wwn_version.attr, | 1433 | &tcm_loop_wwn_version.attr, |
1433 | NULL, | 1434 | NULL, |
1434 | }; | 1435 | }; |
1435 | 1436 | ||
1436 | /* End items for tcm_loop_cit */ | 1437 | /* End items for tcm_loop_cit */ |
1437 | 1438 | ||
1438 | static int tcm_loop_register_configfs(void) | 1439 | static int tcm_loop_register_configfs(void) |
1439 | { | 1440 | { |
1440 | struct target_fabric_configfs *fabric; | 1441 | struct target_fabric_configfs *fabric; |
1441 | int ret; | 1442 | int ret; |
1442 | /* | 1443 | /* |
1443 | * Set the TCM Loop HBA counter to zero | 1444 | * Set the TCM Loop HBA counter to zero |
1444 | */ | 1445 | */ |
1445 | tcm_loop_hba_no_cnt = 0; | 1446 | tcm_loop_hba_no_cnt = 0; |
1446 | /* | 1447 | /* |
1447 | * Register the top level struct config_item_type with TCM core | 1448 | * Register the top level struct config_item_type with TCM core |
1448 | */ | 1449 | */ |
1449 | fabric = target_fabric_configfs_init(THIS_MODULE, "loopback"); | 1450 | fabric = target_fabric_configfs_init(THIS_MODULE, "loopback"); |
1450 | if (IS_ERR(fabric)) { | 1451 | if (IS_ERR(fabric)) { |
1451 | pr_err("tcm_loop_register_configfs() failed!\n"); | 1452 | pr_err("tcm_loop_register_configfs() failed!\n"); |
1452 | return PTR_ERR(fabric); | 1453 | return PTR_ERR(fabric); |
1453 | } | 1454 | } |
1454 | /* | 1455 | /* |
1455 | * Setup the fabric API of function pointers used by target_core_mod | 1456 | * Setup the fabric API of function pointers used by target_core_mod |
1456 | */ | 1457 | */ |
1457 | fabric->tf_ops.get_fabric_name = &tcm_loop_get_fabric_name; | 1458 | fabric->tf_ops.get_fabric_name = &tcm_loop_get_fabric_name; |
1458 | fabric->tf_ops.get_fabric_proto_ident = &tcm_loop_get_fabric_proto_ident; | 1459 | fabric->tf_ops.get_fabric_proto_ident = &tcm_loop_get_fabric_proto_ident; |
1459 | fabric->tf_ops.tpg_get_wwn = &tcm_loop_get_endpoint_wwn; | 1460 | fabric->tf_ops.tpg_get_wwn = &tcm_loop_get_endpoint_wwn; |
1460 | fabric->tf_ops.tpg_get_tag = &tcm_loop_get_tag; | 1461 | fabric->tf_ops.tpg_get_tag = &tcm_loop_get_tag; |
1461 | fabric->tf_ops.tpg_get_default_depth = &tcm_loop_get_default_depth; | 1462 | fabric->tf_ops.tpg_get_default_depth = &tcm_loop_get_default_depth; |
1462 | fabric->tf_ops.tpg_get_pr_transport_id = &tcm_loop_get_pr_transport_id; | 1463 | fabric->tf_ops.tpg_get_pr_transport_id = &tcm_loop_get_pr_transport_id; |
1463 | fabric->tf_ops.tpg_get_pr_transport_id_len = | 1464 | fabric->tf_ops.tpg_get_pr_transport_id_len = |
1464 | &tcm_loop_get_pr_transport_id_len; | 1465 | &tcm_loop_get_pr_transport_id_len; |
1465 | fabric->tf_ops.tpg_parse_pr_out_transport_id = | 1466 | fabric->tf_ops.tpg_parse_pr_out_transport_id = |
1466 | &tcm_loop_parse_pr_out_transport_id; | 1467 | &tcm_loop_parse_pr_out_transport_id; |
1467 | fabric->tf_ops.tpg_check_demo_mode = &tcm_loop_check_demo_mode; | 1468 | fabric->tf_ops.tpg_check_demo_mode = &tcm_loop_check_demo_mode; |
1468 | fabric->tf_ops.tpg_check_demo_mode_cache = | 1469 | fabric->tf_ops.tpg_check_demo_mode_cache = |
1469 | &tcm_loop_check_demo_mode_cache; | 1470 | &tcm_loop_check_demo_mode_cache; |
1470 | fabric->tf_ops.tpg_check_demo_mode_write_protect = | 1471 | fabric->tf_ops.tpg_check_demo_mode_write_protect = |
1471 | &tcm_loop_check_demo_mode_write_protect; | 1472 | &tcm_loop_check_demo_mode_write_protect; |
1472 | fabric->tf_ops.tpg_check_prod_mode_write_protect = | 1473 | fabric->tf_ops.tpg_check_prod_mode_write_protect = |
1473 | &tcm_loop_check_prod_mode_write_protect; | 1474 | &tcm_loop_check_prod_mode_write_protect; |
1474 | /* | 1475 | /* |
1475 | * The TCM loopback fabric module runs in demo-mode to a local | 1476 | * The TCM loopback fabric module runs in demo-mode to a local |
1476 | * virtual SCSI device, so fabric dependent initator ACLs are | 1477 | * virtual SCSI device, so fabric dependent initator ACLs are |
1477 | * not required. | 1478 | * not required. |
1478 | */ | 1479 | */ |
1479 | fabric->tf_ops.tpg_alloc_fabric_acl = &tcm_loop_tpg_alloc_fabric_acl; | 1480 | fabric->tf_ops.tpg_alloc_fabric_acl = &tcm_loop_tpg_alloc_fabric_acl; |
1480 | fabric->tf_ops.tpg_release_fabric_acl = | 1481 | fabric->tf_ops.tpg_release_fabric_acl = |
1481 | &tcm_loop_tpg_release_fabric_acl; | 1482 | &tcm_loop_tpg_release_fabric_acl; |
1482 | fabric->tf_ops.tpg_get_inst_index = &tcm_loop_get_inst_index; | 1483 | fabric->tf_ops.tpg_get_inst_index = &tcm_loop_get_inst_index; |
1483 | /* | 1484 | /* |
1484 | * Used for setting up remaining TCM resources in process context | 1485 | * Used for setting up remaining TCM resources in process context |
1485 | */ | 1486 | */ |
1486 | fabric->tf_ops.check_stop_free = &tcm_loop_check_stop_free; | 1487 | fabric->tf_ops.check_stop_free = &tcm_loop_check_stop_free; |
1487 | fabric->tf_ops.release_cmd = &tcm_loop_release_cmd; | 1488 | fabric->tf_ops.release_cmd = &tcm_loop_release_cmd; |
1488 | fabric->tf_ops.shutdown_session = &tcm_loop_shutdown_session; | 1489 | fabric->tf_ops.shutdown_session = &tcm_loop_shutdown_session; |
1489 | fabric->tf_ops.close_session = &tcm_loop_close_session; | 1490 | fabric->tf_ops.close_session = &tcm_loop_close_session; |
1490 | fabric->tf_ops.sess_get_index = &tcm_loop_sess_get_index; | 1491 | fabric->tf_ops.sess_get_index = &tcm_loop_sess_get_index; |
1491 | fabric->tf_ops.sess_get_initiator_sid = NULL; | 1492 | fabric->tf_ops.sess_get_initiator_sid = NULL; |
1492 | fabric->tf_ops.write_pending = &tcm_loop_write_pending; | 1493 | fabric->tf_ops.write_pending = &tcm_loop_write_pending; |
1493 | fabric->tf_ops.write_pending_status = &tcm_loop_write_pending_status; | 1494 | fabric->tf_ops.write_pending_status = &tcm_loop_write_pending_status; |
1494 | /* | 1495 | /* |
1495 | * Not used for TCM loopback | 1496 | * Not used for TCM loopback |
1496 | */ | 1497 | */ |
1497 | fabric->tf_ops.set_default_node_attributes = | 1498 | fabric->tf_ops.set_default_node_attributes = |
1498 | &tcm_loop_set_default_node_attributes; | 1499 | &tcm_loop_set_default_node_attributes; |
1499 | fabric->tf_ops.get_task_tag = &tcm_loop_get_task_tag; | 1500 | fabric->tf_ops.get_task_tag = &tcm_loop_get_task_tag; |
1500 | fabric->tf_ops.get_cmd_state = &tcm_loop_get_cmd_state; | 1501 | fabric->tf_ops.get_cmd_state = &tcm_loop_get_cmd_state; |
1501 | fabric->tf_ops.queue_data_in = &tcm_loop_queue_data_in; | 1502 | fabric->tf_ops.queue_data_in = &tcm_loop_queue_data_in; |
1502 | fabric->tf_ops.queue_status = &tcm_loop_queue_status; | 1503 | fabric->tf_ops.queue_status = &tcm_loop_queue_status; |
1503 | fabric->tf_ops.queue_tm_rsp = &tcm_loop_queue_tm_rsp; | 1504 | fabric->tf_ops.queue_tm_rsp = &tcm_loop_queue_tm_rsp; |
1504 | fabric->tf_ops.aborted_task = &tcm_loop_aborted_task; | 1505 | fabric->tf_ops.aborted_task = &tcm_loop_aborted_task; |
1505 | 1506 | ||
1506 | /* | 1507 | /* |
1507 | * Setup function pointers for generic logic in target_core_fabric_configfs.c | 1508 | * Setup function pointers for generic logic in target_core_fabric_configfs.c |
1508 | */ | 1509 | */ |
1509 | fabric->tf_ops.fabric_make_wwn = &tcm_loop_make_scsi_hba; | 1510 | fabric->tf_ops.fabric_make_wwn = &tcm_loop_make_scsi_hba; |
1510 | fabric->tf_ops.fabric_drop_wwn = &tcm_loop_drop_scsi_hba; | 1511 | fabric->tf_ops.fabric_drop_wwn = &tcm_loop_drop_scsi_hba; |
1511 | fabric->tf_ops.fabric_make_tpg = &tcm_loop_make_naa_tpg; | 1512 | fabric->tf_ops.fabric_make_tpg = &tcm_loop_make_naa_tpg; |
1512 | fabric->tf_ops.fabric_drop_tpg = &tcm_loop_drop_naa_tpg; | 1513 | fabric->tf_ops.fabric_drop_tpg = &tcm_loop_drop_naa_tpg; |
1513 | /* | 1514 | /* |
1514 | * fabric_post_link() and fabric_pre_unlink() are used for | 1515 | * fabric_post_link() and fabric_pre_unlink() are used for |
1515 | * registration and release of TCM Loop Virtual SCSI LUNs. | 1516 | * registration and release of TCM Loop Virtual SCSI LUNs. |
1516 | */ | 1517 | */ |
1517 | fabric->tf_ops.fabric_post_link = &tcm_loop_port_link; | 1518 | fabric->tf_ops.fabric_post_link = &tcm_loop_port_link; |
1518 | fabric->tf_ops.fabric_pre_unlink = &tcm_loop_port_unlink; | 1519 | fabric->tf_ops.fabric_pre_unlink = &tcm_loop_port_unlink; |
1519 | fabric->tf_ops.fabric_make_np = NULL; | 1520 | fabric->tf_ops.fabric_make_np = NULL; |
1520 | fabric->tf_ops.fabric_drop_np = NULL; | 1521 | fabric->tf_ops.fabric_drop_np = NULL; |
1521 | /* | 1522 | /* |
1522 | * Setup default attribute lists for various fabric->tf_cit_tmpl | 1523 | * Setup default attribute lists for various fabric->tf_cit_tmpl |
1523 | */ | 1524 | */ |
1524 | fabric->tf_cit_tmpl.tfc_wwn_cit.ct_attrs = tcm_loop_wwn_attrs; | 1525 | fabric->tf_cit_tmpl.tfc_wwn_cit.ct_attrs = tcm_loop_wwn_attrs; |
1525 | fabric->tf_cit_tmpl.tfc_tpg_base_cit.ct_attrs = tcm_loop_tpg_attrs; | 1526 | fabric->tf_cit_tmpl.tfc_tpg_base_cit.ct_attrs = tcm_loop_tpg_attrs; |
1526 | fabric->tf_cit_tmpl.tfc_tpg_attrib_cit.ct_attrs = NULL; | 1527 | fabric->tf_cit_tmpl.tfc_tpg_attrib_cit.ct_attrs = NULL; |
1527 | fabric->tf_cit_tmpl.tfc_tpg_param_cit.ct_attrs = NULL; | 1528 | fabric->tf_cit_tmpl.tfc_tpg_param_cit.ct_attrs = NULL; |
1528 | fabric->tf_cit_tmpl.tfc_tpg_np_base_cit.ct_attrs = NULL; | 1529 | fabric->tf_cit_tmpl.tfc_tpg_np_base_cit.ct_attrs = NULL; |
1529 | /* | 1530 | /* |
1530 | * Once fabric->tf_ops has been setup, now register the fabric for | 1531 | * Once fabric->tf_ops has been setup, now register the fabric for |
1531 | * use within TCM | 1532 | * use within TCM |
1532 | */ | 1533 | */ |
1533 | ret = target_fabric_configfs_register(fabric); | 1534 | ret = target_fabric_configfs_register(fabric); |
1534 | if (ret < 0) { | 1535 | if (ret < 0) { |
1535 | pr_err("target_fabric_configfs_register() for" | 1536 | pr_err("target_fabric_configfs_register() for" |
1536 | " TCM_Loop failed!\n"); | 1537 | " TCM_Loop failed!\n"); |
1537 | target_fabric_configfs_free(fabric); | 1538 | target_fabric_configfs_free(fabric); |
1538 | return -1; | 1539 | return -1; |
1539 | } | 1540 | } |
1540 | /* | 1541 | /* |
1541 | * Setup our local pointer to *fabric. | 1542 | * Setup our local pointer to *fabric. |
1542 | */ | 1543 | */ |
1543 | tcm_loop_fabric_configfs = fabric; | 1544 | tcm_loop_fabric_configfs = fabric; |
1544 | pr_debug("TCM_LOOP[0] - Set fabric ->" | 1545 | pr_debug("TCM_LOOP[0] - Set fabric ->" |
1545 | " tcm_loop_fabric_configfs\n"); | 1546 | " tcm_loop_fabric_configfs\n"); |
1546 | return 0; | 1547 | return 0; |
1547 | } | 1548 | } |
1548 | 1549 | ||
1549 | static void tcm_loop_deregister_configfs(void) | 1550 | static void tcm_loop_deregister_configfs(void) |
1550 | { | 1551 | { |
1551 | if (!tcm_loop_fabric_configfs) | 1552 | if (!tcm_loop_fabric_configfs) |
1552 | return; | 1553 | return; |
1553 | 1554 | ||
1554 | target_fabric_configfs_deregister(tcm_loop_fabric_configfs); | 1555 | target_fabric_configfs_deregister(tcm_loop_fabric_configfs); |
1555 | tcm_loop_fabric_configfs = NULL; | 1556 | tcm_loop_fabric_configfs = NULL; |
1556 | pr_debug("TCM_LOOP[0] - Cleared" | 1557 | pr_debug("TCM_LOOP[0] - Cleared" |
1557 | " tcm_loop_fabric_configfs\n"); | 1558 | " tcm_loop_fabric_configfs\n"); |
1558 | } | 1559 | } |
1559 | 1560 | ||
1560 | static int __init tcm_loop_fabric_init(void) | 1561 | static int __init tcm_loop_fabric_init(void) |
1561 | { | 1562 | { |
1562 | int ret = -ENOMEM; | 1563 | int ret = -ENOMEM; |
1563 | 1564 | ||
1564 | tcm_loop_workqueue = alloc_workqueue("tcm_loop", 0, 0); | 1565 | tcm_loop_workqueue = alloc_workqueue("tcm_loop", 0, 0); |
1565 | if (!tcm_loop_workqueue) | 1566 | if (!tcm_loop_workqueue) |
1566 | goto out; | 1567 | goto out; |
1567 | 1568 | ||
1568 | tcm_loop_cmd_cache = kmem_cache_create("tcm_loop_cmd_cache", | 1569 | tcm_loop_cmd_cache = kmem_cache_create("tcm_loop_cmd_cache", |
1569 | sizeof(struct tcm_loop_cmd), | 1570 | sizeof(struct tcm_loop_cmd), |
1570 | __alignof__(struct tcm_loop_cmd), | 1571 | __alignof__(struct tcm_loop_cmd), |
1571 | 0, NULL); | 1572 | 0, NULL); |
1572 | if (!tcm_loop_cmd_cache) { | 1573 | if (!tcm_loop_cmd_cache) { |
1573 | pr_debug("kmem_cache_create() for" | 1574 | pr_debug("kmem_cache_create() for" |
1574 | " tcm_loop_cmd_cache failed\n"); | 1575 | " tcm_loop_cmd_cache failed\n"); |
1575 | goto out_destroy_workqueue; | 1576 | goto out_destroy_workqueue; |
1576 | } | 1577 | } |
1577 | 1578 | ||
1578 | ret = tcm_loop_alloc_core_bus(); | 1579 | ret = tcm_loop_alloc_core_bus(); |
1579 | if (ret) | 1580 | if (ret) |
1580 | goto out_destroy_cache; | 1581 | goto out_destroy_cache; |
1581 | 1582 | ||
1582 | ret = tcm_loop_register_configfs(); | 1583 | ret = tcm_loop_register_configfs(); |
1583 | if (ret) | 1584 | if (ret) |
1584 | goto out_release_core_bus; | 1585 | goto out_release_core_bus; |
1585 | 1586 | ||
1586 | return 0; | 1587 | return 0; |
1587 | 1588 | ||
1588 | out_release_core_bus: | 1589 | out_release_core_bus: |
1589 | tcm_loop_release_core_bus(); | 1590 | tcm_loop_release_core_bus(); |
1590 | out_destroy_cache: | 1591 | out_destroy_cache: |
1591 | kmem_cache_destroy(tcm_loop_cmd_cache); | 1592 | kmem_cache_destroy(tcm_loop_cmd_cache); |
1592 | out_destroy_workqueue: | 1593 | out_destroy_workqueue: |
1593 | destroy_workqueue(tcm_loop_workqueue); | 1594 | destroy_workqueue(tcm_loop_workqueue); |
1594 | out: | 1595 | out: |
1595 | return ret; | 1596 | return ret; |
1596 | } | 1597 | } |
1597 | 1598 | ||
1598 | static void __exit tcm_loop_fabric_exit(void) | 1599 | static void __exit tcm_loop_fabric_exit(void) |
1599 | { | 1600 | { |
1600 | tcm_loop_deregister_configfs(); | 1601 | tcm_loop_deregister_configfs(); |
1601 | tcm_loop_release_core_bus(); | 1602 | tcm_loop_release_core_bus(); |
1602 | kmem_cache_destroy(tcm_loop_cmd_cache); | 1603 | kmem_cache_destroy(tcm_loop_cmd_cache); |
1603 | destroy_workqueue(tcm_loop_workqueue); | 1604 | destroy_workqueue(tcm_loop_workqueue); |
1604 | } | 1605 | } |
1605 | 1606 | ||
1606 | MODULE_DESCRIPTION("TCM loopback virtual Linux/SCSI fabric module"); | 1607 | MODULE_DESCRIPTION("TCM loopback virtual Linux/SCSI fabric module"); |
1607 | MODULE_AUTHOR("Nicholas A. Bellinger <nab@risingtidesystems.com>"); | 1608 | MODULE_AUTHOR("Nicholas A. Bellinger <nab@risingtidesystems.com>"); |
1608 | MODULE_LICENSE("GPL"); | 1609 | MODULE_LICENSE("GPL"); |
1609 | module_init(tcm_loop_fabric_init); | 1610 | module_init(tcm_loop_fabric_init); |
1610 | module_exit(tcm_loop_fabric_exit); | 1611 | module_exit(tcm_loop_fabric_exit); |
1611 | 1612 |