diff options
author | Nicholas Bellinger <nab@linux-iscsi.org> | 2011-07-23 02:43:04 -0400 |
---|---|---|
committer | Nicholas Bellinger <nab@linux-iscsi.org> | 2011-07-26 05:16:43 -0400 |
commit | e48354ce078c079996f89d715dfa44814b4eba01 (patch) | |
tree | 7a593b6dd4864073944160fe227aae5d54e587ab /drivers/target/iscsi | |
parent | 8304bbceee505742925b487fd8ea56e1f8b4b805 (diff) |
iscsi-target: Add iSCSI fabric support for target v4.1
The Linux-iSCSI.org target module is a full featured in-kernel
software implementation of iSCSI target mode (RFC-3720) for the
current WIP mainline target v4.1 infrastructure code for the v3.1
kernel. More information can be found here:
http://linux-iscsi.org/wiki/ISCSI
This includes support for:
* RFC-3720 defined request / response state machines and support for
all defined iSCSI operation codes from Section 10.2.1.2 using libiscsi
include/scsi/iscsi_proto.h PDU definitions
* Target v4.1 compatible control plane using the generic layout in
target_core_fabric_configfs.c and fabric dependent attributes
within /sys/kernel/config/target/iscsi/ subdirectories.
* Target v4.1 compatible iSCSI statistics based on RFC-4544 (iSCSI MIBS)
* Support for IPv6 and IPv4 network portals in M:N mapping to TPGs
* iSCSI Error Recovery Hierarchy support
* Per iSCSI connection RX/TX thread pair scheduling affinity
* crc32c + crc32c_intel SSEv4 instruction offload support using libcrypto
* CHAP Authentication support using libcrypto
* Conversion to use internal SGl allocation with iscsit_alloc_buffs() ->
transport_generic_map_mem_to_cmd()
(nab: Fix iscsi_proto.h struct scsi_lun usage from linux-next in commit:
iscsi: Use struct scsi_lun in iscsi structs instead of u8[8])
(nab: Fix 32-bit compile warnings)
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Andy Grover <agrover@redhat.com>
Acked-by: Roland Dreier <roland@kernel.org>
Signed-off-by: Nicholas A. Bellinger <nab@linux-iscsi.org>
Diffstat (limited to 'drivers/target/iscsi')
39 files changed, 22097 insertions, 0 deletions
diff --git a/drivers/target/iscsi/Kconfig b/drivers/target/iscsi/Kconfig new file mode 100644 index 000000000000..564ff4e0dbc4 --- /dev/null +++ b/drivers/target/iscsi/Kconfig | |||
@@ -0,0 +1,8 @@ | |||
1 | config ISCSI_TARGET | ||
2 | tristate "Linux-iSCSI.org iSCSI Target Mode Stack" | ||
3 | select CRYPTO | ||
4 | select CRYPTO_CRC32C | ||
5 | select CRYPTO_CRC32C_INTEL if X86 | ||
6 | help | ||
7 | Say M here to enable the ConfigFS enabled Linux-iSCSI.org iSCSI | ||
8 | Target Mode Stack. | ||
diff --git a/drivers/target/iscsi/Makefile b/drivers/target/iscsi/Makefile new file mode 100644 index 000000000000..5b9a2cf7f0a9 --- /dev/null +++ b/drivers/target/iscsi/Makefile | |||
@@ -0,0 +1,20 @@ | |||
1 | iscsi_target_mod-y += iscsi_target_parameters.o \ | ||
2 | iscsi_target_seq_pdu_list.o \ | ||
3 | iscsi_target_tq.o \ | ||
4 | iscsi_target_auth.o \ | ||
5 | iscsi_target_datain_values.o \ | ||
6 | iscsi_target_device.o \ | ||
7 | iscsi_target_erl0.o \ | ||
8 | iscsi_target_erl1.o \ | ||
9 | iscsi_target_erl2.o \ | ||
10 | iscsi_target_login.o \ | ||
11 | iscsi_target_nego.o \ | ||
12 | iscsi_target_nodeattrib.o \ | ||
13 | iscsi_target_tmr.o \ | ||
14 | iscsi_target_tpg.o \ | ||
15 | iscsi_target_util.o \ | ||
16 | iscsi_target.o \ | ||
17 | iscsi_target_configfs.o \ | ||
18 | iscsi_target_stat.o | ||
19 | |||
20 | obj-$(CONFIG_ISCSI_TARGET) += iscsi_target_mod.o | ||
diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c new file mode 100644 index 000000000000..14c81c4265bd --- /dev/null +++ b/drivers/target/iscsi/iscsi_target.c | |||
@@ -0,0 +1,4559 @@ | |||
1 | /******************************************************************************* | ||
2 | * This file contains main functions related to the iSCSI Target Core Driver. | ||
3 | * | ||
4 | * \u00a9 Copyright 2007-2011 RisingTide Systems LLC. | ||
5 | * | ||
6 | * Licensed to the Linux Foundation under the General Public License (GPL) version 2. | ||
7 | * | ||
8 | * Author: Nicholas A. Bellinger <nab@linux-iscsi.org> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | ******************************************************************************/ | ||
20 | |||
21 | #include <linux/string.h> | ||
22 | #include <linux/kthread.h> | ||
23 | #include <linux/crypto.h> | ||
24 | #include <linux/completion.h> | ||
25 | #include <asm/unaligned.h> | ||
26 | #include <scsi/scsi_device.h> | ||
27 | #include <scsi/iscsi_proto.h> | ||
28 | #include <target/target_core_base.h> | ||
29 | #include <target/target_core_tmr.h> | ||
30 | #include <target/target_core_transport.h> | ||
31 | |||
32 | #include "iscsi_target_core.h" | ||
33 | #include "iscsi_target_parameters.h" | ||
34 | #include "iscsi_target_seq_pdu_list.h" | ||
35 | #include "iscsi_target_tq.h" | ||
36 | #include "iscsi_target_configfs.h" | ||
37 | #include "iscsi_target_datain_values.h" | ||
38 | #include "iscsi_target_erl0.h" | ||
39 | #include "iscsi_target_erl1.h" | ||
40 | #include "iscsi_target_erl2.h" | ||
41 | #include "iscsi_target_login.h" | ||
42 | #include "iscsi_target_tmr.h" | ||
43 | #include "iscsi_target_tpg.h" | ||
44 | #include "iscsi_target_util.h" | ||
45 | #include "iscsi_target.h" | ||
46 | #include "iscsi_target_device.h" | ||
47 | #include "iscsi_target_stat.h" | ||
48 | |||
49 | static LIST_HEAD(g_tiqn_list); | ||
50 | static LIST_HEAD(g_np_list); | ||
51 | static DEFINE_SPINLOCK(tiqn_lock); | ||
52 | static DEFINE_SPINLOCK(np_lock); | ||
53 | |||
54 | static struct idr tiqn_idr; | ||
55 | struct idr sess_idr; | ||
56 | struct mutex auth_id_lock; | ||
57 | spinlock_t sess_idr_lock; | ||
58 | |||
59 | struct iscsit_global *iscsit_global; | ||
60 | |||
61 | struct kmem_cache *lio_cmd_cache; | ||
62 | struct kmem_cache *lio_qr_cache; | ||
63 | struct kmem_cache *lio_dr_cache; | ||
64 | struct kmem_cache *lio_ooo_cache; | ||
65 | struct kmem_cache *lio_r2t_cache; | ||
66 | |||
67 | static int iscsit_handle_immediate_data(struct iscsi_cmd *, | ||
68 | unsigned char *buf, u32); | ||
69 | static int iscsit_logout_post_handler(struct iscsi_cmd *, struct iscsi_conn *); | ||
70 | |||
71 | struct iscsi_tiqn *iscsit_get_tiqn_for_login(unsigned char *buf) | ||
72 | { | ||
73 | struct iscsi_tiqn *tiqn = NULL; | ||
74 | |||
75 | spin_lock(&tiqn_lock); | ||
76 | list_for_each_entry(tiqn, &g_tiqn_list, tiqn_list) { | ||
77 | if (!strcmp(tiqn->tiqn, buf)) { | ||
78 | |||
79 | spin_lock(&tiqn->tiqn_state_lock); | ||
80 | if (tiqn->tiqn_state == TIQN_STATE_ACTIVE) { | ||
81 | tiqn->tiqn_access_count++; | ||
82 | spin_unlock(&tiqn->tiqn_state_lock); | ||
83 | spin_unlock(&tiqn_lock); | ||
84 | return tiqn; | ||
85 | } | ||
86 | spin_unlock(&tiqn->tiqn_state_lock); | ||
87 | } | ||
88 | } | ||
89 | spin_unlock(&tiqn_lock); | ||
90 | |||
91 | return NULL; | ||
92 | } | ||
93 | |||
94 | static int iscsit_set_tiqn_shutdown(struct iscsi_tiqn *tiqn) | ||
95 | { | ||
96 | spin_lock(&tiqn->tiqn_state_lock); | ||
97 | if (tiqn->tiqn_state == TIQN_STATE_ACTIVE) { | ||
98 | tiqn->tiqn_state = TIQN_STATE_SHUTDOWN; | ||
99 | spin_unlock(&tiqn->tiqn_state_lock); | ||
100 | return 0; | ||
101 | } | ||
102 | spin_unlock(&tiqn->tiqn_state_lock); | ||
103 | |||
104 | return -1; | ||
105 | } | ||
106 | |||
107 | void iscsit_put_tiqn_for_login(struct iscsi_tiqn *tiqn) | ||
108 | { | ||
109 | spin_lock(&tiqn->tiqn_state_lock); | ||
110 | tiqn->tiqn_access_count--; | ||
111 | spin_unlock(&tiqn->tiqn_state_lock); | ||
112 | } | ||
113 | |||
114 | /* | ||
115 | * Note that IQN formatting is expected to be done in userspace, and | ||
116 | * no explict IQN format checks are done here. | ||
117 | */ | ||
118 | struct iscsi_tiqn *iscsit_add_tiqn(unsigned char *buf) | ||
119 | { | ||
120 | struct iscsi_tiqn *tiqn = NULL; | ||
121 | int ret; | ||
122 | |||
123 | if (strlen(buf) > ISCSI_IQN_LEN) { | ||
124 | pr_err("Target IQN exceeds %d bytes\n", | ||
125 | ISCSI_IQN_LEN); | ||
126 | return ERR_PTR(-EINVAL); | ||
127 | } | ||
128 | |||
129 | tiqn = kzalloc(sizeof(struct iscsi_tiqn), GFP_KERNEL); | ||
130 | if (!tiqn) { | ||
131 | pr_err("Unable to allocate struct iscsi_tiqn\n"); | ||
132 | return ERR_PTR(-ENOMEM); | ||
133 | } | ||
134 | |||
135 | sprintf(tiqn->tiqn, "%s", buf); | ||
136 | INIT_LIST_HEAD(&tiqn->tiqn_list); | ||
137 | INIT_LIST_HEAD(&tiqn->tiqn_tpg_list); | ||
138 | spin_lock_init(&tiqn->tiqn_state_lock); | ||
139 | spin_lock_init(&tiqn->tiqn_tpg_lock); | ||
140 | spin_lock_init(&tiqn->sess_err_stats.lock); | ||
141 | spin_lock_init(&tiqn->login_stats.lock); | ||
142 | spin_lock_init(&tiqn->logout_stats.lock); | ||
143 | |||
144 | if (!idr_pre_get(&tiqn_idr, GFP_KERNEL)) { | ||
145 | pr_err("idr_pre_get() for tiqn_idr failed\n"); | ||
146 | kfree(tiqn); | ||
147 | return ERR_PTR(-ENOMEM); | ||
148 | } | ||
149 | tiqn->tiqn_state = TIQN_STATE_ACTIVE; | ||
150 | |||
151 | spin_lock(&tiqn_lock); | ||
152 | ret = idr_get_new(&tiqn_idr, NULL, &tiqn->tiqn_index); | ||
153 | if (ret < 0) { | ||
154 | pr_err("idr_get_new() failed for tiqn->tiqn_index\n"); | ||
155 | spin_unlock(&tiqn_lock); | ||
156 | kfree(tiqn); | ||
157 | return ERR_PTR(ret); | ||
158 | } | ||
159 | list_add_tail(&tiqn->tiqn_list, &g_tiqn_list); | ||
160 | spin_unlock(&tiqn_lock); | ||
161 | |||
162 | pr_debug("CORE[0] - Added iSCSI Target IQN: %s\n", tiqn->tiqn); | ||
163 | |||
164 | return tiqn; | ||
165 | |||
166 | } | ||
167 | |||
168 | static void iscsit_wait_for_tiqn(struct iscsi_tiqn *tiqn) | ||
169 | { | ||
170 | /* | ||
171 | * Wait for accesses to said struct iscsi_tiqn to end. | ||
172 | */ | ||
173 | spin_lock(&tiqn->tiqn_state_lock); | ||
174 | while (tiqn->tiqn_access_count != 0) { | ||
175 | spin_unlock(&tiqn->tiqn_state_lock); | ||
176 | msleep(10); | ||
177 | spin_lock(&tiqn->tiqn_state_lock); | ||
178 | } | ||
179 | spin_unlock(&tiqn->tiqn_state_lock); | ||
180 | } | ||
181 | |||
182 | void iscsit_del_tiqn(struct iscsi_tiqn *tiqn) | ||
183 | { | ||
184 | /* | ||
185 | * iscsit_set_tiqn_shutdown sets tiqn->tiqn_state = TIQN_STATE_SHUTDOWN | ||
186 | * while holding tiqn->tiqn_state_lock. This means that all subsequent | ||
187 | * attempts to access this struct iscsi_tiqn will fail from both transport | ||
188 | * fabric and control code paths. | ||
189 | */ | ||
190 | if (iscsit_set_tiqn_shutdown(tiqn) < 0) { | ||
191 | pr_err("iscsit_set_tiqn_shutdown() failed\n"); | ||
192 | return; | ||
193 | } | ||
194 | |||
195 | iscsit_wait_for_tiqn(tiqn); | ||
196 | |||
197 | spin_lock(&tiqn_lock); | ||
198 | list_del(&tiqn->tiqn_list); | ||
199 | idr_remove(&tiqn_idr, tiqn->tiqn_index); | ||
200 | spin_unlock(&tiqn_lock); | ||
201 | |||
202 | pr_debug("CORE[0] - Deleted iSCSI Target IQN: %s\n", | ||
203 | tiqn->tiqn); | ||
204 | kfree(tiqn); | ||
205 | } | ||
206 | |||
207 | int iscsit_access_np(struct iscsi_np *np, struct iscsi_portal_group *tpg) | ||
208 | { | ||
209 | int ret; | ||
210 | /* | ||
211 | * Determine if the network portal is accepting storage traffic. | ||
212 | */ | ||
213 | spin_lock_bh(&np->np_thread_lock); | ||
214 | if (np->np_thread_state != ISCSI_NP_THREAD_ACTIVE) { | ||
215 | spin_unlock_bh(&np->np_thread_lock); | ||
216 | return -1; | ||
217 | } | ||
218 | if (np->np_login_tpg) { | ||
219 | pr_err("np->np_login_tpg() is not NULL!\n"); | ||
220 | spin_unlock_bh(&np->np_thread_lock); | ||
221 | return -1; | ||
222 | } | ||
223 | spin_unlock_bh(&np->np_thread_lock); | ||
224 | /* | ||
225 | * Determine if the portal group is accepting storage traffic. | ||
226 | */ | ||
227 | spin_lock_bh(&tpg->tpg_state_lock); | ||
228 | if (tpg->tpg_state != TPG_STATE_ACTIVE) { | ||
229 | spin_unlock_bh(&tpg->tpg_state_lock); | ||
230 | return -1; | ||
231 | } | ||
232 | spin_unlock_bh(&tpg->tpg_state_lock); | ||
233 | |||
234 | /* | ||
235 | * Here we serialize access across the TIQN+TPG Tuple. | ||
236 | */ | ||
237 | ret = mutex_lock_interruptible(&tpg->np_login_lock); | ||
238 | if ((ret != 0) || signal_pending(current)) | ||
239 | return -1; | ||
240 | |||
241 | spin_lock_bh(&np->np_thread_lock); | ||
242 | np->np_login_tpg = tpg; | ||
243 | spin_unlock_bh(&np->np_thread_lock); | ||
244 | |||
245 | return 0; | ||
246 | } | ||
247 | |||
248 | int iscsit_deaccess_np(struct iscsi_np *np, struct iscsi_portal_group *tpg) | ||
249 | { | ||
250 | struct iscsi_tiqn *tiqn = tpg->tpg_tiqn; | ||
251 | |||
252 | spin_lock_bh(&np->np_thread_lock); | ||
253 | np->np_login_tpg = NULL; | ||
254 | spin_unlock_bh(&np->np_thread_lock); | ||
255 | |||
256 | mutex_unlock(&tpg->np_login_lock); | ||
257 | |||
258 | if (tiqn) | ||
259 | iscsit_put_tiqn_for_login(tiqn); | ||
260 | |||
261 | return 0; | ||
262 | } | ||
263 | |||
264 | static struct iscsi_np *iscsit_get_np( | ||
265 | struct __kernel_sockaddr_storage *sockaddr, | ||
266 | int network_transport) | ||
267 | { | ||
268 | struct sockaddr_in *sock_in, *sock_in_e; | ||
269 | struct sockaddr_in6 *sock_in6, *sock_in6_e; | ||
270 | struct iscsi_np *np; | ||
271 | int ip_match = 0; | ||
272 | u16 port; | ||
273 | |||
274 | spin_lock_bh(&np_lock); | ||
275 | list_for_each_entry(np, &g_np_list, np_list) { | ||
276 | spin_lock(&np->np_thread_lock); | ||
277 | if (np->np_thread_state != ISCSI_NP_THREAD_ACTIVE) { | ||
278 | spin_unlock(&np->np_thread_lock); | ||
279 | continue; | ||
280 | } | ||
281 | |||
282 | if (sockaddr->ss_family == AF_INET6) { | ||
283 | sock_in6 = (struct sockaddr_in6 *)sockaddr; | ||
284 | sock_in6_e = (struct sockaddr_in6 *)&np->np_sockaddr; | ||
285 | |||
286 | if (!memcmp((void *)&sock_in6->sin6_addr.in6_u, | ||
287 | (void *)&sock_in6_e->sin6_addr.in6_u, | ||
288 | sizeof(struct in6_addr))) | ||
289 | ip_match = 1; | ||
290 | |||
291 | port = ntohs(sock_in6->sin6_port); | ||
292 | } else { | ||
293 | sock_in = (struct sockaddr_in *)sockaddr; | ||
294 | sock_in_e = (struct sockaddr_in *)&np->np_sockaddr; | ||
295 | |||
296 | if (sock_in->sin_addr.s_addr == | ||
297 | sock_in_e->sin_addr.s_addr) | ||
298 | ip_match = 1; | ||
299 | |||
300 | port = ntohs(sock_in->sin_port); | ||
301 | } | ||
302 | |||
303 | if ((ip_match == 1) && (np->np_port == port) && | ||
304 | (np->np_network_transport == network_transport)) { | ||
305 | /* | ||
306 | * Increment the np_exports reference count now to | ||
307 | * prevent iscsit_del_np() below from being called | ||
308 | * while iscsi_tpg_add_network_portal() is called. | ||
309 | */ | ||
310 | np->np_exports++; | ||
311 | spin_unlock(&np->np_thread_lock); | ||
312 | spin_unlock_bh(&np_lock); | ||
313 | return np; | ||
314 | } | ||
315 | spin_unlock(&np->np_thread_lock); | ||
316 | } | ||
317 | spin_unlock_bh(&np_lock); | ||
318 | |||
319 | return NULL; | ||
320 | } | ||
321 | |||
322 | struct iscsi_np *iscsit_add_np( | ||
323 | struct __kernel_sockaddr_storage *sockaddr, | ||
324 | char *ip_str, | ||
325 | int network_transport) | ||
326 | { | ||
327 | struct sockaddr_in *sock_in; | ||
328 | struct sockaddr_in6 *sock_in6; | ||
329 | struct iscsi_np *np; | ||
330 | int ret; | ||
331 | /* | ||
332 | * Locate the existing struct iscsi_np if already active.. | ||
333 | */ | ||
334 | np = iscsit_get_np(sockaddr, network_transport); | ||
335 | if (np) | ||
336 | return np; | ||
337 | |||
338 | np = kzalloc(sizeof(struct iscsi_np), GFP_KERNEL); | ||
339 | if (!np) { | ||
340 | pr_err("Unable to allocate memory for struct iscsi_np\n"); | ||
341 | return ERR_PTR(-ENOMEM); | ||
342 | } | ||
343 | |||
344 | np->np_flags |= NPF_IP_NETWORK; | ||
345 | if (sockaddr->ss_family == AF_INET6) { | ||
346 | sock_in6 = (struct sockaddr_in6 *)sockaddr; | ||
347 | snprintf(np->np_ip, IPV6_ADDRESS_SPACE, "%s", ip_str); | ||
348 | np->np_port = ntohs(sock_in6->sin6_port); | ||
349 | } else { | ||
350 | sock_in = (struct sockaddr_in *)sockaddr; | ||
351 | sprintf(np->np_ip, "%s", ip_str); | ||
352 | np->np_port = ntohs(sock_in->sin_port); | ||
353 | } | ||
354 | |||
355 | np->np_network_transport = network_transport; | ||
356 | spin_lock_init(&np->np_thread_lock); | ||
357 | init_completion(&np->np_restart_comp); | ||
358 | INIT_LIST_HEAD(&np->np_list); | ||
359 | |||
360 | ret = iscsi_target_setup_login_socket(np, sockaddr); | ||
361 | if (ret != 0) { | ||
362 | kfree(np); | ||
363 | return ERR_PTR(ret); | ||
364 | } | ||
365 | |||
366 | np->np_thread = kthread_run(iscsi_target_login_thread, np, "iscsi_np"); | ||
367 | if (IS_ERR(np->np_thread)) { | ||
368 | pr_err("Unable to create kthread: iscsi_np\n"); | ||
369 | ret = PTR_ERR(np->np_thread); | ||
370 | kfree(np); | ||
371 | return ERR_PTR(ret); | ||
372 | } | ||
373 | /* | ||
374 | * Increment the np_exports reference count now to prevent | ||
375 | * iscsit_del_np() below from being run while a new call to | ||
376 | * iscsi_tpg_add_network_portal() for a matching iscsi_np is | ||
377 | * active. We don't need to hold np->np_thread_lock at this | ||
378 | * point because iscsi_np has not been added to g_np_list yet. | ||
379 | */ | ||
380 | np->np_exports = 1; | ||
381 | |||
382 | spin_lock_bh(&np_lock); | ||
383 | list_add_tail(&np->np_list, &g_np_list); | ||
384 | spin_unlock_bh(&np_lock); | ||
385 | |||
386 | pr_debug("CORE[0] - Added Network Portal: %s:%hu on %s\n", | ||
387 | np->np_ip, np->np_port, (np->np_network_transport == ISCSI_TCP) ? | ||
388 | "TCP" : "SCTP"); | ||
389 | |||
390 | return np; | ||
391 | } | ||
392 | |||
393 | int iscsit_reset_np_thread( | ||
394 | struct iscsi_np *np, | ||
395 | struct iscsi_tpg_np *tpg_np, | ||
396 | struct iscsi_portal_group *tpg) | ||
397 | { | ||
398 | spin_lock_bh(&np->np_thread_lock); | ||
399 | if (tpg && tpg_np) { | ||
400 | /* | ||
401 | * The reset operation need only be performed when the | ||
402 | * passed struct iscsi_portal_group has a login in progress | ||
403 | * to one of the network portals. | ||
404 | */ | ||
405 | if (tpg_np->tpg_np->np_login_tpg != tpg) { | ||
406 | spin_unlock_bh(&np->np_thread_lock); | ||
407 | return 0; | ||
408 | } | ||
409 | } | ||
410 | if (np->np_thread_state == ISCSI_NP_THREAD_INACTIVE) { | ||
411 | spin_unlock_bh(&np->np_thread_lock); | ||
412 | return 0; | ||
413 | } | ||
414 | np->np_thread_state = ISCSI_NP_THREAD_RESET; | ||
415 | |||
416 | if (np->np_thread) { | ||
417 | spin_unlock_bh(&np->np_thread_lock); | ||
418 | send_sig(SIGINT, np->np_thread, 1); | ||
419 | wait_for_completion(&np->np_restart_comp); | ||
420 | spin_lock_bh(&np->np_thread_lock); | ||
421 | } | ||
422 | spin_unlock_bh(&np->np_thread_lock); | ||
423 | |||
424 | return 0; | ||
425 | } | ||
426 | |||
427 | int iscsit_del_np_comm(struct iscsi_np *np) | ||
428 | { | ||
429 | if (!np->np_socket) | ||
430 | return 0; | ||
431 | |||
432 | /* | ||
433 | * Some network transports allocate their own struct sock->file, | ||
434 | * see if we need to free any additional allocated resources. | ||
435 | */ | ||
436 | if (np->np_flags & NPF_SCTP_STRUCT_FILE) { | ||
437 | kfree(np->np_socket->file); | ||
438 | np->np_socket->file = NULL; | ||
439 | } | ||
440 | |||
441 | sock_release(np->np_socket); | ||
442 | return 0; | ||
443 | } | ||
444 | |||
445 | int iscsit_del_np(struct iscsi_np *np) | ||
446 | { | ||
447 | spin_lock_bh(&np->np_thread_lock); | ||
448 | np->np_exports--; | ||
449 | if (np->np_exports) { | ||
450 | spin_unlock_bh(&np->np_thread_lock); | ||
451 | return 0; | ||
452 | } | ||
453 | np->np_thread_state = ISCSI_NP_THREAD_SHUTDOWN; | ||
454 | spin_unlock_bh(&np->np_thread_lock); | ||
455 | |||
456 | if (np->np_thread) { | ||
457 | /* | ||
458 | * We need to send the signal to wakeup Linux/Net | ||
459 | * which may be sleeping in sock_accept().. | ||
460 | */ | ||
461 | send_sig(SIGINT, np->np_thread, 1); | ||
462 | kthread_stop(np->np_thread); | ||
463 | } | ||
464 | iscsit_del_np_comm(np); | ||
465 | |||
466 | spin_lock_bh(&np_lock); | ||
467 | list_del(&np->np_list); | ||
468 | spin_unlock_bh(&np_lock); | ||
469 | |||
470 | pr_debug("CORE[0] - Removed Network Portal: %s:%hu on %s\n", | ||
471 | np->np_ip, np->np_port, (np->np_network_transport == ISCSI_TCP) ? | ||
472 | "TCP" : "SCTP"); | ||
473 | |||
474 | kfree(np); | ||
475 | return 0; | ||
476 | } | ||
477 | |||
478 | static int __init iscsi_target_init_module(void) | ||
479 | { | ||
480 | int ret = 0; | ||
481 | |||
482 | pr_debug("iSCSI-Target "ISCSIT_VERSION"\n"); | ||
483 | |||
484 | iscsit_global = kzalloc(sizeof(struct iscsit_global), GFP_KERNEL); | ||
485 | if (!iscsit_global) { | ||
486 | pr_err("Unable to allocate memory for iscsit_global\n"); | ||
487 | return -1; | ||
488 | } | ||
489 | mutex_init(&auth_id_lock); | ||
490 | spin_lock_init(&sess_idr_lock); | ||
491 | idr_init(&tiqn_idr); | ||
492 | idr_init(&sess_idr); | ||
493 | |||
494 | ret = iscsi_target_register_configfs(); | ||
495 | if (ret < 0) | ||
496 | goto out; | ||
497 | |||
498 | ret = iscsi_thread_set_init(); | ||
499 | if (ret < 0) | ||
500 | goto configfs_out; | ||
501 | |||
502 | if (iscsi_allocate_thread_sets(TARGET_THREAD_SET_COUNT) != | ||
503 | TARGET_THREAD_SET_COUNT) { | ||
504 | pr_err("iscsi_allocate_thread_sets() returned" | ||
505 | " unexpected value!\n"); | ||
506 | goto ts_out1; | ||
507 | } | ||
508 | |||
509 | lio_cmd_cache = kmem_cache_create("lio_cmd_cache", | ||
510 | sizeof(struct iscsi_cmd), __alignof__(struct iscsi_cmd), | ||
511 | 0, NULL); | ||
512 | if (!lio_cmd_cache) { | ||
513 | pr_err("Unable to kmem_cache_create() for" | ||
514 | " lio_cmd_cache\n"); | ||
515 | goto ts_out2; | ||
516 | } | ||
517 | |||
518 | lio_qr_cache = kmem_cache_create("lio_qr_cache", | ||
519 | sizeof(struct iscsi_queue_req), | ||
520 | __alignof__(struct iscsi_queue_req), 0, NULL); | ||
521 | if (!lio_qr_cache) { | ||
522 | pr_err("nable to kmem_cache_create() for" | ||
523 | " lio_qr_cache\n"); | ||
524 | goto cmd_out; | ||
525 | } | ||
526 | |||
527 | lio_dr_cache = kmem_cache_create("lio_dr_cache", | ||
528 | sizeof(struct iscsi_datain_req), | ||
529 | __alignof__(struct iscsi_datain_req), 0, NULL); | ||
530 | if (!lio_dr_cache) { | ||
531 | pr_err("Unable to kmem_cache_create() for" | ||
532 | " lio_dr_cache\n"); | ||
533 | goto qr_out; | ||
534 | } | ||
535 | |||
536 | lio_ooo_cache = kmem_cache_create("lio_ooo_cache", | ||
537 | sizeof(struct iscsi_ooo_cmdsn), | ||
538 | __alignof__(struct iscsi_ooo_cmdsn), 0, NULL); | ||
539 | if (!lio_ooo_cache) { | ||
540 | pr_err("Unable to kmem_cache_create() for" | ||
541 | " lio_ooo_cache\n"); | ||
542 | goto dr_out; | ||
543 | } | ||
544 | |||
545 | lio_r2t_cache = kmem_cache_create("lio_r2t_cache", | ||
546 | sizeof(struct iscsi_r2t), __alignof__(struct iscsi_r2t), | ||
547 | 0, NULL); | ||
548 | if (!lio_r2t_cache) { | ||
549 | pr_err("Unable to kmem_cache_create() for" | ||
550 | " lio_r2t_cache\n"); | ||
551 | goto ooo_out; | ||
552 | } | ||
553 | |||
554 | if (iscsit_load_discovery_tpg() < 0) | ||
555 | goto r2t_out; | ||
556 | |||
557 | return ret; | ||
558 | r2t_out: | ||
559 | kmem_cache_destroy(lio_r2t_cache); | ||
560 | ooo_out: | ||
561 | kmem_cache_destroy(lio_ooo_cache); | ||
562 | dr_out: | ||
563 | kmem_cache_destroy(lio_dr_cache); | ||
564 | qr_out: | ||
565 | kmem_cache_destroy(lio_qr_cache); | ||
566 | cmd_out: | ||
567 | kmem_cache_destroy(lio_cmd_cache); | ||
568 | ts_out2: | ||
569 | iscsi_deallocate_thread_sets(); | ||
570 | ts_out1: | ||
571 | iscsi_thread_set_free(); | ||
572 | configfs_out: | ||
573 | iscsi_target_deregister_configfs(); | ||
574 | out: | ||
575 | kfree(iscsit_global); | ||
576 | return -ENOMEM; | ||
577 | } | ||
578 | |||
579 | static void __exit iscsi_target_cleanup_module(void) | ||
580 | { | ||
581 | iscsi_deallocate_thread_sets(); | ||
582 | iscsi_thread_set_free(); | ||
583 | iscsit_release_discovery_tpg(); | ||
584 | kmem_cache_destroy(lio_cmd_cache); | ||
585 | kmem_cache_destroy(lio_qr_cache); | ||
586 | kmem_cache_destroy(lio_dr_cache); | ||
587 | kmem_cache_destroy(lio_ooo_cache); | ||
588 | kmem_cache_destroy(lio_r2t_cache); | ||
589 | |||
590 | iscsi_target_deregister_configfs(); | ||
591 | |||
592 | kfree(iscsit_global); | ||
593 | } | ||
594 | |||
595 | int iscsit_add_reject( | ||
596 | u8 reason, | ||
597 | int fail_conn, | ||
598 | unsigned char *buf, | ||
599 | struct iscsi_conn *conn) | ||
600 | { | ||
601 | struct iscsi_cmd *cmd; | ||
602 | struct iscsi_reject *hdr; | ||
603 | int ret; | ||
604 | |||
605 | cmd = iscsit_allocate_cmd(conn, GFP_KERNEL); | ||
606 | if (!cmd) | ||
607 | return -1; | ||
608 | |||
609 | cmd->iscsi_opcode = ISCSI_OP_REJECT; | ||
610 | if (fail_conn) | ||
611 | cmd->cmd_flags |= ICF_REJECT_FAIL_CONN; | ||
612 | |||
613 | hdr = (struct iscsi_reject *) cmd->pdu; | ||
614 | hdr->reason = reason; | ||
615 | |||
616 | cmd->buf_ptr = kzalloc(ISCSI_HDR_LEN, GFP_KERNEL); | ||
617 | if (!cmd->buf_ptr) { | ||
618 | pr_err("Unable to allocate memory for cmd->buf_ptr\n"); | ||
619 | iscsit_release_cmd(cmd); | ||
620 | return -1; | ||
621 | } | ||
622 | memcpy(cmd->buf_ptr, buf, ISCSI_HDR_LEN); | ||
623 | |||
624 | spin_lock_bh(&conn->cmd_lock); | ||
625 | list_add_tail(&cmd->i_list, &conn->conn_cmd_list); | ||
626 | spin_unlock_bh(&conn->cmd_lock); | ||
627 | |||
628 | cmd->i_state = ISTATE_SEND_REJECT; | ||
629 | iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state); | ||
630 | |||
631 | ret = wait_for_completion_interruptible(&cmd->reject_comp); | ||
632 | if (ret != 0) | ||
633 | return -1; | ||
634 | |||
635 | return (!fail_conn) ? 0 : -1; | ||
636 | } | ||
637 | |||
638 | int iscsit_add_reject_from_cmd( | ||
639 | u8 reason, | ||
640 | int fail_conn, | ||
641 | int add_to_conn, | ||
642 | unsigned char *buf, | ||
643 | struct iscsi_cmd *cmd) | ||
644 | { | ||
645 | struct iscsi_conn *conn; | ||
646 | struct iscsi_reject *hdr; | ||
647 | int ret; | ||
648 | |||
649 | if (!cmd->conn) { | ||
650 | pr_err("cmd->conn is NULL for ITT: 0x%08x\n", | ||
651 | cmd->init_task_tag); | ||
652 | return -1; | ||
653 | } | ||
654 | conn = cmd->conn; | ||
655 | |||
656 | cmd->iscsi_opcode = ISCSI_OP_REJECT; | ||
657 | if (fail_conn) | ||
658 | cmd->cmd_flags |= ICF_REJECT_FAIL_CONN; | ||
659 | |||
660 | hdr = (struct iscsi_reject *) cmd->pdu; | ||
661 | hdr->reason = reason; | ||
662 | |||
663 | cmd->buf_ptr = kzalloc(ISCSI_HDR_LEN, GFP_KERNEL); | ||
664 | if (!cmd->buf_ptr) { | ||
665 | pr_err("Unable to allocate memory for cmd->buf_ptr\n"); | ||
666 | iscsit_release_cmd(cmd); | ||
667 | return -1; | ||
668 | } | ||
669 | memcpy(cmd->buf_ptr, buf, ISCSI_HDR_LEN); | ||
670 | |||
671 | if (add_to_conn) { | ||
672 | spin_lock_bh(&conn->cmd_lock); | ||
673 | list_add_tail(&cmd->i_list, &conn->conn_cmd_list); | ||
674 | spin_unlock_bh(&conn->cmd_lock); | ||
675 | } | ||
676 | |||
677 | cmd->i_state = ISTATE_SEND_REJECT; | ||
678 | iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state); | ||
679 | |||
680 | ret = wait_for_completion_interruptible(&cmd->reject_comp); | ||
681 | if (ret != 0) | ||
682 | return -1; | ||
683 | |||
684 | return (!fail_conn) ? 0 : -1; | ||
685 | } | ||
686 | |||
687 | /* | ||
688 | * Map some portion of the allocated scatterlist to an iovec, suitable for | ||
689 | * kernel sockets to copy data in/out. This handles both pages and slab-allocated | ||
690 | * buffers, since we have been tricky and mapped t_mem_sg to the buffer in | ||
691 | * either case (see iscsit_alloc_buffs) | ||
692 | */ | ||
693 | static int iscsit_map_iovec( | ||
694 | struct iscsi_cmd *cmd, | ||
695 | struct kvec *iov, | ||
696 | u32 data_offset, | ||
697 | u32 data_length) | ||
698 | { | ||
699 | u32 i = 0; | ||
700 | struct scatterlist *sg; | ||
701 | unsigned int page_off; | ||
702 | |||
703 | /* | ||
704 | * We have a private mapping of the allocated pages in t_mem_sg. | ||
705 | * At this point, we also know each contains a page. | ||
706 | */ | ||
707 | sg = &cmd->t_mem_sg[data_offset / PAGE_SIZE]; | ||
708 | page_off = (data_offset % PAGE_SIZE); | ||
709 | |||
710 | cmd->first_data_sg = sg; | ||
711 | cmd->first_data_sg_off = page_off; | ||
712 | |||
713 | while (data_length) { | ||
714 | u32 cur_len = min_t(u32, data_length, sg->length - page_off); | ||
715 | |||
716 | iov[i].iov_base = kmap(sg_page(sg)) + sg->offset + page_off; | ||
717 | iov[i].iov_len = cur_len; | ||
718 | |||
719 | data_length -= cur_len; | ||
720 | page_off = 0; | ||
721 | sg = sg_next(sg); | ||
722 | i++; | ||
723 | } | ||
724 | |||
725 | cmd->kmapped_nents = i; | ||
726 | |||
727 | return i; | ||
728 | } | ||
729 | |||
730 | static void iscsit_unmap_iovec(struct iscsi_cmd *cmd) | ||
731 | { | ||
732 | u32 i; | ||
733 | struct scatterlist *sg; | ||
734 | |||
735 | sg = cmd->first_data_sg; | ||
736 | |||
737 | for (i = 0; i < cmd->kmapped_nents; i++) | ||
738 | kunmap(sg_page(&sg[i])); | ||
739 | } | ||
740 | |||
741 | static void iscsit_ack_from_expstatsn(struct iscsi_conn *conn, u32 exp_statsn) | ||
742 | { | ||
743 | struct iscsi_cmd *cmd; | ||
744 | |||
745 | conn->exp_statsn = exp_statsn; | ||
746 | |||
747 | spin_lock_bh(&conn->cmd_lock); | ||
748 | list_for_each_entry(cmd, &conn->conn_cmd_list, i_list) { | ||
749 | spin_lock(&cmd->istate_lock); | ||
750 | if ((cmd->i_state == ISTATE_SENT_STATUS) && | ||
751 | (cmd->stat_sn < exp_statsn)) { | ||
752 | cmd->i_state = ISTATE_REMOVE; | ||
753 | spin_unlock(&cmd->istate_lock); | ||
754 | iscsit_add_cmd_to_immediate_queue(cmd, conn, | ||
755 | cmd->i_state); | ||
756 | continue; | ||
757 | } | ||
758 | spin_unlock(&cmd->istate_lock); | ||
759 | } | ||
760 | spin_unlock_bh(&conn->cmd_lock); | ||
761 | } | ||
762 | |||
763 | static int iscsit_allocate_iovecs(struct iscsi_cmd *cmd) | ||
764 | { | ||
765 | u32 iov_count = (cmd->se_cmd.t_data_nents == 0) ? 1 : | ||
766 | cmd->se_cmd.t_data_nents; | ||
767 | |||
768 | iov_count += TRANSPORT_IOV_DATA_BUFFER; | ||
769 | |||
770 | cmd->iov_data = kzalloc(iov_count * sizeof(struct kvec), GFP_KERNEL); | ||
771 | if (!cmd->iov_data) { | ||
772 | pr_err("Unable to allocate cmd->iov_data\n"); | ||
773 | return -ENOMEM; | ||
774 | } | ||
775 | |||
776 | cmd->orig_iov_data_count = iov_count; | ||
777 | return 0; | ||
778 | } | ||
779 | |||
780 | static int iscsit_alloc_buffs(struct iscsi_cmd *cmd) | ||
781 | { | ||
782 | struct scatterlist *sgl; | ||
783 | u32 length = cmd->se_cmd.data_length; | ||
784 | int nents = DIV_ROUND_UP(length, PAGE_SIZE); | ||
785 | int i = 0, ret; | ||
786 | /* | ||
787 | * If no SCSI payload is present, allocate the default iovecs used for | ||
788 | * iSCSI PDU Header | ||
789 | */ | ||
790 | if (!length) | ||
791 | return iscsit_allocate_iovecs(cmd); | ||
792 | |||
793 | sgl = kzalloc(sizeof(*sgl) * nents, GFP_KERNEL); | ||
794 | if (!sgl) | ||
795 | return -ENOMEM; | ||
796 | |||
797 | sg_init_table(sgl, nents); | ||
798 | |||
799 | while (length) { | ||
800 | int buf_size = min_t(int, length, PAGE_SIZE); | ||
801 | struct page *page; | ||
802 | |||
803 | page = alloc_page(GFP_KERNEL | __GFP_ZERO); | ||
804 | if (!page) | ||
805 | goto page_alloc_failed; | ||
806 | |||
807 | sg_set_page(&sgl[i], page, buf_size, 0); | ||
808 | |||
809 | length -= buf_size; | ||
810 | i++; | ||
811 | } | ||
812 | |||
813 | cmd->t_mem_sg = sgl; | ||
814 | cmd->t_mem_sg_nents = nents; | ||
815 | |||
816 | /* BIDI ops not supported */ | ||
817 | |||
818 | /* Tell the core about our preallocated memory */ | ||
819 | transport_generic_map_mem_to_cmd(&cmd->se_cmd, sgl, nents, NULL, 0); | ||
820 | /* | ||
821 | * Allocate iovecs for SCSI payload after transport_generic_map_mem_to_cmd | ||
822 | * so that cmd->se_cmd.t_tasks_se_num has been set. | ||
823 | */ | ||
824 | ret = iscsit_allocate_iovecs(cmd); | ||
825 | if (ret < 0) | ||
826 | goto page_alloc_failed; | ||
827 | |||
828 | return 0; | ||
829 | |||
830 | page_alloc_failed: | ||
831 | while (i >= 0) { | ||
832 | __free_page(sg_page(&sgl[i])); | ||
833 | i--; | ||
834 | } | ||
835 | kfree(cmd->t_mem_sg); | ||
836 | cmd->t_mem_sg = NULL; | ||
837 | return -ENOMEM; | ||
838 | } | ||
839 | |||
840 | static int iscsit_handle_scsi_cmd( | ||
841 | struct iscsi_conn *conn, | ||
842 | unsigned char *buf) | ||
843 | { | ||
844 | int data_direction, cmdsn_ret = 0, immed_ret, ret, transport_ret; | ||
845 | int dump_immediate_data = 0, send_check_condition = 0, payload_length; | ||
846 | struct iscsi_cmd *cmd = NULL; | ||
847 | struct iscsi_scsi_req *hdr; | ||
848 | |||
849 | spin_lock_bh(&conn->sess->session_stats_lock); | ||
850 | conn->sess->cmd_pdus++; | ||
851 | if (conn->sess->se_sess->se_node_acl) { | ||
852 | spin_lock(&conn->sess->se_sess->se_node_acl->stats_lock); | ||
853 | conn->sess->se_sess->se_node_acl->num_cmds++; | ||
854 | spin_unlock(&conn->sess->se_sess->se_node_acl->stats_lock); | ||
855 | } | ||
856 | spin_unlock_bh(&conn->sess->session_stats_lock); | ||
857 | |||
858 | hdr = (struct iscsi_scsi_req *) buf; | ||
859 | payload_length = ntoh24(hdr->dlength); | ||
860 | hdr->itt = be32_to_cpu(hdr->itt); | ||
861 | hdr->data_length = be32_to_cpu(hdr->data_length); | ||
862 | hdr->cmdsn = be32_to_cpu(hdr->cmdsn); | ||
863 | hdr->exp_statsn = be32_to_cpu(hdr->exp_statsn); | ||
864 | |||
865 | /* FIXME; Add checks for AdditionalHeaderSegment */ | ||
866 | |||
867 | if (!(hdr->flags & ISCSI_FLAG_CMD_WRITE) && | ||
868 | !(hdr->flags & ISCSI_FLAG_CMD_FINAL)) { | ||
869 | pr_err("ISCSI_FLAG_CMD_WRITE & ISCSI_FLAG_CMD_FINAL" | ||
870 | " not set. Bad iSCSI Initiator.\n"); | ||
871 | return iscsit_add_reject(ISCSI_REASON_BOOKMARK_INVALID, 1, | ||
872 | buf, conn); | ||
873 | } | ||
874 | |||
875 | if (((hdr->flags & ISCSI_FLAG_CMD_READ) || | ||
876 | (hdr->flags & ISCSI_FLAG_CMD_WRITE)) && !hdr->data_length) { | ||
877 | /* | ||
878 | * Vmware ESX v3.0 uses a modified Cisco Initiator (v3.4.2) | ||
879 | * that adds support for RESERVE/RELEASE. There is a bug | ||
880 | * add with this new functionality that sets R/W bits when | ||
881 | * neither CDB carries any READ or WRITE datapayloads. | ||
882 | */ | ||
883 | if ((hdr->cdb[0] == 0x16) || (hdr->cdb[0] == 0x17)) { | ||
884 | hdr->flags &= ~ISCSI_FLAG_CMD_READ; | ||
885 | hdr->flags &= ~ISCSI_FLAG_CMD_WRITE; | ||
886 | goto done; | ||
887 | } | ||
888 | |||
889 | pr_err("ISCSI_FLAG_CMD_READ or ISCSI_FLAG_CMD_WRITE" | ||
890 | " set when Expected Data Transfer Length is 0 for" | ||
891 | " CDB: 0x%02x. Bad iSCSI Initiator.\n", hdr->cdb[0]); | ||
892 | return iscsit_add_reject(ISCSI_REASON_BOOKMARK_INVALID, 1, | ||
893 | buf, conn); | ||
894 | } | ||
895 | done: | ||
896 | |||
897 | if (!(hdr->flags & ISCSI_FLAG_CMD_READ) && | ||
898 | !(hdr->flags & ISCSI_FLAG_CMD_WRITE) && (hdr->data_length != 0)) { | ||
899 | pr_err("ISCSI_FLAG_CMD_READ and/or ISCSI_FLAG_CMD_WRITE" | ||
900 | " MUST be set if Expected Data Transfer Length is not 0." | ||
901 | " Bad iSCSI Initiator\n"); | ||
902 | return iscsit_add_reject(ISCSI_REASON_BOOKMARK_INVALID, 1, | ||
903 | buf, conn); | ||
904 | } | ||
905 | |||
906 | if ((hdr->flags & ISCSI_FLAG_CMD_READ) && | ||
907 | (hdr->flags & ISCSI_FLAG_CMD_WRITE)) { | ||
908 | pr_err("Bidirectional operations not supported!\n"); | ||
909 | return iscsit_add_reject(ISCSI_REASON_BOOKMARK_INVALID, 1, | ||
910 | buf, conn); | ||
911 | } | ||
912 | |||
913 | if (hdr->opcode & ISCSI_OP_IMMEDIATE) { | ||
914 | pr_err("Illegally set Immediate Bit in iSCSI Initiator" | ||
915 | " Scsi Command PDU.\n"); | ||
916 | return iscsit_add_reject(ISCSI_REASON_BOOKMARK_INVALID, 1, | ||
917 | buf, conn); | ||
918 | } | ||
919 | |||
920 | if (payload_length && !conn->sess->sess_ops->ImmediateData) { | ||
921 | pr_err("ImmediateData=No but DataSegmentLength=%u," | ||
922 | " protocol error.\n", payload_length); | ||
923 | return iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1, | ||
924 | buf, conn); | ||
925 | } | ||
926 | |||
927 | if ((hdr->data_length == payload_length) && | ||
928 | (!(hdr->flags & ISCSI_FLAG_CMD_FINAL))) { | ||
929 | pr_err("Expected Data Transfer Length and Length of" | ||
930 | " Immediate Data are the same, but ISCSI_FLAG_CMD_FINAL" | ||
931 | " bit is not set protocol error\n"); | ||
932 | return iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1, | ||
933 | buf, conn); | ||
934 | } | ||
935 | |||
936 | if (payload_length > hdr->data_length) { | ||
937 | pr_err("DataSegmentLength: %u is greater than" | ||
938 | " EDTL: %u, protocol error.\n", payload_length, | ||
939 | hdr->data_length); | ||
940 | return iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1, | ||
941 | buf, conn); | ||
942 | } | ||
943 | |||
944 | if (payload_length > conn->conn_ops->MaxRecvDataSegmentLength) { | ||
945 | pr_err("DataSegmentLength: %u is greater than" | ||
946 | " MaxRecvDataSegmentLength: %u, protocol error.\n", | ||
947 | payload_length, conn->conn_ops->MaxRecvDataSegmentLength); | ||
948 | return iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1, | ||
949 | buf, conn); | ||
950 | } | ||
951 | |||
952 | if (payload_length > conn->sess->sess_ops->FirstBurstLength) { | ||
953 | pr_err("DataSegmentLength: %u is greater than" | ||
954 | " FirstBurstLength: %u, protocol error.\n", | ||
955 | payload_length, conn->sess->sess_ops->FirstBurstLength); | ||
956 | return iscsit_add_reject(ISCSI_REASON_BOOKMARK_INVALID, 1, | ||
957 | buf, conn); | ||
958 | } | ||
959 | |||
960 | data_direction = (hdr->flags & ISCSI_FLAG_CMD_WRITE) ? DMA_TO_DEVICE : | ||
961 | (hdr->flags & ISCSI_FLAG_CMD_READ) ? DMA_FROM_DEVICE : | ||
962 | DMA_NONE; | ||
963 | |||
964 | cmd = iscsit_allocate_se_cmd(conn, hdr->data_length, data_direction, | ||
965 | (hdr->flags & ISCSI_FLAG_CMD_ATTR_MASK)); | ||
966 | if (!cmd) | ||
967 | return iscsit_add_reject(ISCSI_REASON_BOOKMARK_NO_RESOURCES, 1, | ||
968 | buf, conn); | ||
969 | |||
970 | pr_debug("Got SCSI Command, ITT: 0x%08x, CmdSN: 0x%08x," | ||
971 | " ExpXferLen: %u, Length: %u, CID: %hu\n", hdr->itt, | ||
972 | hdr->cmdsn, hdr->data_length, payload_length, conn->cid); | ||
973 | |||
974 | cmd->iscsi_opcode = ISCSI_OP_SCSI_CMD; | ||
975 | cmd->i_state = ISTATE_NEW_CMD; | ||
976 | cmd->immediate_cmd = ((hdr->opcode & ISCSI_OP_IMMEDIATE) ? 1 : 0); | ||
977 | cmd->immediate_data = (payload_length) ? 1 : 0; | ||
978 | cmd->unsolicited_data = ((!(hdr->flags & ISCSI_FLAG_CMD_FINAL) && | ||
979 | (hdr->flags & ISCSI_FLAG_CMD_WRITE)) ? 1 : 0); | ||
980 | if (cmd->unsolicited_data) | ||
981 | cmd->cmd_flags |= ICF_NON_IMMEDIATE_UNSOLICITED_DATA; | ||
982 | |||
983 | conn->sess->init_task_tag = cmd->init_task_tag = hdr->itt; | ||
984 | if (hdr->flags & ISCSI_FLAG_CMD_READ) { | ||
985 | spin_lock_bh(&conn->sess->ttt_lock); | ||
986 | cmd->targ_xfer_tag = conn->sess->targ_xfer_tag++; | ||
987 | if (cmd->targ_xfer_tag == 0xFFFFFFFF) | ||
988 | cmd->targ_xfer_tag = conn->sess->targ_xfer_tag++; | ||
989 | spin_unlock_bh(&conn->sess->ttt_lock); | ||
990 | } else if (hdr->flags & ISCSI_FLAG_CMD_WRITE) | ||
991 | cmd->targ_xfer_tag = 0xFFFFFFFF; | ||
992 | cmd->cmd_sn = hdr->cmdsn; | ||
993 | cmd->exp_stat_sn = hdr->exp_statsn; | ||
994 | cmd->first_burst_len = payload_length; | ||
995 | |||
996 | if (cmd->data_direction == DMA_FROM_DEVICE) { | ||
997 | struct iscsi_datain_req *dr; | ||
998 | |||
999 | dr = iscsit_allocate_datain_req(); | ||
1000 | if (!dr) | ||
1001 | return iscsit_add_reject_from_cmd( | ||
1002 | ISCSI_REASON_BOOKMARK_NO_RESOURCES, | ||
1003 | 1, 1, buf, cmd); | ||
1004 | |||
1005 | iscsit_attach_datain_req(cmd, dr); | ||
1006 | } | ||
1007 | |||
1008 | /* | ||
1009 | * The CDB is going to an se_device_t. | ||
1010 | */ | ||
1011 | ret = iscsit_get_lun_for_cmd(cmd, hdr->cdb, | ||
1012 | get_unaligned_le64(&hdr->lun)); | ||
1013 | if (ret < 0) { | ||
1014 | if (cmd->se_cmd.scsi_sense_reason == TCM_NON_EXISTENT_LUN) { | ||
1015 | pr_debug("Responding to non-acl'ed," | ||
1016 | " non-existent or non-exported iSCSI LUN:" | ||
1017 | " 0x%016Lx\n", get_unaligned_le64(&hdr->lun)); | ||
1018 | } | ||
1019 | if (ret == PYX_TRANSPORT_OUT_OF_MEMORY_RESOURCES) | ||
1020 | return iscsit_add_reject_from_cmd( | ||
1021 | ISCSI_REASON_BOOKMARK_NO_RESOURCES, | ||
1022 | 1, 1, buf, cmd); | ||
1023 | |||
1024 | send_check_condition = 1; | ||
1025 | goto attach_cmd; | ||
1026 | } | ||
1027 | /* | ||
1028 | * The Initiator Node has access to the LUN (the addressing method | ||
1029 | * is handled inside of iscsit_get_lun_for_cmd()). Now it's time to | ||
1030 | * allocate 1->N transport tasks (depending on sector count and | ||
1031 | * maximum request size the physical HBA(s) can handle. | ||
1032 | */ | ||
1033 | transport_ret = transport_generic_allocate_tasks(&cmd->se_cmd, hdr->cdb); | ||
1034 | if (transport_ret == -ENOMEM) { | ||
1035 | return iscsit_add_reject_from_cmd( | ||
1036 | ISCSI_REASON_BOOKMARK_NO_RESOURCES, | ||
1037 | 1, 1, buf, cmd); | ||
1038 | } else if (transport_ret == -EINVAL) { | ||
1039 | /* | ||
1040 | * Unsupported SAM Opcode. CHECK_CONDITION will be sent | ||
1041 | * in iscsit_execute_cmd() during the CmdSN OOO Execution | ||
1042 | * Mechinism. | ||
1043 | */ | ||
1044 | send_check_condition = 1; | ||
1045 | } else { | ||
1046 | if (iscsit_decide_list_to_build(cmd, payload_length) < 0) | ||
1047 | return iscsit_add_reject_from_cmd( | ||
1048 | ISCSI_REASON_BOOKMARK_NO_RESOURCES, | ||
1049 | 1, 1, buf, cmd); | ||
1050 | } | ||
1051 | |||
1052 | attach_cmd: | ||
1053 | spin_lock_bh(&conn->cmd_lock); | ||
1054 | list_add_tail(&cmd->i_list, &conn->conn_cmd_list); | ||
1055 | spin_unlock_bh(&conn->cmd_lock); | ||
1056 | /* | ||
1057 | * Check if we need to delay processing because of ALUA | ||
1058 | * Active/NonOptimized primary access state.. | ||
1059 | */ | ||
1060 | core_alua_check_nonop_delay(&cmd->se_cmd); | ||
1061 | /* | ||
1062 | * Allocate and setup SGL used with transport_generic_map_mem_to_cmd(). | ||
1063 | * also call iscsit_allocate_iovecs() | ||
1064 | */ | ||
1065 | ret = iscsit_alloc_buffs(cmd); | ||
1066 | if (ret < 0) | ||
1067 | return iscsit_add_reject_from_cmd( | ||
1068 | ISCSI_REASON_BOOKMARK_NO_RESOURCES, | ||
1069 | 1, 1, buf, cmd); | ||
1070 | /* | ||
1071 | * Check the CmdSN against ExpCmdSN/MaxCmdSN here if | ||
1072 | * the Immediate Bit is not set, and no Immediate | ||
1073 | * Data is attached. | ||
1074 | * | ||
1075 | * A PDU/CmdSN carrying Immediate Data can only | ||
1076 | * be processed after the DataCRC has passed. | ||
1077 | * If the DataCRC fails, the CmdSN MUST NOT | ||
1078 | * be acknowledged. (See below) | ||
1079 | */ | ||
1080 | if (!cmd->immediate_data) { | ||
1081 | cmdsn_ret = iscsit_sequence_cmd(conn, cmd, hdr->cmdsn); | ||
1082 | if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER) | ||
1083 | return iscsit_add_reject_from_cmd( | ||
1084 | ISCSI_REASON_PROTOCOL_ERROR, | ||
1085 | 1, 0, buf, cmd); | ||
1086 | } | ||
1087 | |||
1088 | iscsit_ack_from_expstatsn(conn, hdr->exp_statsn); | ||
1089 | |||
1090 | /* | ||
1091 | * If no Immediate Data is attached, it's OK to return now. | ||
1092 | */ | ||
1093 | if (!cmd->immediate_data) { | ||
1094 | if (send_check_condition) | ||
1095 | return 0; | ||
1096 | |||
1097 | if (cmd->unsolicited_data) { | ||
1098 | iscsit_set_dataout_sequence_values(cmd); | ||
1099 | |||
1100 | spin_lock_bh(&cmd->dataout_timeout_lock); | ||
1101 | iscsit_start_dataout_timer(cmd, cmd->conn); | ||
1102 | spin_unlock_bh(&cmd->dataout_timeout_lock); | ||
1103 | } | ||
1104 | |||
1105 | return 0; | ||
1106 | } | ||
1107 | |||
1108 | /* | ||
1109 | * Early CHECK_CONDITIONs never make it to the transport processing | ||
1110 | * thread. They are processed in CmdSN order by | ||
1111 | * iscsit_check_received_cmdsn() below. | ||
1112 | */ | ||
1113 | if (send_check_condition) { | ||
1114 | immed_ret = IMMEDIATE_DATA_NORMAL_OPERATION; | ||
1115 | dump_immediate_data = 1; | ||
1116 | goto after_immediate_data; | ||
1117 | } | ||
1118 | /* | ||
1119 | * Call directly into transport_generic_new_cmd() to perform | ||
1120 | * the backend memory allocation. | ||
1121 | */ | ||
1122 | ret = transport_generic_new_cmd(&cmd->se_cmd); | ||
1123 | if ((ret < 0) || (cmd->se_cmd.se_cmd_flags & SCF_SE_CMD_FAILED)) { | ||
1124 | immed_ret = IMMEDIATE_DATA_NORMAL_OPERATION; | ||
1125 | dump_immediate_data = 1; | ||
1126 | goto after_immediate_data; | ||
1127 | } | ||
1128 | |||
1129 | immed_ret = iscsit_handle_immediate_data(cmd, buf, payload_length); | ||
1130 | after_immediate_data: | ||
1131 | if (immed_ret == IMMEDIATE_DATA_NORMAL_OPERATION) { | ||
1132 | /* | ||
1133 | * A PDU/CmdSN carrying Immediate Data passed | ||
1134 | * DataCRC, check against ExpCmdSN/MaxCmdSN if | ||
1135 | * Immediate Bit is not set. | ||
1136 | */ | ||
1137 | cmdsn_ret = iscsit_sequence_cmd(conn, cmd, hdr->cmdsn); | ||
1138 | /* | ||
1139 | * Special case for Unsupported SAM WRITE Opcodes | ||
1140 | * and ImmediateData=Yes. | ||
1141 | */ | ||
1142 | if (dump_immediate_data) { | ||
1143 | if (iscsit_dump_data_payload(conn, payload_length, 1) < 0) | ||
1144 | return -1; | ||
1145 | } else if (cmd->unsolicited_data) { | ||
1146 | iscsit_set_dataout_sequence_values(cmd); | ||
1147 | |||
1148 | spin_lock_bh(&cmd->dataout_timeout_lock); | ||
1149 | iscsit_start_dataout_timer(cmd, cmd->conn); | ||
1150 | spin_unlock_bh(&cmd->dataout_timeout_lock); | ||
1151 | } | ||
1152 | |||
1153 | if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER) | ||
1154 | return iscsit_add_reject_from_cmd( | ||
1155 | ISCSI_REASON_PROTOCOL_ERROR, | ||
1156 | 1, 0, buf, cmd); | ||
1157 | |||
1158 | } else if (immed_ret == IMMEDIATE_DATA_ERL1_CRC_FAILURE) { | ||
1159 | /* | ||
1160 | * Immediate Data failed DataCRC and ERL>=1, | ||
1161 | * silently drop this PDU and let the initiator | ||
1162 | * plug the CmdSN gap. | ||
1163 | * | ||
1164 | * FIXME: Send Unsolicited NOPIN with reserved | ||
1165 | * TTT here to help the initiator figure out | ||
1166 | * the missing CmdSN, although they should be | ||
1167 | * intelligent enough to determine the missing | ||
1168 | * CmdSN and issue a retry to plug the sequence. | ||
1169 | */ | ||
1170 | cmd->i_state = ISTATE_REMOVE; | ||
1171 | iscsit_add_cmd_to_immediate_queue(cmd, conn, cmd->i_state); | ||
1172 | } else /* immed_ret == IMMEDIATE_DATA_CANNOT_RECOVER */ | ||
1173 | return -1; | ||
1174 | |||
1175 | return 0; | ||
1176 | } | ||
1177 | |||
1178 | static u32 iscsit_do_crypto_hash_sg( | ||
1179 | struct hash_desc *hash, | ||
1180 | struct iscsi_cmd *cmd, | ||
1181 | u32 data_offset, | ||
1182 | u32 data_length, | ||
1183 | u32 padding, | ||
1184 | u8 *pad_bytes) | ||
1185 | { | ||
1186 | u32 data_crc; | ||
1187 | u32 i; | ||
1188 | struct scatterlist *sg; | ||
1189 | unsigned int page_off; | ||
1190 | |||
1191 | crypto_hash_init(hash); | ||
1192 | |||
1193 | sg = cmd->first_data_sg; | ||
1194 | page_off = cmd->first_data_sg_off; | ||
1195 | |||
1196 | i = 0; | ||
1197 | while (data_length) { | ||
1198 | u32 cur_len = min_t(u32, data_length, (sg[i].length - page_off)); | ||
1199 | |||
1200 | crypto_hash_update(hash, &sg[i], cur_len); | ||
1201 | |||
1202 | data_length -= cur_len; | ||
1203 | page_off = 0; | ||
1204 | i++; | ||
1205 | } | ||
1206 | |||
1207 | if (padding) { | ||
1208 | struct scatterlist pad_sg; | ||
1209 | |||
1210 | sg_init_one(&pad_sg, pad_bytes, padding); | ||
1211 | crypto_hash_update(hash, &pad_sg, padding); | ||
1212 | } | ||
1213 | crypto_hash_final(hash, (u8 *) &data_crc); | ||
1214 | |||
1215 | return data_crc; | ||
1216 | } | ||
1217 | |||
1218 | static void iscsit_do_crypto_hash_buf( | ||
1219 | struct hash_desc *hash, | ||
1220 | unsigned char *buf, | ||
1221 | u32 payload_length, | ||
1222 | u32 padding, | ||
1223 | u8 *pad_bytes, | ||
1224 | u8 *data_crc) | ||
1225 | { | ||
1226 | struct scatterlist sg; | ||
1227 | |||
1228 | crypto_hash_init(hash); | ||
1229 | |||
1230 | sg_init_one(&sg, (u8 *)buf, payload_length); | ||
1231 | crypto_hash_update(hash, &sg, payload_length); | ||
1232 | |||
1233 | if (padding) { | ||
1234 | sg_init_one(&sg, pad_bytes, padding); | ||
1235 | crypto_hash_update(hash, &sg, padding); | ||
1236 | } | ||
1237 | crypto_hash_final(hash, data_crc); | ||
1238 | } | ||
1239 | |||
1240 | static int iscsit_handle_data_out(struct iscsi_conn *conn, unsigned char *buf) | ||
1241 | { | ||
1242 | int iov_ret, ooo_cmdsn = 0, ret; | ||
1243 | u8 data_crc_failed = 0; | ||
1244 | u32 checksum, iov_count = 0, padding = 0, rx_got = 0; | ||
1245 | u32 rx_size = 0, payload_length; | ||
1246 | struct iscsi_cmd *cmd = NULL; | ||
1247 | struct se_cmd *se_cmd; | ||
1248 | struct iscsi_data *hdr; | ||
1249 | struct kvec *iov; | ||
1250 | unsigned long flags; | ||
1251 | |||
1252 | hdr = (struct iscsi_data *) buf; | ||
1253 | payload_length = ntoh24(hdr->dlength); | ||
1254 | hdr->itt = be32_to_cpu(hdr->itt); | ||
1255 | hdr->ttt = be32_to_cpu(hdr->ttt); | ||
1256 | hdr->exp_statsn = be32_to_cpu(hdr->exp_statsn); | ||
1257 | hdr->datasn = be32_to_cpu(hdr->datasn); | ||
1258 | hdr->offset = be32_to_cpu(hdr->offset); | ||
1259 | |||
1260 | if (!payload_length) { | ||
1261 | pr_err("DataOUT payload is ZERO, protocol error.\n"); | ||
1262 | return iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1, | ||
1263 | buf, conn); | ||
1264 | } | ||
1265 | |||
1266 | /* iSCSI write */ | ||
1267 | spin_lock_bh(&conn->sess->session_stats_lock); | ||
1268 | conn->sess->rx_data_octets += payload_length; | ||
1269 | if (conn->sess->se_sess->se_node_acl) { | ||
1270 | spin_lock(&conn->sess->se_sess->se_node_acl->stats_lock); | ||
1271 | conn->sess->se_sess->se_node_acl->write_bytes += payload_length; | ||
1272 | spin_unlock(&conn->sess->se_sess->se_node_acl->stats_lock); | ||
1273 | } | ||
1274 | spin_unlock_bh(&conn->sess->session_stats_lock); | ||
1275 | |||
1276 | if (payload_length > conn->conn_ops->MaxRecvDataSegmentLength) { | ||
1277 | pr_err("DataSegmentLength: %u is greater than" | ||
1278 | " MaxRecvDataSegmentLength: %u\n", payload_length, | ||
1279 | conn->conn_ops->MaxRecvDataSegmentLength); | ||
1280 | return iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1, | ||
1281 | buf, conn); | ||
1282 | } | ||
1283 | |||
1284 | cmd = iscsit_find_cmd_from_itt_or_dump(conn, hdr->itt, | ||
1285 | payload_length); | ||
1286 | if (!cmd) | ||
1287 | return 0; | ||
1288 | |||
1289 | pr_debug("Got DataOut ITT: 0x%08x, TTT: 0x%08x," | ||
1290 | " DataSN: 0x%08x, Offset: %u, Length: %u, CID: %hu\n", | ||
1291 | hdr->itt, hdr->ttt, hdr->datasn, hdr->offset, | ||
1292 | payload_length, conn->cid); | ||
1293 | |||
1294 | if (cmd->cmd_flags & ICF_GOT_LAST_DATAOUT) { | ||
1295 | pr_err("Command ITT: 0x%08x received DataOUT after" | ||
1296 | " last DataOUT received, dumping payload\n", | ||
1297 | cmd->init_task_tag); | ||
1298 | return iscsit_dump_data_payload(conn, payload_length, 1); | ||
1299 | } | ||
1300 | |||
1301 | if (cmd->data_direction != DMA_TO_DEVICE) { | ||
1302 | pr_err("Command ITT: 0x%08x received DataOUT for a" | ||
1303 | " NON-WRITE command.\n", cmd->init_task_tag); | ||
1304 | return iscsit_add_reject_from_cmd(ISCSI_REASON_PROTOCOL_ERROR, | ||
1305 | 1, 0, buf, cmd); | ||
1306 | } | ||
1307 | se_cmd = &cmd->se_cmd; | ||
1308 | iscsit_mod_dataout_timer(cmd); | ||
1309 | |||
1310 | if ((hdr->offset + payload_length) > cmd->data_length) { | ||
1311 | pr_err("DataOut Offset: %u, Length %u greater than" | ||
1312 | " iSCSI Command EDTL %u, protocol error.\n", | ||
1313 | hdr->offset, payload_length, cmd->data_length); | ||
1314 | return iscsit_add_reject_from_cmd(ISCSI_REASON_BOOKMARK_INVALID, | ||
1315 | 1, 0, buf, cmd); | ||
1316 | } | ||
1317 | |||
1318 | if (cmd->unsolicited_data) { | ||
1319 | int dump_unsolicited_data = 0; | ||
1320 | |||
1321 | if (conn->sess->sess_ops->InitialR2T) { | ||
1322 | pr_err("Received unexpected unsolicited data" | ||
1323 | " while InitialR2T=Yes, protocol error.\n"); | ||
1324 | transport_send_check_condition_and_sense(&cmd->se_cmd, | ||
1325 | TCM_UNEXPECTED_UNSOLICITED_DATA, 0); | ||
1326 | return -1; | ||
1327 | } | ||
1328 | /* | ||
1329 | * Special case for dealing with Unsolicited DataOUT | ||
1330 | * and Unsupported SAM WRITE Opcodes and SE resource allocation | ||
1331 | * failures; | ||
1332 | */ | ||
1333 | |||
1334 | /* Something's amiss if we're not in WRITE_PENDING state... */ | ||
1335 | spin_lock_irqsave(&se_cmd->t_state_lock, flags); | ||
1336 | WARN_ON(se_cmd->t_state != TRANSPORT_WRITE_PENDING); | ||
1337 | spin_unlock_irqrestore(&se_cmd->t_state_lock, flags); | ||
1338 | |||
1339 | spin_lock_irqsave(&se_cmd->t_state_lock, flags); | ||
1340 | if (!(se_cmd->se_cmd_flags & SCF_SUPPORTED_SAM_OPCODE) || | ||
1341 | (se_cmd->se_cmd_flags & SCF_SE_CMD_FAILED)) | ||
1342 | dump_unsolicited_data = 1; | ||
1343 | spin_unlock_irqrestore(&se_cmd->t_state_lock, flags); | ||
1344 | |||
1345 | if (dump_unsolicited_data) { | ||
1346 | /* | ||
1347 | * Check if a delayed TASK_ABORTED status needs to | ||
1348 | * be sent now if the ISCSI_FLAG_CMD_FINAL has been | ||
1349 | * received with the unsolicitied data out. | ||
1350 | */ | ||
1351 | if (hdr->flags & ISCSI_FLAG_CMD_FINAL) | ||
1352 | iscsit_stop_dataout_timer(cmd); | ||
1353 | |||
1354 | transport_check_aborted_status(se_cmd, | ||
1355 | (hdr->flags & ISCSI_FLAG_CMD_FINAL)); | ||
1356 | return iscsit_dump_data_payload(conn, payload_length, 1); | ||
1357 | } | ||
1358 | } else { | ||
1359 | /* | ||
1360 | * For the normal solicited data path: | ||
1361 | * | ||
1362 | * Check for a delayed TASK_ABORTED status and dump any | ||
1363 | * incoming data out payload if one exists. Also, when the | ||
1364 | * ISCSI_FLAG_CMD_FINAL is set to denote the end of the current | ||
1365 | * data out sequence, we decrement outstanding_r2ts. Once | ||
1366 | * outstanding_r2ts reaches zero, go ahead and send the delayed | ||
1367 | * TASK_ABORTED status. | ||
1368 | */ | ||
1369 | if (atomic_read(&se_cmd->t_transport_aborted) != 0) { | ||
1370 | if (hdr->flags & ISCSI_FLAG_CMD_FINAL) | ||
1371 | if (--cmd->outstanding_r2ts < 1) { | ||
1372 | iscsit_stop_dataout_timer(cmd); | ||
1373 | transport_check_aborted_status( | ||
1374 | se_cmd, 1); | ||
1375 | } | ||
1376 | |||
1377 | return iscsit_dump_data_payload(conn, payload_length, 1); | ||
1378 | } | ||
1379 | } | ||
1380 | /* | ||
1381 | * Preform DataSN, DataSequenceInOrder, DataPDUInOrder, and | ||
1382 | * within-command recovery checks before receiving the payload. | ||
1383 | */ | ||
1384 | ret = iscsit_check_pre_dataout(cmd, buf); | ||
1385 | if (ret == DATAOUT_WITHIN_COMMAND_RECOVERY) | ||
1386 | return 0; | ||
1387 | else if (ret == DATAOUT_CANNOT_RECOVER) | ||
1388 | return -1; | ||
1389 | |||
1390 | rx_size += payload_length; | ||
1391 | iov = &cmd->iov_data[0]; | ||
1392 | |||
1393 | iov_ret = iscsit_map_iovec(cmd, iov, hdr->offset, payload_length); | ||
1394 | if (iov_ret < 0) | ||
1395 | return -1; | ||
1396 | |||
1397 | iov_count += iov_ret; | ||
1398 | |||
1399 | padding = ((-payload_length) & 3); | ||
1400 | if (padding != 0) { | ||
1401 | iov[iov_count].iov_base = cmd->pad_bytes; | ||
1402 | iov[iov_count++].iov_len = padding; | ||
1403 | rx_size += padding; | ||
1404 | pr_debug("Receiving %u padding bytes.\n", padding); | ||
1405 | } | ||
1406 | |||
1407 | if (conn->conn_ops->DataDigest) { | ||
1408 | iov[iov_count].iov_base = &checksum; | ||
1409 | iov[iov_count++].iov_len = ISCSI_CRC_LEN; | ||
1410 | rx_size += ISCSI_CRC_LEN; | ||
1411 | } | ||
1412 | |||
1413 | rx_got = rx_data(conn, &cmd->iov_data[0], iov_count, rx_size); | ||
1414 | |||
1415 | iscsit_unmap_iovec(cmd); | ||
1416 | |||
1417 | if (rx_got != rx_size) | ||
1418 | return -1; | ||
1419 | |||
1420 | if (conn->conn_ops->DataDigest) { | ||
1421 | u32 data_crc; | ||
1422 | |||
1423 | data_crc = iscsit_do_crypto_hash_sg(&conn->conn_rx_hash, cmd, | ||
1424 | hdr->offset, payload_length, padding, | ||
1425 | cmd->pad_bytes); | ||
1426 | |||
1427 | if (checksum != data_crc) { | ||
1428 | pr_err("ITT: 0x%08x, Offset: %u, Length: %u," | ||
1429 | " DataSN: 0x%08x, CRC32C DataDigest 0x%08x" | ||
1430 | " does not match computed 0x%08x\n", | ||
1431 | hdr->itt, hdr->offset, payload_length, | ||
1432 | hdr->datasn, checksum, data_crc); | ||
1433 | data_crc_failed = 1; | ||
1434 | } else { | ||
1435 | pr_debug("Got CRC32C DataDigest 0x%08x for" | ||
1436 | " %u bytes of Data Out\n", checksum, | ||
1437 | payload_length); | ||
1438 | } | ||
1439 | } | ||
1440 | /* | ||
1441 | * Increment post receive data and CRC values or perform | ||
1442 | * within-command recovery. | ||
1443 | */ | ||
1444 | ret = iscsit_check_post_dataout(cmd, buf, data_crc_failed); | ||
1445 | if ((ret == DATAOUT_NORMAL) || (ret == DATAOUT_WITHIN_COMMAND_RECOVERY)) | ||
1446 | return 0; | ||
1447 | else if (ret == DATAOUT_SEND_R2T) { | ||
1448 | iscsit_set_dataout_sequence_values(cmd); | ||
1449 | iscsit_build_r2ts_for_cmd(cmd, conn, 0); | ||
1450 | } else if (ret == DATAOUT_SEND_TO_TRANSPORT) { | ||
1451 | /* | ||
1452 | * Handle extra special case for out of order | ||
1453 | * Unsolicited Data Out. | ||
1454 | */ | ||
1455 | spin_lock_bh(&cmd->istate_lock); | ||
1456 | ooo_cmdsn = (cmd->cmd_flags & ICF_OOO_CMDSN); | ||
1457 | cmd->cmd_flags |= ICF_GOT_LAST_DATAOUT; | ||
1458 | cmd->i_state = ISTATE_RECEIVED_LAST_DATAOUT; | ||
1459 | spin_unlock_bh(&cmd->istate_lock); | ||
1460 | |||
1461 | iscsit_stop_dataout_timer(cmd); | ||
1462 | return (!ooo_cmdsn) ? transport_generic_handle_data( | ||
1463 | &cmd->se_cmd) : 0; | ||
1464 | } else /* DATAOUT_CANNOT_RECOVER */ | ||
1465 | return -1; | ||
1466 | |||
1467 | return 0; | ||
1468 | } | ||
1469 | |||
1470 | static int iscsit_handle_nop_out( | ||
1471 | struct iscsi_conn *conn, | ||
1472 | unsigned char *buf) | ||
1473 | { | ||
1474 | unsigned char *ping_data = NULL; | ||
1475 | int cmdsn_ret, niov = 0, ret = 0, rx_got, rx_size; | ||
1476 | u32 checksum, data_crc, padding = 0, payload_length; | ||
1477 | u64 lun; | ||
1478 | struct iscsi_cmd *cmd = NULL; | ||
1479 | struct kvec *iov = NULL; | ||
1480 | struct iscsi_nopout *hdr; | ||
1481 | |||
1482 | hdr = (struct iscsi_nopout *) buf; | ||
1483 | payload_length = ntoh24(hdr->dlength); | ||
1484 | lun = get_unaligned_le64(&hdr->lun); | ||
1485 | hdr->itt = be32_to_cpu(hdr->itt); | ||
1486 | hdr->ttt = be32_to_cpu(hdr->ttt); | ||
1487 | hdr->cmdsn = be32_to_cpu(hdr->cmdsn); | ||
1488 | hdr->exp_statsn = be32_to_cpu(hdr->exp_statsn); | ||
1489 | |||
1490 | if ((hdr->itt == 0xFFFFFFFF) && !(hdr->opcode & ISCSI_OP_IMMEDIATE)) { | ||
1491 | pr_err("NOPOUT ITT is reserved, but Immediate Bit is" | ||
1492 | " not set, protocol error.\n"); | ||
1493 | return iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1, | ||
1494 | buf, conn); | ||
1495 | } | ||
1496 | |||
1497 | if (payload_length > conn->conn_ops->MaxRecvDataSegmentLength) { | ||
1498 | pr_err("NOPOUT Ping Data DataSegmentLength: %u is" | ||
1499 | " greater than MaxRecvDataSegmentLength: %u, protocol" | ||
1500 | " error.\n", payload_length, | ||
1501 | conn->conn_ops->MaxRecvDataSegmentLength); | ||
1502 | return iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1, | ||
1503 | buf, conn); | ||
1504 | } | ||
1505 | |||
1506 | pr_debug("Got NOPOUT Ping %s ITT: 0x%08x, TTT: 0x%09x," | ||
1507 | " CmdSN: 0x%08x, ExpStatSN: 0x%08x, Length: %u\n", | ||
1508 | (hdr->itt == 0xFFFFFFFF) ? "Response" : "Request", | ||
1509 | hdr->itt, hdr->ttt, hdr->cmdsn, hdr->exp_statsn, | ||
1510 | payload_length); | ||
1511 | /* | ||
1512 | * This is not a response to a Unsolicited NopIN, which means | ||
1513 | * it can either be a NOPOUT ping request (with a valid ITT), | ||
1514 | * or a NOPOUT not requesting a NOPIN (with a reserved ITT). | ||
1515 | * Either way, make sure we allocate an struct iscsi_cmd, as both | ||
1516 | * can contain ping data. | ||
1517 | */ | ||
1518 | if (hdr->ttt == 0xFFFFFFFF) { | ||
1519 | cmd = iscsit_allocate_cmd(conn, GFP_KERNEL); | ||
1520 | if (!cmd) | ||
1521 | return iscsit_add_reject( | ||
1522 | ISCSI_REASON_BOOKMARK_NO_RESOURCES, | ||
1523 | 1, buf, conn); | ||
1524 | |||
1525 | cmd->iscsi_opcode = ISCSI_OP_NOOP_OUT; | ||
1526 | cmd->i_state = ISTATE_SEND_NOPIN; | ||
1527 | cmd->immediate_cmd = ((hdr->opcode & ISCSI_OP_IMMEDIATE) ? | ||
1528 | 1 : 0); | ||
1529 | conn->sess->init_task_tag = cmd->init_task_tag = hdr->itt; | ||
1530 | cmd->targ_xfer_tag = 0xFFFFFFFF; | ||
1531 | cmd->cmd_sn = hdr->cmdsn; | ||
1532 | cmd->exp_stat_sn = hdr->exp_statsn; | ||
1533 | cmd->data_direction = DMA_NONE; | ||
1534 | } | ||
1535 | |||
1536 | if (payload_length && (hdr->ttt == 0xFFFFFFFF)) { | ||
1537 | rx_size = payload_length; | ||
1538 | ping_data = kzalloc(payload_length + 1, GFP_KERNEL); | ||
1539 | if (!ping_data) { | ||
1540 | pr_err("Unable to allocate memory for" | ||
1541 | " NOPOUT ping data.\n"); | ||
1542 | ret = -1; | ||
1543 | goto out; | ||
1544 | } | ||
1545 | |||
1546 | iov = &cmd->iov_misc[0]; | ||
1547 | iov[niov].iov_base = ping_data; | ||
1548 | iov[niov++].iov_len = payload_length; | ||
1549 | |||
1550 | padding = ((-payload_length) & 3); | ||
1551 | if (padding != 0) { | ||
1552 | pr_debug("Receiving %u additional bytes" | ||
1553 | " for padding.\n", padding); | ||
1554 | iov[niov].iov_base = &cmd->pad_bytes; | ||
1555 | iov[niov++].iov_len = padding; | ||
1556 | rx_size += padding; | ||
1557 | } | ||
1558 | if (conn->conn_ops->DataDigest) { | ||
1559 | iov[niov].iov_base = &checksum; | ||
1560 | iov[niov++].iov_len = ISCSI_CRC_LEN; | ||
1561 | rx_size += ISCSI_CRC_LEN; | ||
1562 | } | ||
1563 | |||
1564 | rx_got = rx_data(conn, &cmd->iov_misc[0], niov, rx_size); | ||
1565 | if (rx_got != rx_size) { | ||
1566 | ret = -1; | ||
1567 | goto out; | ||
1568 | } | ||
1569 | |||
1570 | if (conn->conn_ops->DataDigest) { | ||
1571 | iscsit_do_crypto_hash_buf(&conn->conn_rx_hash, | ||
1572 | ping_data, payload_length, | ||
1573 | padding, cmd->pad_bytes, | ||
1574 | (u8 *)&data_crc); | ||
1575 | |||
1576 | if (checksum != data_crc) { | ||
1577 | pr_err("Ping data CRC32C DataDigest" | ||
1578 | " 0x%08x does not match computed 0x%08x\n", | ||
1579 | checksum, data_crc); | ||
1580 | if (!conn->sess->sess_ops->ErrorRecoveryLevel) { | ||
1581 | pr_err("Unable to recover from" | ||
1582 | " NOPOUT Ping DataCRC failure while in" | ||
1583 | " ERL=0.\n"); | ||
1584 | ret = -1; | ||
1585 | goto out; | ||
1586 | } else { | ||
1587 | /* | ||
1588 | * Silently drop this PDU and let the | ||
1589 | * initiator plug the CmdSN gap. | ||
1590 | */ | ||
1591 | pr_debug("Dropping NOPOUT" | ||
1592 | " Command CmdSN: 0x%08x due to" | ||
1593 | " DataCRC error.\n", hdr->cmdsn); | ||
1594 | ret = 0; | ||
1595 | goto out; | ||
1596 | } | ||
1597 | } else { | ||
1598 | pr_debug("Got CRC32C DataDigest" | ||
1599 | " 0x%08x for %u bytes of ping data.\n", | ||
1600 | checksum, payload_length); | ||
1601 | } | ||
1602 | } | ||
1603 | |||
1604 | ping_data[payload_length] = '\0'; | ||
1605 | /* | ||
1606 | * Attach ping data to struct iscsi_cmd->buf_ptr. | ||
1607 | */ | ||
1608 | cmd->buf_ptr = (void *)ping_data; | ||
1609 | cmd->buf_ptr_size = payload_length; | ||
1610 | |||
1611 | pr_debug("Got %u bytes of NOPOUT ping" | ||
1612 | " data.\n", payload_length); | ||
1613 | pr_debug("Ping Data: \"%s\"\n", ping_data); | ||
1614 | } | ||
1615 | |||
1616 | if (hdr->itt != 0xFFFFFFFF) { | ||
1617 | if (!cmd) { | ||
1618 | pr_err("Checking CmdSN for NOPOUT," | ||
1619 | " but cmd is NULL!\n"); | ||
1620 | return -1; | ||
1621 | } | ||
1622 | /* | ||
1623 | * Initiator is expecting a NopIN ping reply, | ||
1624 | */ | ||
1625 | spin_lock_bh(&conn->cmd_lock); | ||
1626 | list_add_tail(&cmd->i_list, &conn->conn_cmd_list); | ||
1627 | spin_unlock_bh(&conn->cmd_lock); | ||
1628 | |||
1629 | iscsit_ack_from_expstatsn(conn, hdr->exp_statsn); | ||
1630 | |||
1631 | if (hdr->opcode & ISCSI_OP_IMMEDIATE) { | ||
1632 | iscsit_add_cmd_to_response_queue(cmd, conn, | ||
1633 | cmd->i_state); | ||
1634 | return 0; | ||
1635 | } | ||
1636 | |||
1637 | cmdsn_ret = iscsit_sequence_cmd(conn, cmd, hdr->cmdsn); | ||
1638 | if (cmdsn_ret == CMDSN_LOWER_THAN_EXP) { | ||
1639 | ret = 0; | ||
1640 | goto ping_out; | ||
1641 | } | ||
1642 | if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER) | ||
1643 | return iscsit_add_reject_from_cmd( | ||
1644 | ISCSI_REASON_PROTOCOL_ERROR, | ||
1645 | 1, 0, buf, cmd); | ||
1646 | |||
1647 | return 0; | ||
1648 | } | ||
1649 | |||
1650 | if (hdr->ttt != 0xFFFFFFFF) { | ||
1651 | /* | ||
1652 | * This was a response to a unsolicited NOPIN ping. | ||
1653 | */ | ||
1654 | cmd = iscsit_find_cmd_from_ttt(conn, hdr->ttt); | ||
1655 | if (!cmd) | ||
1656 | return -1; | ||
1657 | |||
1658 | iscsit_stop_nopin_response_timer(conn); | ||
1659 | |||
1660 | cmd->i_state = ISTATE_REMOVE; | ||
1661 | iscsit_add_cmd_to_immediate_queue(cmd, conn, cmd->i_state); | ||
1662 | iscsit_start_nopin_timer(conn); | ||
1663 | } else { | ||
1664 | /* | ||
1665 | * Initiator is not expecting a NOPIN is response. | ||
1666 | * Just ignore for now. | ||
1667 | * | ||
1668 | * iSCSI v19-91 10.18 | ||
1669 | * "A NOP-OUT may also be used to confirm a changed | ||
1670 | * ExpStatSN if another PDU will not be available | ||
1671 | * for a long time." | ||
1672 | */ | ||
1673 | ret = 0; | ||
1674 | goto out; | ||
1675 | } | ||
1676 | |||
1677 | return 0; | ||
1678 | out: | ||
1679 | if (cmd) | ||
1680 | iscsit_release_cmd(cmd); | ||
1681 | ping_out: | ||
1682 | kfree(ping_data); | ||
1683 | return ret; | ||
1684 | } | ||
1685 | |||
1686 | static int iscsit_handle_task_mgt_cmd( | ||
1687 | struct iscsi_conn *conn, | ||
1688 | unsigned char *buf) | ||
1689 | { | ||
1690 | struct iscsi_cmd *cmd; | ||
1691 | struct se_tmr_req *se_tmr; | ||
1692 | struct iscsi_tmr_req *tmr_req; | ||
1693 | struct iscsi_tm *hdr; | ||
1694 | u32 payload_length; | ||
1695 | int out_of_order_cmdsn = 0; | ||
1696 | int ret; | ||
1697 | u8 function; | ||
1698 | |||
1699 | hdr = (struct iscsi_tm *) buf; | ||
1700 | payload_length = ntoh24(hdr->dlength); | ||
1701 | hdr->itt = be32_to_cpu(hdr->itt); | ||
1702 | hdr->rtt = be32_to_cpu(hdr->rtt); | ||
1703 | hdr->cmdsn = be32_to_cpu(hdr->cmdsn); | ||
1704 | hdr->exp_statsn = be32_to_cpu(hdr->exp_statsn); | ||
1705 | hdr->refcmdsn = be32_to_cpu(hdr->refcmdsn); | ||
1706 | hdr->exp_datasn = be32_to_cpu(hdr->exp_datasn); | ||
1707 | hdr->flags &= ~ISCSI_FLAG_CMD_FINAL; | ||
1708 | function = hdr->flags; | ||
1709 | |||
1710 | pr_debug("Got Task Management Request ITT: 0x%08x, CmdSN:" | ||
1711 | " 0x%08x, Function: 0x%02x, RefTaskTag: 0x%08x, RefCmdSN:" | ||
1712 | " 0x%08x, CID: %hu\n", hdr->itt, hdr->cmdsn, function, | ||
1713 | hdr->rtt, hdr->refcmdsn, conn->cid); | ||
1714 | |||
1715 | if ((function != ISCSI_TM_FUNC_ABORT_TASK) && | ||
1716 | ((function != ISCSI_TM_FUNC_TASK_REASSIGN) && | ||
1717 | (hdr->rtt != ISCSI_RESERVED_TAG))) { | ||
1718 | pr_err("RefTaskTag should be set to 0xFFFFFFFF.\n"); | ||
1719 | hdr->rtt = ISCSI_RESERVED_TAG; | ||
1720 | } | ||
1721 | |||
1722 | if ((function == ISCSI_TM_FUNC_TASK_REASSIGN) && | ||
1723 | !(hdr->opcode & ISCSI_OP_IMMEDIATE)) { | ||
1724 | pr_err("Task Management Request TASK_REASSIGN not" | ||
1725 | " issued as immediate command, bad iSCSI Initiator" | ||
1726 | "implementation\n"); | ||
1727 | return iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1, | ||
1728 | buf, conn); | ||
1729 | } | ||
1730 | if ((function != ISCSI_TM_FUNC_ABORT_TASK) && | ||
1731 | (hdr->refcmdsn != ISCSI_RESERVED_TAG)) | ||
1732 | hdr->refcmdsn = ISCSI_RESERVED_TAG; | ||
1733 | |||
1734 | cmd = iscsit_allocate_se_cmd_for_tmr(conn, function); | ||
1735 | if (!cmd) | ||
1736 | return iscsit_add_reject(ISCSI_REASON_BOOKMARK_NO_RESOURCES, | ||
1737 | 1, buf, conn); | ||
1738 | |||
1739 | cmd->iscsi_opcode = ISCSI_OP_SCSI_TMFUNC; | ||
1740 | cmd->i_state = ISTATE_SEND_TASKMGTRSP; | ||
1741 | cmd->immediate_cmd = ((hdr->opcode & ISCSI_OP_IMMEDIATE) ? 1 : 0); | ||
1742 | cmd->init_task_tag = hdr->itt; | ||
1743 | cmd->targ_xfer_tag = 0xFFFFFFFF; | ||
1744 | cmd->cmd_sn = hdr->cmdsn; | ||
1745 | cmd->exp_stat_sn = hdr->exp_statsn; | ||
1746 | se_tmr = cmd->se_cmd.se_tmr_req; | ||
1747 | tmr_req = cmd->tmr_req; | ||
1748 | /* | ||
1749 | * Locate the struct se_lun for all TMRs not related to ERL=2 TASK_REASSIGN | ||
1750 | */ | ||
1751 | if (function != ISCSI_TM_FUNC_TASK_REASSIGN) { | ||
1752 | ret = iscsit_get_lun_for_tmr(cmd, | ||
1753 | get_unaligned_le64(&hdr->lun)); | ||
1754 | if (ret < 0) { | ||
1755 | cmd->se_cmd.se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION; | ||
1756 | se_tmr->response = ISCSI_TMF_RSP_NO_LUN; | ||
1757 | goto attach; | ||
1758 | } | ||
1759 | } | ||
1760 | |||
1761 | switch (function) { | ||
1762 | case ISCSI_TM_FUNC_ABORT_TASK: | ||
1763 | se_tmr->response = iscsit_tmr_abort_task(cmd, buf); | ||
1764 | if (se_tmr->response != ISCSI_TMF_RSP_COMPLETE) { | ||
1765 | cmd->se_cmd.se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION; | ||
1766 | goto attach; | ||
1767 | } | ||
1768 | break; | ||
1769 | case ISCSI_TM_FUNC_ABORT_TASK_SET: | ||
1770 | case ISCSI_TM_FUNC_CLEAR_ACA: | ||
1771 | case ISCSI_TM_FUNC_CLEAR_TASK_SET: | ||
1772 | case ISCSI_TM_FUNC_LOGICAL_UNIT_RESET: | ||
1773 | break; | ||
1774 | case ISCSI_TM_FUNC_TARGET_WARM_RESET: | ||
1775 | if (iscsit_tmr_task_warm_reset(conn, tmr_req, buf) < 0) { | ||
1776 | cmd->se_cmd.se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION; | ||
1777 | se_tmr->response = ISCSI_TMF_RSP_AUTH_FAILED; | ||
1778 | goto attach; | ||
1779 | } | ||
1780 | break; | ||
1781 | case ISCSI_TM_FUNC_TARGET_COLD_RESET: | ||
1782 | if (iscsit_tmr_task_cold_reset(conn, tmr_req, buf) < 0) { | ||
1783 | cmd->se_cmd.se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION; | ||
1784 | se_tmr->response = ISCSI_TMF_RSP_AUTH_FAILED; | ||
1785 | goto attach; | ||
1786 | } | ||
1787 | break; | ||
1788 | case ISCSI_TM_FUNC_TASK_REASSIGN: | ||
1789 | se_tmr->response = iscsit_tmr_task_reassign(cmd, buf); | ||
1790 | /* | ||
1791 | * Perform sanity checks on the ExpDataSN only if the | ||
1792 | * TASK_REASSIGN was successful. | ||
1793 | */ | ||
1794 | if (se_tmr->response != ISCSI_TMF_RSP_COMPLETE) | ||
1795 | break; | ||
1796 | |||
1797 | if (iscsit_check_task_reassign_expdatasn(tmr_req, conn) < 0) | ||
1798 | return iscsit_add_reject_from_cmd( | ||
1799 | ISCSI_REASON_BOOKMARK_INVALID, 1, 1, | ||
1800 | buf, cmd); | ||
1801 | break; | ||
1802 | default: | ||
1803 | pr_err("Unknown TMR function: 0x%02x, protocol" | ||
1804 | " error.\n", function); | ||
1805 | cmd->se_cmd.se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION; | ||
1806 | se_tmr->response = ISCSI_TMF_RSP_NOT_SUPPORTED; | ||
1807 | goto attach; | ||
1808 | } | ||
1809 | |||
1810 | if ((function != ISCSI_TM_FUNC_TASK_REASSIGN) && | ||
1811 | (se_tmr->response == ISCSI_TMF_RSP_COMPLETE)) | ||
1812 | se_tmr->call_transport = 1; | ||
1813 | attach: | ||
1814 | spin_lock_bh(&conn->cmd_lock); | ||
1815 | list_add_tail(&cmd->i_list, &conn->conn_cmd_list); | ||
1816 | spin_unlock_bh(&conn->cmd_lock); | ||
1817 | |||
1818 | if (!(hdr->opcode & ISCSI_OP_IMMEDIATE)) { | ||
1819 | int cmdsn_ret = iscsit_sequence_cmd(conn, cmd, hdr->cmdsn); | ||
1820 | if (cmdsn_ret == CMDSN_HIGHER_THAN_EXP) | ||
1821 | out_of_order_cmdsn = 1; | ||
1822 | else if (cmdsn_ret == CMDSN_LOWER_THAN_EXP) { | ||
1823 | return 0; | ||
1824 | } else { /* (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER) */ | ||
1825 | return iscsit_add_reject_from_cmd( | ||
1826 | ISCSI_REASON_PROTOCOL_ERROR, | ||
1827 | 1, 0, buf, cmd); | ||
1828 | } | ||
1829 | } | ||
1830 | iscsit_ack_from_expstatsn(conn, hdr->exp_statsn); | ||
1831 | |||
1832 | if (out_of_order_cmdsn) | ||
1833 | return 0; | ||
1834 | /* | ||
1835 | * Found the referenced task, send to transport for processing. | ||
1836 | */ | ||
1837 | if (se_tmr->call_transport) | ||
1838 | return transport_generic_handle_tmr(&cmd->se_cmd); | ||
1839 | |||
1840 | /* | ||
1841 | * Could not find the referenced LUN, task, or Task Management | ||
1842 | * command not authorized or supported. Change state and | ||
1843 | * let the tx_thread send the response. | ||
1844 | * | ||
1845 | * For connection recovery, this is also the default action for | ||
1846 | * TMR TASK_REASSIGN. | ||
1847 | */ | ||
1848 | iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state); | ||
1849 | return 0; | ||
1850 | } | ||
1851 | |||
1852 | /* #warning FIXME: Support Text Command parameters besides SendTargets */ | ||
1853 | static int iscsit_handle_text_cmd( | ||
1854 | struct iscsi_conn *conn, | ||
1855 | unsigned char *buf) | ||
1856 | { | ||
1857 | char *text_ptr, *text_in; | ||
1858 | int cmdsn_ret, niov = 0, rx_got, rx_size; | ||
1859 | u32 checksum = 0, data_crc = 0, payload_length; | ||
1860 | u32 padding = 0, text_length = 0; | ||
1861 | struct iscsi_cmd *cmd; | ||
1862 | struct kvec iov[3]; | ||
1863 | struct iscsi_text *hdr; | ||
1864 | |||
1865 | hdr = (struct iscsi_text *) buf; | ||
1866 | payload_length = ntoh24(hdr->dlength); | ||
1867 | hdr->itt = be32_to_cpu(hdr->itt); | ||
1868 | hdr->ttt = be32_to_cpu(hdr->ttt); | ||
1869 | hdr->cmdsn = be32_to_cpu(hdr->cmdsn); | ||
1870 | hdr->exp_statsn = be32_to_cpu(hdr->exp_statsn); | ||
1871 | |||
1872 | if (payload_length > conn->conn_ops->MaxRecvDataSegmentLength) { | ||
1873 | pr_err("Unable to accept text parameter length: %u" | ||
1874 | "greater than MaxRecvDataSegmentLength %u.\n", | ||
1875 | payload_length, conn->conn_ops->MaxRecvDataSegmentLength); | ||
1876 | return iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1, | ||
1877 | buf, conn); | ||
1878 | } | ||
1879 | |||
1880 | pr_debug("Got Text Request: ITT: 0x%08x, CmdSN: 0x%08x," | ||
1881 | " ExpStatSN: 0x%08x, Length: %u\n", hdr->itt, hdr->cmdsn, | ||
1882 | hdr->exp_statsn, payload_length); | ||
1883 | |||
1884 | rx_size = text_length = payload_length; | ||
1885 | if (text_length) { | ||
1886 | text_in = kzalloc(text_length, GFP_KERNEL); | ||
1887 | if (!text_in) { | ||
1888 | pr_err("Unable to allocate memory for" | ||
1889 | " incoming text parameters\n"); | ||
1890 | return -1; | ||
1891 | } | ||
1892 | |||
1893 | memset(iov, 0, 3 * sizeof(struct kvec)); | ||
1894 | iov[niov].iov_base = text_in; | ||
1895 | iov[niov++].iov_len = text_length; | ||
1896 | |||
1897 | padding = ((-payload_length) & 3); | ||
1898 | if (padding != 0) { | ||
1899 | iov[niov].iov_base = cmd->pad_bytes; | ||
1900 | iov[niov++].iov_len = padding; | ||
1901 | rx_size += padding; | ||
1902 | pr_debug("Receiving %u additional bytes" | ||
1903 | " for padding.\n", padding); | ||
1904 | } | ||
1905 | if (conn->conn_ops->DataDigest) { | ||
1906 | iov[niov].iov_base = &checksum; | ||
1907 | iov[niov++].iov_len = ISCSI_CRC_LEN; | ||
1908 | rx_size += ISCSI_CRC_LEN; | ||
1909 | } | ||
1910 | |||
1911 | rx_got = rx_data(conn, &iov[0], niov, rx_size); | ||
1912 | if (rx_got != rx_size) { | ||
1913 | kfree(text_in); | ||
1914 | return -1; | ||
1915 | } | ||
1916 | |||
1917 | if (conn->conn_ops->DataDigest) { | ||
1918 | iscsit_do_crypto_hash_buf(&conn->conn_rx_hash, | ||
1919 | text_in, text_length, | ||
1920 | padding, cmd->pad_bytes, | ||
1921 | (u8 *)&data_crc); | ||
1922 | |||
1923 | if (checksum != data_crc) { | ||
1924 | pr_err("Text data CRC32C DataDigest" | ||
1925 | " 0x%08x does not match computed" | ||
1926 | " 0x%08x\n", checksum, data_crc); | ||
1927 | if (!conn->sess->sess_ops->ErrorRecoveryLevel) { | ||
1928 | pr_err("Unable to recover from" | ||
1929 | " Text Data digest failure while in" | ||
1930 | " ERL=0.\n"); | ||
1931 | kfree(text_in); | ||
1932 | return -1; | ||
1933 | } else { | ||
1934 | /* | ||
1935 | * Silently drop this PDU and let the | ||
1936 | * initiator plug the CmdSN gap. | ||
1937 | */ | ||
1938 | pr_debug("Dropping Text" | ||
1939 | " Command CmdSN: 0x%08x due to" | ||
1940 | " DataCRC error.\n", hdr->cmdsn); | ||
1941 | kfree(text_in); | ||
1942 | return 0; | ||
1943 | } | ||
1944 | } else { | ||
1945 | pr_debug("Got CRC32C DataDigest" | ||
1946 | " 0x%08x for %u bytes of text data.\n", | ||
1947 | checksum, text_length); | ||
1948 | } | ||
1949 | } | ||
1950 | text_in[text_length - 1] = '\0'; | ||
1951 | pr_debug("Successfully read %d bytes of text" | ||
1952 | " data.\n", text_length); | ||
1953 | |||
1954 | if (strncmp("SendTargets", text_in, 11) != 0) { | ||
1955 | pr_err("Received Text Data that is not" | ||
1956 | " SendTargets, cannot continue.\n"); | ||
1957 | kfree(text_in); | ||
1958 | return -1; | ||
1959 | } | ||
1960 | text_ptr = strchr(text_in, '='); | ||
1961 | if (!text_ptr) { | ||
1962 | pr_err("No \"=\" separator found in Text Data," | ||
1963 | " cannot continue.\n"); | ||
1964 | kfree(text_in); | ||
1965 | return -1; | ||
1966 | } | ||
1967 | if (strncmp("=All", text_ptr, 4) != 0) { | ||
1968 | pr_err("Unable to locate All value for" | ||
1969 | " SendTargets key, cannot continue.\n"); | ||
1970 | kfree(text_in); | ||
1971 | return -1; | ||
1972 | } | ||
1973 | /*#warning Support SendTargets=(iSCSI Target Name/Nothing) values. */ | ||
1974 | kfree(text_in); | ||
1975 | } | ||
1976 | |||
1977 | cmd = iscsit_allocate_cmd(conn, GFP_KERNEL); | ||
1978 | if (!cmd) | ||
1979 | return iscsit_add_reject(ISCSI_REASON_BOOKMARK_NO_RESOURCES, | ||
1980 | 1, buf, conn); | ||
1981 | |||
1982 | cmd->iscsi_opcode = ISCSI_OP_TEXT; | ||
1983 | cmd->i_state = ISTATE_SEND_TEXTRSP; | ||
1984 | cmd->immediate_cmd = ((hdr->opcode & ISCSI_OP_IMMEDIATE) ? 1 : 0); | ||
1985 | conn->sess->init_task_tag = cmd->init_task_tag = hdr->itt; | ||
1986 | cmd->targ_xfer_tag = 0xFFFFFFFF; | ||
1987 | cmd->cmd_sn = hdr->cmdsn; | ||
1988 | cmd->exp_stat_sn = hdr->exp_statsn; | ||
1989 | cmd->data_direction = DMA_NONE; | ||
1990 | |||
1991 | spin_lock_bh(&conn->cmd_lock); | ||
1992 | list_add_tail(&cmd->i_list, &conn->conn_cmd_list); | ||
1993 | spin_unlock_bh(&conn->cmd_lock); | ||
1994 | |||
1995 | iscsit_ack_from_expstatsn(conn, hdr->exp_statsn); | ||
1996 | |||
1997 | if (!(hdr->opcode & ISCSI_OP_IMMEDIATE)) { | ||
1998 | cmdsn_ret = iscsit_sequence_cmd(conn, cmd, hdr->cmdsn); | ||
1999 | if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER) | ||
2000 | return iscsit_add_reject_from_cmd( | ||
2001 | ISCSI_REASON_PROTOCOL_ERROR, | ||
2002 | 1, 0, buf, cmd); | ||
2003 | |||
2004 | return 0; | ||
2005 | } | ||
2006 | |||
2007 | return iscsit_execute_cmd(cmd, 0); | ||
2008 | } | ||
2009 | |||
2010 | int iscsit_logout_closesession(struct iscsi_cmd *cmd, struct iscsi_conn *conn) | ||
2011 | { | ||
2012 | struct iscsi_conn *conn_p; | ||
2013 | struct iscsi_session *sess = conn->sess; | ||
2014 | |||
2015 | pr_debug("Received logout request CLOSESESSION on CID: %hu" | ||
2016 | " for SID: %u.\n", conn->cid, conn->sess->sid); | ||
2017 | |||
2018 | atomic_set(&sess->session_logout, 1); | ||
2019 | atomic_set(&conn->conn_logout_remove, 1); | ||
2020 | conn->conn_logout_reason = ISCSI_LOGOUT_REASON_CLOSE_SESSION; | ||
2021 | |||
2022 | iscsit_inc_conn_usage_count(conn); | ||
2023 | iscsit_inc_session_usage_count(sess); | ||
2024 | |||
2025 | spin_lock_bh(&sess->conn_lock); | ||
2026 | list_for_each_entry(conn_p, &sess->sess_conn_list, conn_list) { | ||
2027 | if (conn_p->conn_state != TARG_CONN_STATE_LOGGED_IN) | ||
2028 | continue; | ||
2029 | |||
2030 | pr_debug("Moving to TARG_CONN_STATE_IN_LOGOUT.\n"); | ||
2031 | conn_p->conn_state = TARG_CONN_STATE_IN_LOGOUT; | ||
2032 | } | ||
2033 | spin_unlock_bh(&sess->conn_lock); | ||
2034 | |||
2035 | iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state); | ||
2036 | |||
2037 | return 0; | ||
2038 | } | ||
2039 | |||
2040 | int iscsit_logout_closeconnection(struct iscsi_cmd *cmd, struct iscsi_conn *conn) | ||
2041 | { | ||
2042 | struct iscsi_conn *l_conn; | ||
2043 | struct iscsi_session *sess = conn->sess; | ||
2044 | |||
2045 | pr_debug("Received logout request CLOSECONNECTION for CID:" | ||
2046 | " %hu on CID: %hu.\n", cmd->logout_cid, conn->cid); | ||
2047 | |||
2048 | /* | ||
2049 | * A Logout Request with a CLOSECONNECTION reason code for a CID | ||
2050 | * can arrive on a connection with a differing CID. | ||
2051 | */ | ||
2052 | if (conn->cid == cmd->logout_cid) { | ||
2053 | spin_lock_bh(&conn->state_lock); | ||
2054 | pr_debug("Moving to TARG_CONN_STATE_IN_LOGOUT.\n"); | ||
2055 | conn->conn_state = TARG_CONN_STATE_IN_LOGOUT; | ||
2056 | |||
2057 | atomic_set(&conn->conn_logout_remove, 1); | ||
2058 | conn->conn_logout_reason = ISCSI_LOGOUT_REASON_CLOSE_CONNECTION; | ||
2059 | iscsit_inc_conn_usage_count(conn); | ||
2060 | |||
2061 | spin_unlock_bh(&conn->state_lock); | ||
2062 | } else { | ||
2063 | /* | ||
2064 | * Handle all different cid CLOSECONNECTION requests in | ||
2065 | * iscsit_logout_post_handler_diffcid() as to give enough | ||
2066 | * time for any non immediate command's CmdSN to be | ||
2067 | * acknowledged on the connection in question. | ||
2068 | * | ||
2069 | * Here we simply make sure the CID is still around. | ||
2070 | */ | ||
2071 | l_conn = iscsit_get_conn_from_cid(sess, | ||
2072 | cmd->logout_cid); | ||
2073 | if (!l_conn) { | ||
2074 | cmd->logout_response = ISCSI_LOGOUT_CID_NOT_FOUND; | ||
2075 | iscsit_add_cmd_to_response_queue(cmd, conn, | ||
2076 | cmd->i_state); | ||
2077 | return 0; | ||
2078 | } | ||
2079 | |||
2080 | iscsit_dec_conn_usage_count(l_conn); | ||
2081 | } | ||
2082 | |||
2083 | iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state); | ||
2084 | |||
2085 | return 0; | ||
2086 | } | ||
2087 | |||
2088 | int iscsit_logout_removeconnforrecovery(struct iscsi_cmd *cmd, struct iscsi_conn *conn) | ||
2089 | { | ||
2090 | struct iscsi_session *sess = conn->sess; | ||
2091 | |||
2092 | pr_debug("Received explicit REMOVECONNFORRECOVERY logout for" | ||
2093 | " CID: %hu on CID: %hu.\n", cmd->logout_cid, conn->cid); | ||
2094 | |||
2095 | if (sess->sess_ops->ErrorRecoveryLevel != 2) { | ||
2096 | pr_err("Received Logout Request REMOVECONNFORRECOVERY" | ||
2097 | " while ERL!=2.\n"); | ||
2098 | cmd->logout_response = ISCSI_LOGOUT_RECOVERY_UNSUPPORTED; | ||
2099 | iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state); | ||
2100 | return 0; | ||
2101 | } | ||
2102 | |||
2103 | if (conn->cid == cmd->logout_cid) { | ||
2104 | pr_err("Received Logout Request REMOVECONNFORRECOVERY" | ||
2105 | " with CID: %hu on CID: %hu, implementation error.\n", | ||
2106 | cmd->logout_cid, conn->cid); | ||
2107 | cmd->logout_response = ISCSI_LOGOUT_CLEANUP_FAILED; | ||
2108 | iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state); | ||
2109 | return 0; | ||
2110 | } | ||
2111 | |||
2112 | iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state); | ||
2113 | |||
2114 | return 0; | ||
2115 | } | ||
2116 | |||
2117 | static int iscsit_handle_logout_cmd( | ||
2118 | struct iscsi_conn *conn, | ||
2119 | unsigned char *buf) | ||
2120 | { | ||
2121 | int cmdsn_ret, logout_remove = 0; | ||
2122 | u8 reason_code = 0; | ||
2123 | struct iscsi_cmd *cmd; | ||
2124 | struct iscsi_logout *hdr; | ||
2125 | struct iscsi_tiqn *tiqn = iscsit_snmp_get_tiqn(conn); | ||
2126 | |||
2127 | hdr = (struct iscsi_logout *) buf; | ||
2128 | reason_code = (hdr->flags & 0x7f); | ||
2129 | hdr->itt = be32_to_cpu(hdr->itt); | ||
2130 | hdr->cid = be16_to_cpu(hdr->cid); | ||
2131 | hdr->cmdsn = be32_to_cpu(hdr->cmdsn); | ||
2132 | hdr->exp_statsn = be32_to_cpu(hdr->exp_statsn); | ||
2133 | |||
2134 | if (tiqn) { | ||
2135 | spin_lock(&tiqn->logout_stats.lock); | ||
2136 | if (reason_code == ISCSI_LOGOUT_REASON_CLOSE_SESSION) | ||
2137 | tiqn->logout_stats.normal_logouts++; | ||
2138 | else | ||
2139 | tiqn->logout_stats.abnormal_logouts++; | ||
2140 | spin_unlock(&tiqn->logout_stats.lock); | ||
2141 | } | ||
2142 | |||
2143 | pr_debug("Got Logout Request ITT: 0x%08x CmdSN: 0x%08x" | ||
2144 | " ExpStatSN: 0x%08x Reason: 0x%02x CID: %hu on CID: %hu\n", | ||
2145 | hdr->itt, hdr->cmdsn, hdr->exp_statsn, reason_code, | ||
2146 | hdr->cid, conn->cid); | ||
2147 | |||
2148 | if (conn->conn_state != TARG_CONN_STATE_LOGGED_IN) { | ||
2149 | pr_err("Received logout request on connection that" | ||
2150 | " is not in logged in state, ignoring request.\n"); | ||
2151 | return 0; | ||
2152 | } | ||
2153 | |||
2154 | cmd = iscsit_allocate_cmd(conn, GFP_KERNEL); | ||
2155 | if (!cmd) | ||
2156 | return iscsit_add_reject(ISCSI_REASON_BOOKMARK_NO_RESOURCES, 1, | ||
2157 | buf, conn); | ||
2158 | |||
2159 | cmd->iscsi_opcode = ISCSI_OP_LOGOUT; | ||
2160 | cmd->i_state = ISTATE_SEND_LOGOUTRSP; | ||
2161 | cmd->immediate_cmd = ((hdr->opcode & ISCSI_OP_IMMEDIATE) ? 1 : 0); | ||
2162 | conn->sess->init_task_tag = cmd->init_task_tag = hdr->itt; | ||
2163 | cmd->targ_xfer_tag = 0xFFFFFFFF; | ||
2164 | cmd->cmd_sn = hdr->cmdsn; | ||
2165 | cmd->exp_stat_sn = hdr->exp_statsn; | ||
2166 | cmd->logout_cid = hdr->cid; | ||
2167 | cmd->logout_reason = reason_code; | ||
2168 | cmd->data_direction = DMA_NONE; | ||
2169 | |||
2170 | /* | ||
2171 | * We need to sleep in these cases (by returning 1) until the Logout | ||
2172 | * Response gets sent in the tx thread. | ||
2173 | */ | ||
2174 | if ((reason_code == ISCSI_LOGOUT_REASON_CLOSE_SESSION) || | ||
2175 | ((reason_code == ISCSI_LOGOUT_REASON_CLOSE_CONNECTION) && | ||
2176 | (hdr->cid == conn->cid))) | ||
2177 | logout_remove = 1; | ||
2178 | |||
2179 | spin_lock_bh(&conn->cmd_lock); | ||
2180 | list_add_tail(&cmd->i_list, &conn->conn_cmd_list); | ||
2181 | spin_unlock_bh(&conn->cmd_lock); | ||
2182 | |||
2183 | if (reason_code != ISCSI_LOGOUT_REASON_RECOVERY) | ||
2184 | iscsit_ack_from_expstatsn(conn, hdr->exp_statsn); | ||
2185 | |||
2186 | /* | ||
2187 | * Immediate commands are executed, well, immediately. | ||
2188 | * Non-Immediate Logout Commands are executed in CmdSN order. | ||
2189 | */ | ||
2190 | if (hdr->opcode & ISCSI_OP_IMMEDIATE) { | ||
2191 | int ret = iscsit_execute_cmd(cmd, 0); | ||
2192 | |||
2193 | if (ret < 0) | ||
2194 | return ret; | ||
2195 | } else { | ||
2196 | cmdsn_ret = iscsit_sequence_cmd(conn, cmd, hdr->cmdsn); | ||
2197 | if (cmdsn_ret == CMDSN_LOWER_THAN_EXP) { | ||
2198 | logout_remove = 0; | ||
2199 | } else if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER) { | ||
2200 | return iscsit_add_reject_from_cmd( | ||
2201 | ISCSI_REASON_PROTOCOL_ERROR, | ||
2202 | 1, 0, buf, cmd); | ||
2203 | } | ||
2204 | } | ||
2205 | |||
2206 | return logout_remove; | ||
2207 | } | ||
2208 | |||
2209 | static int iscsit_handle_snack( | ||
2210 | struct iscsi_conn *conn, | ||
2211 | unsigned char *buf) | ||
2212 | { | ||
2213 | u32 unpacked_lun; | ||
2214 | u64 lun; | ||
2215 | struct iscsi_snack *hdr; | ||
2216 | |||
2217 | hdr = (struct iscsi_snack *) buf; | ||
2218 | hdr->flags &= ~ISCSI_FLAG_CMD_FINAL; | ||
2219 | lun = get_unaligned_le64(&hdr->lun); | ||
2220 | unpacked_lun = scsilun_to_int((struct scsi_lun *)&lun); | ||
2221 | hdr->itt = be32_to_cpu(hdr->itt); | ||
2222 | hdr->ttt = be32_to_cpu(hdr->ttt); | ||
2223 | hdr->exp_statsn = be32_to_cpu(hdr->exp_statsn); | ||
2224 | hdr->begrun = be32_to_cpu(hdr->begrun); | ||
2225 | hdr->runlength = be32_to_cpu(hdr->runlength); | ||
2226 | |||
2227 | pr_debug("Got ISCSI_INIT_SNACK, ITT: 0x%08x, ExpStatSN:" | ||
2228 | " 0x%08x, Type: 0x%02x, BegRun: 0x%08x, RunLength: 0x%08x," | ||
2229 | " CID: %hu\n", hdr->itt, hdr->exp_statsn, hdr->flags, | ||
2230 | hdr->begrun, hdr->runlength, conn->cid); | ||
2231 | |||
2232 | if (!conn->sess->sess_ops->ErrorRecoveryLevel) { | ||
2233 | pr_err("Initiator sent SNACK request while in" | ||
2234 | " ErrorRecoveryLevel=0.\n"); | ||
2235 | return iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1, | ||
2236 | buf, conn); | ||
2237 | } | ||
2238 | /* | ||
2239 | * SNACK_DATA and SNACK_R2T are both 0, so check which function to | ||
2240 | * call from inside iscsi_send_recovery_datain_or_r2t(). | ||
2241 | */ | ||
2242 | switch (hdr->flags & ISCSI_FLAG_SNACK_TYPE_MASK) { | ||
2243 | case 0: | ||
2244 | return iscsit_handle_recovery_datain_or_r2t(conn, buf, | ||
2245 | hdr->itt, hdr->ttt, hdr->begrun, hdr->runlength); | ||
2246 | return 0; | ||
2247 | case ISCSI_FLAG_SNACK_TYPE_STATUS: | ||
2248 | return iscsit_handle_status_snack(conn, hdr->itt, hdr->ttt, | ||
2249 | hdr->begrun, hdr->runlength); | ||
2250 | case ISCSI_FLAG_SNACK_TYPE_DATA_ACK: | ||
2251 | return iscsit_handle_data_ack(conn, hdr->ttt, hdr->begrun, | ||
2252 | hdr->runlength); | ||
2253 | case ISCSI_FLAG_SNACK_TYPE_RDATA: | ||
2254 | /* FIXME: Support R-Data SNACK */ | ||
2255 | pr_err("R-Data SNACK Not Supported.\n"); | ||
2256 | return iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1, | ||
2257 | buf, conn); | ||
2258 | default: | ||
2259 | pr_err("Unknown SNACK type 0x%02x, protocol" | ||
2260 | " error.\n", hdr->flags & 0x0f); | ||
2261 | return iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1, | ||
2262 | buf, conn); | ||
2263 | } | ||
2264 | |||
2265 | return 0; | ||
2266 | } | ||
2267 | |||
2268 | static void iscsit_rx_thread_wait_for_tcp(struct iscsi_conn *conn) | ||
2269 | { | ||
2270 | if ((conn->sock->sk->sk_shutdown & SEND_SHUTDOWN) || | ||
2271 | (conn->sock->sk->sk_shutdown & RCV_SHUTDOWN)) { | ||
2272 | wait_for_completion_interruptible_timeout( | ||
2273 | &conn->rx_half_close_comp, | ||
2274 | ISCSI_RX_THREAD_TCP_TIMEOUT * HZ); | ||
2275 | } | ||
2276 | } | ||
2277 | |||
2278 | static int iscsit_handle_immediate_data( | ||
2279 | struct iscsi_cmd *cmd, | ||
2280 | unsigned char *buf, | ||
2281 | u32 length) | ||
2282 | { | ||
2283 | int iov_ret, rx_got = 0, rx_size = 0; | ||
2284 | u32 checksum, iov_count = 0, padding = 0; | ||
2285 | struct iscsi_conn *conn = cmd->conn; | ||
2286 | struct kvec *iov; | ||
2287 | |||
2288 | iov_ret = iscsit_map_iovec(cmd, cmd->iov_data, cmd->write_data_done, length); | ||
2289 | if (iov_ret < 0) | ||
2290 | return IMMEDIATE_DATA_CANNOT_RECOVER; | ||
2291 | |||
2292 | rx_size = length; | ||
2293 | iov_count = iov_ret; | ||
2294 | iov = &cmd->iov_data[0]; | ||
2295 | |||
2296 | padding = ((-length) & 3); | ||
2297 | if (padding != 0) { | ||
2298 | iov[iov_count].iov_base = cmd->pad_bytes; | ||
2299 | iov[iov_count++].iov_len = padding; | ||
2300 | rx_size += padding; | ||
2301 | } | ||
2302 | |||
2303 | if (conn->conn_ops->DataDigest) { | ||
2304 | iov[iov_count].iov_base = &checksum; | ||
2305 | iov[iov_count++].iov_len = ISCSI_CRC_LEN; | ||
2306 | rx_size += ISCSI_CRC_LEN; | ||
2307 | } | ||
2308 | |||
2309 | rx_got = rx_data(conn, &cmd->iov_data[0], iov_count, rx_size); | ||
2310 | |||
2311 | iscsit_unmap_iovec(cmd); | ||
2312 | |||
2313 | if (rx_got != rx_size) { | ||
2314 | iscsit_rx_thread_wait_for_tcp(conn); | ||
2315 | return IMMEDIATE_DATA_CANNOT_RECOVER; | ||
2316 | } | ||
2317 | |||
2318 | if (conn->conn_ops->DataDigest) { | ||
2319 | u32 data_crc; | ||
2320 | |||
2321 | data_crc = iscsit_do_crypto_hash_sg(&conn->conn_rx_hash, cmd, | ||
2322 | cmd->write_data_done, length, padding, | ||
2323 | cmd->pad_bytes); | ||
2324 | |||
2325 | if (checksum != data_crc) { | ||
2326 | pr_err("ImmediateData CRC32C DataDigest 0x%08x" | ||
2327 | " does not match computed 0x%08x\n", checksum, | ||
2328 | data_crc); | ||
2329 | |||
2330 | if (!conn->sess->sess_ops->ErrorRecoveryLevel) { | ||
2331 | pr_err("Unable to recover from" | ||
2332 | " Immediate Data digest failure while" | ||
2333 | " in ERL=0.\n"); | ||
2334 | iscsit_add_reject_from_cmd( | ||
2335 | ISCSI_REASON_DATA_DIGEST_ERROR, | ||
2336 | 1, 0, buf, cmd); | ||
2337 | return IMMEDIATE_DATA_CANNOT_RECOVER; | ||
2338 | } else { | ||
2339 | iscsit_add_reject_from_cmd( | ||
2340 | ISCSI_REASON_DATA_DIGEST_ERROR, | ||
2341 | 0, 0, buf, cmd); | ||
2342 | return IMMEDIATE_DATA_ERL1_CRC_FAILURE; | ||
2343 | } | ||
2344 | } else { | ||
2345 | pr_debug("Got CRC32C DataDigest 0x%08x for" | ||
2346 | " %u bytes of Immediate Data\n", checksum, | ||
2347 | length); | ||
2348 | } | ||
2349 | } | ||
2350 | |||
2351 | cmd->write_data_done += length; | ||
2352 | |||
2353 | if (cmd->write_data_done == cmd->data_length) { | ||
2354 | spin_lock_bh(&cmd->istate_lock); | ||
2355 | cmd->cmd_flags |= ICF_GOT_LAST_DATAOUT; | ||
2356 | cmd->i_state = ISTATE_RECEIVED_LAST_DATAOUT; | ||
2357 | spin_unlock_bh(&cmd->istate_lock); | ||
2358 | } | ||
2359 | |||
2360 | return IMMEDIATE_DATA_NORMAL_OPERATION; | ||
2361 | } | ||
2362 | |||
2363 | /* | ||
2364 | * Called with sess->conn_lock held. | ||
2365 | */ | ||
2366 | /* #warning iscsi_build_conn_drop_async_message() only sends out on connections | ||
2367 | with active network interface */ | ||
2368 | static void iscsit_build_conn_drop_async_message(struct iscsi_conn *conn) | ||
2369 | { | ||
2370 | struct iscsi_cmd *cmd; | ||
2371 | struct iscsi_conn *conn_p; | ||
2372 | |||
2373 | /* | ||
2374 | * Only send a Asynchronous Message on connections whos network | ||
2375 | * interface is still functional. | ||
2376 | */ | ||
2377 | list_for_each_entry(conn_p, &conn->sess->sess_conn_list, conn_list) { | ||
2378 | if (conn_p->conn_state == TARG_CONN_STATE_LOGGED_IN) { | ||
2379 | iscsit_inc_conn_usage_count(conn_p); | ||
2380 | break; | ||
2381 | } | ||
2382 | } | ||
2383 | |||
2384 | if (!conn_p) | ||
2385 | return; | ||
2386 | |||
2387 | cmd = iscsit_allocate_cmd(conn_p, GFP_KERNEL); | ||
2388 | if (!cmd) { | ||
2389 | iscsit_dec_conn_usage_count(conn_p); | ||
2390 | return; | ||
2391 | } | ||
2392 | |||
2393 | cmd->logout_cid = conn->cid; | ||
2394 | cmd->iscsi_opcode = ISCSI_OP_ASYNC_EVENT; | ||
2395 | cmd->i_state = ISTATE_SEND_ASYNCMSG; | ||
2396 | |||
2397 | spin_lock_bh(&conn_p->cmd_lock); | ||
2398 | list_add_tail(&cmd->i_list, &conn_p->conn_cmd_list); | ||
2399 | spin_unlock_bh(&conn_p->cmd_lock); | ||
2400 | |||
2401 | iscsit_add_cmd_to_response_queue(cmd, conn_p, cmd->i_state); | ||
2402 | iscsit_dec_conn_usage_count(conn_p); | ||
2403 | } | ||
2404 | |||
2405 | static int iscsit_send_conn_drop_async_message( | ||
2406 | struct iscsi_cmd *cmd, | ||
2407 | struct iscsi_conn *conn) | ||
2408 | { | ||
2409 | struct iscsi_async *hdr; | ||
2410 | |||
2411 | cmd->tx_size = ISCSI_HDR_LEN; | ||
2412 | cmd->iscsi_opcode = ISCSI_OP_ASYNC_EVENT; | ||
2413 | |||
2414 | hdr = (struct iscsi_async *) cmd->pdu; | ||
2415 | hdr->opcode = ISCSI_OP_ASYNC_EVENT; | ||
2416 | hdr->flags = ISCSI_FLAG_CMD_FINAL; | ||
2417 | cmd->init_task_tag = 0xFFFFFFFF; | ||
2418 | cmd->targ_xfer_tag = 0xFFFFFFFF; | ||
2419 | put_unaligned_be64(0xFFFFFFFFFFFFFFFFULL, &hdr->rsvd4[0]); | ||
2420 | cmd->stat_sn = conn->stat_sn++; | ||
2421 | hdr->statsn = cpu_to_be32(cmd->stat_sn); | ||
2422 | hdr->exp_cmdsn = cpu_to_be32(conn->sess->exp_cmd_sn); | ||
2423 | hdr->max_cmdsn = cpu_to_be32(conn->sess->max_cmd_sn); | ||
2424 | hdr->async_event = ISCSI_ASYNC_MSG_DROPPING_CONNECTION; | ||
2425 | hdr->param1 = cpu_to_be16(cmd->logout_cid); | ||
2426 | hdr->param2 = cpu_to_be16(conn->sess->sess_ops->DefaultTime2Wait); | ||
2427 | hdr->param3 = cpu_to_be16(conn->sess->sess_ops->DefaultTime2Retain); | ||
2428 | |||
2429 | if (conn->conn_ops->HeaderDigest) { | ||
2430 | u32 *header_digest = (u32 *)&cmd->pdu[ISCSI_HDR_LEN]; | ||
2431 | |||
2432 | iscsit_do_crypto_hash_buf(&conn->conn_tx_hash, | ||
2433 | (unsigned char *)hdr, ISCSI_HDR_LEN, | ||
2434 | 0, NULL, (u8 *)header_digest); | ||
2435 | |||
2436 | cmd->tx_size += ISCSI_CRC_LEN; | ||
2437 | pr_debug("Attaching CRC32C HeaderDigest to" | ||
2438 | " Async Message 0x%08x\n", *header_digest); | ||
2439 | } | ||
2440 | |||
2441 | cmd->iov_misc[0].iov_base = cmd->pdu; | ||
2442 | cmd->iov_misc[0].iov_len = cmd->tx_size; | ||
2443 | cmd->iov_misc_count = 1; | ||
2444 | |||
2445 | pr_debug("Sending Connection Dropped Async Message StatSN:" | ||
2446 | " 0x%08x, for CID: %hu on CID: %hu\n", cmd->stat_sn, | ||
2447 | cmd->logout_cid, conn->cid); | ||
2448 | return 0; | ||
2449 | } | ||
2450 | |||
2451 | static int iscsit_send_data_in( | ||
2452 | struct iscsi_cmd *cmd, | ||
2453 | struct iscsi_conn *conn, | ||
2454 | int *eodr) | ||
2455 | { | ||
2456 | int iov_ret = 0, set_statsn = 0; | ||
2457 | u32 iov_count = 0, tx_size = 0; | ||
2458 | struct iscsi_datain datain; | ||
2459 | struct iscsi_datain_req *dr; | ||
2460 | struct iscsi_data_rsp *hdr; | ||
2461 | struct kvec *iov; | ||
2462 | |||
2463 | memset(&datain, 0, sizeof(struct iscsi_datain)); | ||
2464 | dr = iscsit_get_datain_values(cmd, &datain); | ||
2465 | if (!dr) { | ||
2466 | pr_err("iscsit_get_datain_values failed for ITT: 0x%08x\n", | ||
2467 | cmd->init_task_tag); | ||
2468 | return -1; | ||
2469 | } | ||
2470 | |||
2471 | /* | ||
2472 | * Be paranoid and double check the logic for now. | ||
2473 | */ | ||
2474 | if ((datain.offset + datain.length) > cmd->data_length) { | ||
2475 | pr_err("Command ITT: 0x%08x, datain.offset: %u and" | ||
2476 | " datain.length: %u exceeds cmd->data_length: %u\n", | ||
2477 | cmd->init_task_tag, datain.offset, datain.length, | ||
2478 | cmd->data_length); | ||
2479 | return -1; | ||
2480 | } | ||
2481 | |||
2482 | spin_lock_bh(&conn->sess->session_stats_lock); | ||
2483 | conn->sess->tx_data_octets += datain.length; | ||
2484 | if (conn->sess->se_sess->se_node_acl) { | ||
2485 | spin_lock(&conn->sess->se_sess->se_node_acl->stats_lock); | ||
2486 | conn->sess->se_sess->se_node_acl->read_bytes += datain.length; | ||
2487 | spin_unlock(&conn->sess->se_sess->se_node_acl->stats_lock); | ||
2488 | } | ||
2489 | spin_unlock_bh(&conn->sess->session_stats_lock); | ||
2490 | /* | ||
2491 | * Special case for successfully execution w/ both DATAIN | ||
2492 | * and Sense Data. | ||
2493 | */ | ||
2494 | if ((datain.flags & ISCSI_FLAG_DATA_STATUS) && | ||
2495 | (cmd->se_cmd.se_cmd_flags & SCF_TRANSPORT_TASK_SENSE)) | ||
2496 | datain.flags &= ~ISCSI_FLAG_DATA_STATUS; | ||
2497 | else { | ||
2498 | if ((dr->dr_complete == DATAIN_COMPLETE_NORMAL) || | ||
2499 | (dr->dr_complete == DATAIN_COMPLETE_CONNECTION_RECOVERY)) { | ||
2500 | iscsit_increment_maxcmdsn(cmd, conn->sess); | ||
2501 | cmd->stat_sn = conn->stat_sn++; | ||
2502 | set_statsn = 1; | ||
2503 | } else if (dr->dr_complete == | ||
2504 | DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY) | ||
2505 | set_statsn = 1; | ||
2506 | } | ||
2507 | |||
2508 | hdr = (struct iscsi_data_rsp *) cmd->pdu; | ||
2509 | memset(hdr, 0, ISCSI_HDR_LEN); | ||
2510 | hdr->opcode = ISCSI_OP_SCSI_DATA_IN; | ||
2511 | hdr->flags = datain.flags; | ||
2512 | if (hdr->flags & ISCSI_FLAG_DATA_STATUS) { | ||
2513 | if (cmd->se_cmd.se_cmd_flags & SCF_OVERFLOW_BIT) { | ||
2514 | hdr->flags |= ISCSI_FLAG_DATA_OVERFLOW; | ||
2515 | hdr->residual_count = cpu_to_be32(cmd->residual_count); | ||
2516 | } else if (cmd->se_cmd.se_cmd_flags & SCF_UNDERFLOW_BIT) { | ||
2517 | hdr->flags |= ISCSI_FLAG_DATA_UNDERFLOW; | ||
2518 | hdr->residual_count = cpu_to_be32(cmd->residual_count); | ||
2519 | } | ||
2520 | } | ||
2521 | hton24(hdr->dlength, datain.length); | ||
2522 | if (hdr->flags & ISCSI_FLAG_DATA_ACK) | ||
2523 | int_to_scsilun(cmd->se_cmd.orig_fe_lun, | ||
2524 | (struct scsi_lun *)&hdr->lun); | ||
2525 | else | ||
2526 | put_unaligned_le64(0xFFFFFFFFFFFFFFFFULL, &hdr->lun); | ||
2527 | |||
2528 | hdr->itt = cpu_to_be32(cmd->init_task_tag); | ||
2529 | hdr->ttt = (hdr->flags & ISCSI_FLAG_DATA_ACK) ? | ||
2530 | cpu_to_be32(cmd->targ_xfer_tag) : | ||
2531 | 0xFFFFFFFF; | ||
2532 | hdr->statsn = (set_statsn) ? cpu_to_be32(cmd->stat_sn) : | ||
2533 | 0xFFFFFFFF; | ||
2534 | hdr->exp_cmdsn = cpu_to_be32(conn->sess->exp_cmd_sn); | ||
2535 | hdr->max_cmdsn = cpu_to_be32(conn->sess->max_cmd_sn); | ||
2536 | hdr->datasn = cpu_to_be32(datain.data_sn); | ||
2537 | hdr->offset = cpu_to_be32(datain.offset); | ||
2538 | |||
2539 | iov = &cmd->iov_data[0]; | ||
2540 | iov[iov_count].iov_base = cmd->pdu; | ||
2541 | iov[iov_count++].iov_len = ISCSI_HDR_LEN; | ||
2542 | tx_size += ISCSI_HDR_LEN; | ||
2543 | |||
2544 | if (conn->conn_ops->HeaderDigest) { | ||
2545 | u32 *header_digest = (u32 *)&cmd->pdu[ISCSI_HDR_LEN]; | ||
2546 | |||
2547 | iscsit_do_crypto_hash_buf(&conn->conn_tx_hash, | ||
2548 | (unsigned char *)hdr, ISCSI_HDR_LEN, | ||
2549 | 0, NULL, (u8 *)header_digest); | ||
2550 | |||
2551 | iov[0].iov_len += ISCSI_CRC_LEN; | ||
2552 | tx_size += ISCSI_CRC_LEN; | ||
2553 | |||
2554 | pr_debug("Attaching CRC32 HeaderDigest" | ||
2555 | " for DataIN PDU 0x%08x\n", *header_digest); | ||
2556 | } | ||
2557 | |||
2558 | iov_ret = iscsit_map_iovec(cmd, &cmd->iov_data[1], datain.offset, datain.length); | ||
2559 | if (iov_ret < 0) | ||
2560 | return -1; | ||
2561 | |||
2562 | iov_count += iov_ret; | ||
2563 | tx_size += datain.length; | ||
2564 | |||
2565 | cmd->padding = ((-datain.length) & 3); | ||
2566 | if (cmd->padding) { | ||
2567 | iov[iov_count].iov_base = cmd->pad_bytes; | ||
2568 | iov[iov_count++].iov_len = cmd->padding; | ||
2569 | tx_size += cmd->padding; | ||
2570 | |||
2571 | pr_debug("Attaching %u padding bytes\n", | ||
2572 | cmd->padding); | ||
2573 | } | ||
2574 | if (conn->conn_ops->DataDigest) { | ||
2575 | cmd->data_crc = iscsit_do_crypto_hash_sg(&conn->conn_tx_hash, cmd, | ||
2576 | datain.offset, datain.length, cmd->padding, cmd->pad_bytes); | ||
2577 | |||
2578 | iov[iov_count].iov_base = &cmd->data_crc; | ||
2579 | iov[iov_count++].iov_len = ISCSI_CRC_LEN; | ||
2580 | tx_size += ISCSI_CRC_LEN; | ||
2581 | |||
2582 | pr_debug("Attached CRC32C DataDigest %d bytes, crc" | ||
2583 | " 0x%08x\n", datain.length+cmd->padding, cmd->data_crc); | ||
2584 | } | ||
2585 | |||
2586 | cmd->iov_data_count = iov_count; | ||
2587 | cmd->tx_size = tx_size; | ||
2588 | |||
2589 | pr_debug("Built DataIN ITT: 0x%08x, StatSN: 0x%08x," | ||
2590 | " DataSN: 0x%08x, Offset: %u, Length: %u, CID: %hu\n", | ||
2591 | cmd->init_task_tag, ntohl(hdr->statsn), ntohl(hdr->datasn), | ||
2592 | ntohl(hdr->offset), datain.length, conn->cid); | ||
2593 | |||
2594 | if (dr->dr_complete) { | ||
2595 | *eodr = (cmd->se_cmd.se_cmd_flags & SCF_TRANSPORT_TASK_SENSE) ? | ||
2596 | 2 : 1; | ||
2597 | iscsit_free_datain_req(cmd, dr); | ||
2598 | } | ||
2599 | |||
2600 | return 0; | ||
2601 | } | ||
2602 | |||
2603 | static int iscsit_send_logout_response( | ||
2604 | struct iscsi_cmd *cmd, | ||
2605 | struct iscsi_conn *conn) | ||
2606 | { | ||
2607 | int niov = 0, tx_size; | ||
2608 | struct iscsi_conn *logout_conn = NULL; | ||
2609 | struct iscsi_conn_recovery *cr = NULL; | ||
2610 | struct iscsi_session *sess = conn->sess; | ||
2611 | struct kvec *iov; | ||
2612 | struct iscsi_logout_rsp *hdr; | ||
2613 | /* | ||
2614 | * The actual shutting down of Sessions and/or Connections | ||
2615 | * for CLOSESESSION and CLOSECONNECTION Logout Requests | ||
2616 | * is done in scsi_logout_post_handler(). | ||
2617 | */ | ||
2618 | switch (cmd->logout_reason) { | ||
2619 | case ISCSI_LOGOUT_REASON_CLOSE_SESSION: | ||
2620 | pr_debug("iSCSI session logout successful, setting" | ||
2621 | " logout response to ISCSI_LOGOUT_SUCCESS.\n"); | ||
2622 | cmd->logout_response = ISCSI_LOGOUT_SUCCESS; | ||
2623 | break; | ||
2624 | case ISCSI_LOGOUT_REASON_CLOSE_CONNECTION: | ||
2625 | if (cmd->logout_response == ISCSI_LOGOUT_CID_NOT_FOUND) | ||
2626 | break; | ||
2627 | /* | ||
2628 | * For CLOSECONNECTION logout requests carrying | ||
2629 | * a matching logout CID -> local CID, the reference | ||
2630 | * for the local CID will have been incremented in | ||
2631 | * iscsi_logout_closeconnection(). | ||
2632 | * | ||
2633 | * For CLOSECONNECTION logout requests carrying | ||
2634 | * a different CID than the connection it arrived | ||
2635 | * on, the connection responding to cmd->logout_cid | ||
2636 | * is stopped in iscsit_logout_post_handler_diffcid(). | ||
2637 | */ | ||
2638 | |||
2639 | pr_debug("iSCSI CID: %hu logout on CID: %hu" | ||
2640 | " successful.\n", cmd->logout_cid, conn->cid); | ||
2641 | cmd->logout_response = ISCSI_LOGOUT_SUCCESS; | ||
2642 | break; | ||
2643 | case ISCSI_LOGOUT_REASON_RECOVERY: | ||
2644 | if ((cmd->logout_response == ISCSI_LOGOUT_RECOVERY_UNSUPPORTED) || | ||
2645 | (cmd->logout_response == ISCSI_LOGOUT_CLEANUP_FAILED)) | ||
2646 | break; | ||
2647 | /* | ||
2648 | * If the connection is still active from our point of view | ||
2649 | * force connection recovery to occur. | ||
2650 | */ | ||
2651 | logout_conn = iscsit_get_conn_from_cid_rcfr(sess, | ||
2652 | cmd->logout_cid); | ||
2653 | if ((logout_conn)) { | ||
2654 | iscsit_connection_reinstatement_rcfr(logout_conn); | ||
2655 | iscsit_dec_conn_usage_count(logout_conn); | ||
2656 | } | ||
2657 | |||
2658 | cr = iscsit_get_inactive_connection_recovery_entry( | ||
2659 | conn->sess, cmd->logout_cid); | ||
2660 | if (!cr) { | ||
2661 | pr_err("Unable to locate CID: %hu for" | ||
2662 | " REMOVECONNFORRECOVERY Logout Request.\n", | ||
2663 | cmd->logout_cid); | ||
2664 | cmd->logout_response = ISCSI_LOGOUT_CID_NOT_FOUND; | ||
2665 | break; | ||
2666 | } | ||
2667 | |||
2668 | iscsit_discard_cr_cmds_by_expstatsn(cr, cmd->exp_stat_sn); | ||
2669 | |||
2670 | pr_debug("iSCSI REMOVECONNFORRECOVERY logout" | ||
2671 | " for recovery for CID: %hu on CID: %hu successful.\n", | ||
2672 | cmd->logout_cid, conn->cid); | ||
2673 | cmd->logout_response = ISCSI_LOGOUT_SUCCESS; | ||
2674 | break; | ||
2675 | default: | ||
2676 | pr_err("Unknown cmd->logout_reason: 0x%02x\n", | ||
2677 | cmd->logout_reason); | ||
2678 | return -1; | ||
2679 | } | ||
2680 | |||
2681 | tx_size = ISCSI_HDR_LEN; | ||
2682 | hdr = (struct iscsi_logout_rsp *)cmd->pdu; | ||
2683 | memset(hdr, 0, ISCSI_HDR_LEN); | ||
2684 | hdr->opcode = ISCSI_OP_LOGOUT_RSP; | ||
2685 | hdr->flags |= ISCSI_FLAG_CMD_FINAL; | ||
2686 | hdr->response = cmd->logout_response; | ||
2687 | hdr->itt = cpu_to_be32(cmd->init_task_tag); | ||
2688 | cmd->stat_sn = conn->stat_sn++; | ||
2689 | hdr->statsn = cpu_to_be32(cmd->stat_sn); | ||
2690 | |||
2691 | iscsit_increment_maxcmdsn(cmd, conn->sess); | ||
2692 | hdr->exp_cmdsn = cpu_to_be32(conn->sess->exp_cmd_sn); | ||
2693 | hdr->max_cmdsn = cpu_to_be32(conn->sess->max_cmd_sn); | ||
2694 | |||
2695 | iov = &cmd->iov_misc[0]; | ||
2696 | iov[niov].iov_base = cmd->pdu; | ||
2697 | iov[niov++].iov_len = ISCSI_HDR_LEN; | ||
2698 | |||
2699 | if (conn->conn_ops->HeaderDigest) { | ||
2700 | u32 *header_digest = (u32 *)&cmd->pdu[ISCSI_HDR_LEN]; | ||
2701 | |||
2702 | iscsit_do_crypto_hash_buf(&conn->conn_tx_hash, | ||
2703 | (unsigned char *)hdr, ISCSI_HDR_LEN, | ||
2704 | 0, NULL, (u8 *)header_digest); | ||
2705 | |||
2706 | iov[0].iov_len += ISCSI_CRC_LEN; | ||
2707 | tx_size += ISCSI_CRC_LEN; | ||
2708 | pr_debug("Attaching CRC32C HeaderDigest to" | ||
2709 | " Logout Response 0x%08x\n", *header_digest); | ||
2710 | } | ||
2711 | cmd->iov_misc_count = niov; | ||
2712 | cmd->tx_size = tx_size; | ||
2713 | |||
2714 | pr_debug("Sending Logout Response ITT: 0x%08x StatSN:" | ||
2715 | " 0x%08x Response: 0x%02x CID: %hu on CID: %hu\n", | ||
2716 | cmd->init_task_tag, cmd->stat_sn, hdr->response, | ||
2717 | cmd->logout_cid, conn->cid); | ||
2718 | |||
2719 | return 0; | ||
2720 | } | ||
2721 | |||
2722 | /* | ||
2723 | * Unsolicited NOPIN, either requesting a response or not. | ||
2724 | */ | ||
2725 | static int iscsit_send_unsolicited_nopin( | ||
2726 | struct iscsi_cmd *cmd, | ||
2727 | struct iscsi_conn *conn, | ||
2728 | int want_response) | ||
2729 | { | ||
2730 | int tx_size = ISCSI_HDR_LEN; | ||
2731 | struct iscsi_nopin *hdr; | ||
2732 | |||
2733 | hdr = (struct iscsi_nopin *) cmd->pdu; | ||
2734 | memset(hdr, 0, ISCSI_HDR_LEN); | ||
2735 | hdr->opcode = ISCSI_OP_NOOP_IN; | ||
2736 | hdr->flags |= ISCSI_FLAG_CMD_FINAL; | ||
2737 | hdr->itt = cpu_to_be32(cmd->init_task_tag); | ||
2738 | hdr->ttt = cpu_to_be32(cmd->targ_xfer_tag); | ||
2739 | cmd->stat_sn = conn->stat_sn; | ||
2740 | hdr->statsn = cpu_to_be32(cmd->stat_sn); | ||
2741 | hdr->exp_cmdsn = cpu_to_be32(conn->sess->exp_cmd_sn); | ||
2742 | hdr->max_cmdsn = cpu_to_be32(conn->sess->max_cmd_sn); | ||
2743 | |||
2744 | if (conn->conn_ops->HeaderDigest) { | ||
2745 | u32 *header_digest = (u32 *)&cmd->pdu[ISCSI_HDR_LEN]; | ||
2746 | |||
2747 | iscsit_do_crypto_hash_buf(&conn->conn_tx_hash, | ||
2748 | (unsigned char *)hdr, ISCSI_HDR_LEN, | ||
2749 | 0, NULL, (u8 *)header_digest); | ||
2750 | |||
2751 | tx_size += ISCSI_CRC_LEN; | ||
2752 | pr_debug("Attaching CRC32C HeaderDigest to" | ||
2753 | " NopIN 0x%08x\n", *header_digest); | ||
2754 | } | ||
2755 | |||
2756 | cmd->iov_misc[0].iov_base = cmd->pdu; | ||
2757 | cmd->iov_misc[0].iov_len = tx_size; | ||
2758 | cmd->iov_misc_count = 1; | ||
2759 | cmd->tx_size = tx_size; | ||
2760 | |||
2761 | pr_debug("Sending Unsolicited NOPIN TTT: 0x%08x StatSN:" | ||
2762 | " 0x%08x CID: %hu\n", hdr->ttt, cmd->stat_sn, conn->cid); | ||
2763 | |||
2764 | return 0; | ||
2765 | } | ||
2766 | |||
2767 | static int iscsit_send_nopin_response( | ||
2768 | struct iscsi_cmd *cmd, | ||
2769 | struct iscsi_conn *conn) | ||
2770 | { | ||
2771 | int niov = 0, tx_size; | ||
2772 | u32 padding = 0; | ||
2773 | struct kvec *iov; | ||
2774 | struct iscsi_nopin *hdr; | ||
2775 | |||
2776 | tx_size = ISCSI_HDR_LEN; | ||
2777 | hdr = (struct iscsi_nopin *) cmd->pdu; | ||
2778 | memset(hdr, 0, ISCSI_HDR_LEN); | ||
2779 | hdr->opcode = ISCSI_OP_NOOP_IN; | ||
2780 | hdr->flags |= ISCSI_FLAG_CMD_FINAL; | ||
2781 | hton24(hdr->dlength, cmd->buf_ptr_size); | ||
2782 | put_unaligned_le64(0xFFFFFFFFFFFFFFFFULL, &hdr->lun); | ||
2783 | hdr->itt = cpu_to_be32(cmd->init_task_tag); | ||
2784 | hdr->ttt = cpu_to_be32(cmd->targ_xfer_tag); | ||
2785 | cmd->stat_sn = conn->stat_sn++; | ||
2786 | hdr->statsn = cpu_to_be32(cmd->stat_sn); | ||
2787 | |||
2788 | iscsit_increment_maxcmdsn(cmd, conn->sess); | ||
2789 | hdr->exp_cmdsn = cpu_to_be32(conn->sess->exp_cmd_sn); | ||
2790 | hdr->max_cmdsn = cpu_to_be32(conn->sess->max_cmd_sn); | ||
2791 | |||
2792 | iov = &cmd->iov_misc[0]; | ||
2793 | iov[niov].iov_base = cmd->pdu; | ||
2794 | iov[niov++].iov_len = ISCSI_HDR_LEN; | ||
2795 | |||
2796 | if (conn->conn_ops->HeaderDigest) { | ||
2797 | u32 *header_digest = (u32 *)&cmd->pdu[ISCSI_HDR_LEN]; | ||
2798 | |||
2799 | iscsit_do_crypto_hash_buf(&conn->conn_tx_hash, | ||
2800 | (unsigned char *)hdr, ISCSI_HDR_LEN, | ||
2801 | 0, NULL, (u8 *)header_digest); | ||
2802 | |||
2803 | iov[0].iov_len += ISCSI_CRC_LEN; | ||
2804 | tx_size += ISCSI_CRC_LEN; | ||
2805 | pr_debug("Attaching CRC32C HeaderDigest" | ||
2806 | " to NopIn 0x%08x\n", *header_digest); | ||
2807 | } | ||
2808 | |||
2809 | /* | ||
2810 | * NOPOUT Ping Data is attached to struct iscsi_cmd->buf_ptr. | ||
2811 | * NOPOUT DataSegmentLength is at struct iscsi_cmd->buf_ptr_size. | ||
2812 | */ | ||
2813 | if (cmd->buf_ptr_size) { | ||
2814 | iov[niov].iov_base = cmd->buf_ptr; | ||
2815 | iov[niov++].iov_len = cmd->buf_ptr_size; | ||
2816 | tx_size += cmd->buf_ptr_size; | ||
2817 | |||
2818 | pr_debug("Echoing back %u bytes of ping" | ||
2819 | " data.\n", cmd->buf_ptr_size); | ||
2820 | |||
2821 | padding = ((-cmd->buf_ptr_size) & 3); | ||
2822 | if (padding != 0) { | ||
2823 | iov[niov].iov_base = &cmd->pad_bytes; | ||
2824 | iov[niov++].iov_len = padding; | ||
2825 | tx_size += padding; | ||
2826 | pr_debug("Attaching %u additional" | ||
2827 | " padding bytes.\n", padding); | ||
2828 | } | ||
2829 | if (conn->conn_ops->DataDigest) { | ||
2830 | iscsit_do_crypto_hash_buf(&conn->conn_tx_hash, | ||
2831 | cmd->buf_ptr, cmd->buf_ptr_size, | ||
2832 | padding, (u8 *)&cmd->pad_bytes, | ||
2833 | (u8 *)&cmd->data_crc); | ||
2834 | |||
2835 | iov[niov].iov_base = &cmd->data_crc; | ||
2836 | iov[niov++].iov_len = ISCSI_CRC_LEN; | ||
2837 | tx_size += ISCSI_CRC_LEN; | ||
2838 | pr_debug("Attached DataDigest for %u" | ||
2839 | " bytes of ping data, CRC 0x%08x\n", | ||
2840 | cmd->buf_ptr_size, cmd->data_crc); | ||
2841 | } | ||
2842 | } | ||
2843 | |||
2844 | cmd->iov_misc_count = niov; | ||
2845 | cmd->tx_size = tx_size; | ||
2846 | |||
2847 | pr_debug("Sending NOPIN Response ITT: 0x%08x, TTT:" | ||
2848 | " 0x%08x, StatSN: 0x%08x, Length %u\n", cmd->init_task_tag, | ||
2849 | cmd->targ_xfer_tag, cmd->stat_sn, cmd->buf_ptr_size); | ||
2850 | |||
2851 | return 0; | ||
2852 | } | ||
2853 | |||
2854 | int iscsit_send_r2t( | ||
2855 | struct iscsi_cmd *cmd, | ||
2856 | struct iscsi_conn *conn) | ||
2857 | { | ||
2858 | int tx_size = 0; | ||
2859 | struct iscsi_r2t *r2t; | ||
2860 | struct iscsi_r2t_rsp *hdr; | ||
2861 | |||
2862 | r2t = iscsit_get_r2t_from_list(cmd); | ||
2863 | if (!r2t) | ||
2864 | return -1; | ||
2865 | |||
2866 | hdr = (struct iscsi_r2t_rsp *) cmd->pdu; | ||
2867 | memset(hdr, 0, ISCSI_HDR_LEN); | ||
2868 | hdr->opcode = ISCSI_OP_R2T; | ||
2869 | hdr->flags |= ISCSI_FLAG_CMD_FINAL; | ||
2870 | int_to_scsilun(cmd->se_cmd.orig_fe_lun, | ||
2871 | (struct scsi_lun *)&hdr->lun); | ||
2872 | hdr->itt = cpu_to_be32(cmd->init_task_tag); | ||
2873 | spin_lock_bh(&conn->sess->ttt_lock); | ||
2874 | r2t->targ_xfer_tag = conn->sess->targ_xfer_tag++; | ||
2875 | if (r2t->targ_xfer_tag == 0xFFFFFFFF) | ||
2876 | r2t->targ_xfer_tag = conn->sess->targ_xfer_tag++; | ||
2877 | spin_unlock_bh(&conn->sess->ttt_lock); | ||
2878 | hdr->ttt = cpu_to_be32(r2t->targ_xfer_tag); | ||
2879 | hdr->statsn = cpu_to_be32(conn->stat_sn); | ||
2880 | hdr->exp_cmdsn = cpu_to_be32(conn->sess->exp_cmd_sn); | ||
2881 | hdr->max_cmdsn = cpu_to_be32(conn->sess->max_cmd_sn); | ||
2882 | hdr->r2tsn = cpu_to_be32(r2t->r2t_sn); | ||
2883 | hdr->data_offset = cpu_to_be32(r2t->offset); | ||
2884 | hdr->data_length = cpu_to_be32(r2t->xfer_len); | ||
2885 | |||
2886 | cmd->iov_misc[0].iov_base = cmd->pdu; | ||
2887 | cmd->iov_misc[0].iov_len = ISCSI_HDR_LEN; | ||
2888 | tx_size += ISCSI_HDR_LEN; | ||
2889 | |||
2890 | if (conn->conn_ops->HeaderDigest) { | ||
2891 | u32 *header_digest = (u32 *)&cmd->pdu[ISCSI_HDR_LEN]; | ||
2892 | |||
2893 | iscsit_do_crypto_hash_buf(&conn->conn_tx_hash, | ||
2894 | (unsigned char *)hdr, ISCSI_HDR_LEN, | ||
2895 | 0, NULL, (u8 *)header_digest); | ||
2896 | |||
2897 | cmd->iov_misc[0].iov_len += ISCSI_CRC_LEN; | ||
2898 | tx_size += ISCSI_CRC_LEN; | ||
2899 | pr_debug("Attaching CRC32 HeaderDigest for R2T" | ||
2900 | " PDU 0x%08x\n", *header_digest); | ||
2901 | } | ||
2902 | |||
2903 | pr_debug("Built %sR2T, ITT: 0x%08x, TTT: 0x%08x, StatSN:" | ||
2904 | " 0x%08x, R2TSN: 0x%08x, Offset: %u, DDTL: %u, CID: %hu\n", | ||
2905 | (!r2t->recovery_r2t) ? "" : "Recovery ", cmd->init_task_tag, | ||
2906 | r2t->targ_xfer_tag, ntohl(hdr->statsn), r2t->r2t_sn, | ||
2907 | r2t->offset, r2t->xfer_len, conn->cid); | ||
2908 | |||
2909 | cmd->iov_misc_count = 1; | ||
2910 | cmd->tx_size = tx_size; | ||
2911 | |||
2912 | spin_lock_bh(&cmd->r2t_lock); | ||
2913 | r2t->sent_r2t = 1; | ||
2914 | spin_unlock_bh(&cmd->r2t_lock); | ||
2915 | |||
2916 | return 0; | ||
2917 | } | ||
2918 | |||
2919 | /* | ||
2920 | * type 0: Normal Operation. | ||
2921 | * type 1: Called from Storage Transport. | ||
2922 | * type 2: Called from iscsi_task_reassign_complete_write() for | ||
2923 | * connection recovery. | ||
2924 | */ | ||
2925 | int iscsit_build_r2ts_for_cmd( | ||
2926 | struct iscsi_cmd *cmd, | ||
2927 | struct iscsi_conn *conn, | ||
2928 | int type) | ||
2929 | { | ||
2930 | int first_r2t = 1; | ||
2931 | u32 offset = 0, xfer_len = 0; | ||
2932 | |||
2933 | spin_lock_bh(&cmd->r2t_lock); | ||
2934 | if (cmd->cmd_flags & ICF_SENT_LAST_R2T) { | ||
2935 | spin_unlock_bh(&cmd->r2t_lock); | ||
2936 | return 0; | ||
2937 | } | ||
2938 | |||
2939 | if (conn->sess->sess_ops->DataSequenceInOrder && (type != 2)) | ||
2940 | if (cmd->r2t_offset < cmd->write_data_done) | ||
2941 | cmd->r2t_offset = cmd->write_data_done; | ||
2942 | |||
2943 | while (cmd->outstanding_r2ts < conn->sess->sess_ops->MaxOutstandingR2T) { | ||
2944 | if (conn->sess->sess_ops->DataSequenceInOrder) { | ||
2945 | offset = cmd->r2t_offset; | ||
2946 | |||
2947 | if (first_r2t && (type == 2)) { | ||
2948 | xfer_len = ((offset + | ||
2949 | (conn->sess->sess_ops->MaxBurstLength - | ||
2950 | cmd->next_burst_len) > | ||
2951 | cmd->data_length) ? | ||
2952 | (cmd->data_length - offset) : | ||
2953 | (conn->sess->sess_ops->MaxBurstLength - | ||
2954 | cmd->next_burst_len)); | ||
2955 | } else { | ||
2956 | xfer_len = ((offset + | ||
2957 | conn->sess->sess_ops->MaxBurstLength) > | ||
2958 | cmd->data_length) ? | ||
2959 | (cmd->data_length - offset) : | ||
2960 | conn->sess->sess_ops->MaxBurstLength; | ||
2961 | } | ||
2962 | cmd->r2t_offset += xfer_len; | ||
2963 | |||
2964 | if (cmd->r2t_offset == cmd->data_length) | ||
2965 | cmd->cmd_flags |= ICF_SENT_LAST_R2T; | ||
2966 | } else { | ||
2967 | struct iscsi_seq *seq; | ||
2968 | |||
2969 | seq = iscsit_get_seq_holder_for_r2t(cmd); | ||
2970 | if (!seq) { | ||
2971 | spin_unlock_bh(&cmd->r2t_lock); | ||
2972 | return -1; | ||
2973 | } | ||
2974 | |||
2975 | offset = seq->offset; | ||
2976 | xfer_len = seq->xfer_len; | ||
2977 | |||
2978 | if (cmd->seq_send_order == cmd->seq_count) | ||
2979 | cmd->cmd_flags |= ICF_SENT_LAST_R2T; | ||
2980 | } | ||
2981 | cmd->outstanding_r2ts++; | ||
2982 | first_r2t = 0; | ||
2983 | |||
2984 | if (iscsit_add_r2t_to_list(cmd, offset, xfer_len, 0, 0) < 0) { | ||
2985 | spin_unlock_bh(&cmd->r2t_lock); | ||
2986 | return -1; | ||
2987 | } | ||
2988 | |||
2989 | if (cmd->cmd_flags & ICF_SENT_LAST_R2T) | ||
2990 | break; | ||
2991 | } | ||
2992 | spin_unlock_bh(&cmd->r2t_lock); | ||
2993 | |||
2994 | return 0; | ||
2995 | } | ||
2996 | |||
2997 | static int iscsit_send_status( | ||
2998 | struct iscsi_cmd *cmd, | ||
2999 | struct iscsi_conn *conn) | ||
3000 | { | ||
3001 | u8 iov_count = 0, recovery; | ||
3002 | u32 padding = 0, tx_size = 0; | ||
3003 | struct iscsi_scsi_rsp *hdr; | ||
3004 | struct kvec *iov; | ||
3005 | |||
3006 | recovery = (cmd->i_state != ISTATE_SEND_STATUS); | ||
3007 | if (!recovery) | ||
3008 | cmd->stat_sn = conn->stat_sn++; | ||
3009 | |||
3010 | spin_lock_bh(&conn->sess->session_stats_lock); | ||
3011 | conn->sess->rsp_pdus++; | ||
3012 | spin_unlock_bh(&conn->sess->session_stats_lock); | ||
3013 | |||
3014 | hdr = (struct iscsi_scsi_rsp *) cmd->pdu; | ||
3015 | memset(hdr, 0, ISCSI_HDR_LEN); | ||
3016 | hdr->opcode = ISCSI_OP_SCSI_CMD_RSP; | ||
3017 | hdr->flags |= ISCSI_FLAG_CMD_FINAL; | ||
3018 | if (cmd->se_cmd.se_cmd_flags & SCF_OVERFLOW_BIT) { | ||
3019 | hdr->flags |= ISCSI_FLAG_CMD_OVERFLOW; | ||
3020 | hdr->residual_count = cpu_to_be32(cmd->residual_count); | ||
3021 | } else if (cmd->se_cmd.se_cmd_flags & SCF_UNDERFLOW_BIT) { | ||
3022 | hdr->flags |= ISCSI_FLAG_CMD_UNDERFLOW; | ||
3023 | hdr->residual_count = cpu_to_be32(cmd->residual_count); | ||
3024 | } | ||
3025 | hdr->response = cmd->iscsi_response; | ||
3026 | hdr->cmd_status = cmd->se_cmd.scsi_status; | ||
3027 | hdr->itt = cpu_to_be32(cmd->init_task_tag); | ||
3028 | hdr->statsn = cpu_to_be32(cmd->stat_sn); | ||
3029 | |||
3030 | iscsit_increment_maxcmdsn(cmd, conn->sess); | ||
3031 | hdr->exp_cmdsn = cpu_to_be32(conn->sess->exp_cmd_sn); | ||
3032 | hdr->max_cmdsn = cpu_to_be32(conn->sess->max_cmd_sn); | ||
3033 | |||
3034 | iov = &cmd->iov_misc[0]; | ||
3035 | iov[iov_count].iov_base = cmd->pdu; | ||
3036 | iov[iov_count++].iov_len = ISCSI_HDR_LEN; | ||
3037 | tx_size += ISCSI_HDR_LEN; | ||
3038 | |||
3039 | /* | ||
3040 | * Attach SENSE DATA payload to iSCSI Response PDU | ||
3041 | */ | ||
3042 | if (cmd->se_cmd.sense_buffer && | ||
3043 | ((cmd->se_cmd.se_cmd_flags & SCF_TRANSPORT_TASK_SENSE) || | ||
3044 | (cmd->se_cmd.se_cmd_flags & SCF_EMULATED_TASK_SENSE))) { | ||
3045 | padding = -(cmd->se_cmd.scsi_sense_length) & 3; | ||
3046 | hton24(hdr->dlength, cmd->se_cmd.scsi_sense_length); | ||
3047 | iov[iov_count].iov_base = cmd->se_cmd.sense_buffer; | ||
3048 | iov[iov_count++].iov_len = | ||
3049 | (cmd->se_cmd.scsi_sense_length + padding); | ||
3050 | tx_size += cmd->se_cmd.scsi_sense_length; | ||
3051 | |||
3052 | if (padding) { | ||
3053 | memset(cmd->se_cmd.sense_buffer + | ||
3054 | cmd->se_cmd.scsi_sense_length, 0, padding); | ||
3055 | tx_size += padding; | ||
3056 | pr_debug("Adding %u bytes of padding to" | ||
3057 | " SENSE.\n", padding); | ||
3058 | } | ||
3059 | |||
3060 | if (conn->conn_ops->DataDigest) { | ||
3061 | iscsit_do_crypto_hash_buf(&conn->conn_tx_hash, | ||
3062 | cmd->se_cmd.sense_buffer, | ||
3063 | (cmd->se_cmd.scsi_sense_length + padding), | ||
3064 | 0, NULL, (u8 *)&cmd->data_crc); | ||
3065 | |||
3066 | iov[iov_count].iov_base = &cmd->data_crc; | ||
3067 | iov[iov_count++].iov_len = ISCSI_CRC_LEN; | ||
3068 | tx_size += ISCSI_CRC_LEN; | ||
3069 | |||
3070 | pr_debug("Attaching CRC32 DataDigest for" | ||
3071 | " SENSE, %u bytes CRC 0x%08x\n", | ||
3072 | (cmd->se_cmd.scsi_sense_length + padding), | ||
3073 | cmd->data_crc); | ||
3074 | } | ||
3075 | |||
3076 | pr_debug("Attaching SENSE DATA: %u bytes to iSCSI" | ||
3077 | " Response PDU\n", | ||
3078 | cmd->se_cmd.scsi_sense_length); | ||
3079 | } | ||
3080 | |||
3081 | if (conn->conn_ops->HeaderDigest) { | ||
3082 | u32 *header_digest = (u32 *)&cmd->pdu[ISCSI_HDR_LEN]; | ||
3083 | |||
3084 | iscsit_do_crypto_hash_buf(&conn->conn_tx_hash, | ||
3085 | (unsigned char *)hdr, ISCSI_HDR_LEN, | ||
3086 | 0, NULL, (u8 *)header_digest); | ||
3087 | |||
3088 | iov[0].iov_len += ISCSI_CRC_LEN; | ||
3089 | tx_size += ISCSI_CRC_LEN; | ||
3090 | pr_debug("Attaching CRC32 HeaderDigest for Response" | ||
3091 | " PDU 0x%08x\n", *header_digest); | ||
3092 | } | ||
3093 | |||
3094 | cmd->iov_misc_count = iov_count; | ||
3095 | cmd->tx_size = tx_size; | ||
3096 | |||
3097 | pr_debug("Built %sSCSI Response, ITT: 0x%08x, StatSN: 0x%08x," | ||
3098 | " Response: 0x%02x, SAM Status: 0x%02x, CID: %hu\n", | ||
3099 | (!recovery) ? "" : "Recovery ", cmd->init_task_tag, | ||
3100 | cmd->stat_sn, 0x00, cmd->se_cmd.scsi_status, conn->cid); | ||
3101 | |||
3102 | return 0; | ||
3103 | } | ||
3104 | |||
3105 | static u8 iscsit_convert_tcm_tmr_rsp(struct se_tmr_req *se_tmr) | ||
3106 | { | ||
3107 | switch (se_tmr->response) { | ||
3108 | case TMR_FUNCTION_COMPLETE: | ||
3109 | return ISCSI_TMF_RSP_COMPLETE; | ||
3110 | case TMR_TASK_DOES_NOT_EXIST: | ||
3111 | return ISCSI_TMF_RSP_NO_TASK; | ||
3112 | case TMR_LUN_DOES_NOT_EXIST: | ||
3113 | return ISCSI_TMF_RSP_NO_LUN; | ||
3114 | case TMR_TASK_MGMT_FUNCTION_NOT_SUPPORTED: | ||
3115 | return ISCSI_TMF_RSP_NOT_SUPPORTED; | ||
3116 | case TMR_FUNCTION_AUTHORIZATION_FAILED: | ||
3117 | return ISCSI_TMF_RSP_AUTH_FAILED; | ||
3118 | case TMR_FUNCTION_REJECTED: | ||
3119 | default: | ||
3120 | return ISCSI_TMF_RSP_REJECTED; | ||
3121 | } | ||
3122 | } | ||
3123 | |||
3124 | static int iscsit_send_task_mgt_rsp( | ||
3125 | struct iscsi_cmd *cmd, | ||
3126 | struct iscsi_conn *conn) | ||
3127 | { | ||
3128 | struct se_tmr_req *se_tmr = cmd->se_cmd.se_tmr_req; | ||
3129 | struct iscsi_tm_rsp *hdr; | ||
3130 | u32 tx_size = 0; | ||
3131 | |||
3132 | hdr = (struct iscsi_tm_rsp *) cmd->pdu; | ||
3133 | memset(hdr, 0, ISCSI_HDR_LEN); | ||
3134 | hdr->opcode = ISCSI_OP_SCSI_TMFUNC_RSP; | ||
3135 | hdr->response = iscsit_convert_tcm_tmr_rsp(se_tmr); | ||
3136 | hdr->itt = cpu_to_be32(cmd->init_task_tag); | ||
3137 | cmd->stat_sn = conn->stat_sn++; | ||
3138 | hdr->statsn = cpu_to_be32(cmd->stat_sn); | ||
3139 | |||
3140 | iscsit_increment_maxcmdsn(cmd, conn->sess); | ||
3141 | hdr->exp_cmdsn = cpu_to_be32(conn->sess->exp_cmd_sn); | ||
3142 | hdr->max_cmdsn = cpu_to_be32(conn->sess->max_cmd_sn); | ||
3143 | |||
3144 | cmd->iov_misc[0].iov_base = cmd->pdu; | ||
3145 | cmd->iov_misc[0].iov_len = ISCSI_HDR_LEN; | ||
3146 | tx_size += ISCSI_HDR_LEN; | ||
3147 | |||
3148 | if (conn->conn_ops->HeaderDigest) { | ||
3149 | u32 *header_digest = (u32 *)&cmd->pdu[ISCSI_HDR_LEN]; | ||
3150 | |||
3151 | iscsit_do_crypto_hash_buf(&conn->conn_tx_hash, | ||
3152 | (unsigned char *)hdr, ISCSI_HDR_LEN, | ||
3153 | 0, NULL, (u8 *)header_digest); | ||
3154 | |||
3155 | cmd->iov_misc[0].iov_len += ISCSI_CRC_LEN; | ||
3156 | tx_size += ISCSI_CRC_LEN; | ||
3157 | pr_debug("Attaching CRC32 HeaderDigest for Task" | ||
3158 | " Mgmt Response PDU 0x%08x\n", *header_digest); | ||
3159 | } | ||
3160 | |||
3161 | cmd->iov_misc_count = 1; | ||
3162 | cmd->tx_size = tx_size; | ||
3163 | |||
3164 | pr_debug("Built Task Management Response ITT: 0x%08x," | ||
3165 | " StatSN: 0x%08x, Response: 0x%02x, CID: %hu\n", | ||
3166 | cmd->init_task_tag, cmd->stat_sn, hdr->response, conn->cid); | ||
3167 | |||
3168 | return 0; | ||
3169 | } | ||
3170 | |||
3171 | static int iscsit_build_sendtargets_response(struct iscsi_cmd *cmd) | ||
3172 | { | ||
3173 | char *payload = NULL; | ||
3174 | struct iscsi_conn *conn = cmd->conn; | ||
3175 | struct iscsi_portal_group *tpg; | ||
3176 | struct iscsi_tiqn *tiqn; | ||
3177 | struct iscsi_tpg_np *tpg_np; | ||
3178 | int buffer_len, end_of_buf = 0, len = 0, payload_len = 0; | ||
3179 | unsigned char buf[256]; | ||
3180 | |||
3181 | buffer_len = (conn->conn_ops->MaxRecvDataSegmentLength > 32768) ? | ||
3182 | 32768 : conn->conn_ops->MaxRecvDataSegmentLength; | ||
3183 | |||
3184 | memset(buf, 0, 256); | ||
3185 | |||
3186 | payload = kzalloc(buffer_len, GFP_KERNEL); | ||
3187 | if (!payload) { | ||
3188 | pr_err("Unable to allocate memory for sendtargets" | ||
3189 | " response.\n"); | ||
3190 | return -ENOMEM; | ||
3191 | } | ||
3192 | |||
3193 | spin_lock(&tiqn_lock); | ||
3194 | list_for_each_entry(tiqn, &g_tiqn_list, tiqn_list) { | ||
3195 | len = sprintf(buf, "TargetName=%s", tiqn->tiqn); | ||
3196 | len += 1; | ||
3197 | |||
3198 | if ((len + payload_len) > buffer_len) { | ||
3199 | spin_unlock(&tiqn->tiqn_tpg_lock); | ||
3200 | end_of_buf = 1; | ||
3201 | goto eob; | ||
3202 | } | ||
3203 | memcpy((void *)payload + payload_len, buf, len); | ||
3204 | payload_len += len; | ||
3205 | |||
3206 | spin_lock(&tiqn->tiqn_tpg_lock); | ||
3207 | list_for_each_entry(tpg, &tiqn->tiqn_tpg_list, tpg_list) { | ||
3208 | |||
3209 | spin_lock(&tpg->tpg_state_lock); | ||
3210 | if ((tpg->tpg_state == TPG_STATE_FREE) || | ||
3211 | (tpg->tpg_state == TPG_STATE_INACTIVE)) { | ||
3212 | spin_unlock(&tpg->tpg_state_lock); | ||
3213 | continue; | ||
3214 | } | ||
3215 | spin_unlock(&tpg->tpg_state_lock); | ||
3216 | |||
3217 | spin_lock(&tpg->tpg_np_lock); | ||
3218 | list_for_each_entry(tpg_np, &tpg->tpg_gnp_list, | ||
3219 | tpg_np_list) { | ||
3220 | len = sprintf(buf, "TargetAddress=" | ||
3221 | "%s%s%s:%hu,%hu", | ||
3222 | (tpg_np->tpg_np->np_sockaddr.ss_family == AF_INET6) ? | ||
3223 | "[" : "", tpg_np->tpg_np->np_ip, | ||
3224 | (tpg_np->tpg_np->np_sockaddr.ss_family == AF_INET6) ? | ||
3225 | "]" : "", tpg_np->tpg_np->np_port, | ||
3226 | tpg->tpgt); | ||
3227 | len += 1; | ||
3228 | |||
3229 | if ((len + payload_len) > buffer_len) { | ||
3230 | spin_unlock(&tpg->tpg_np_lock); | ||
3231 | spin_unlock(&tiqn->tiqn_tpg_lock); | ||
3232 | end_of_buf = 1; | ||
3233 | goto eob; | ||
3234 | } | ||
3235 | memcpy((void *)payload + payload_len, buf, len); | ||
3236 | payload_len += len; | ||
3237 | } | ||
3238 | spin_unlock(&tpg->tpg_np_lock); | ||
3239 | } | ||
3240 | spin_unlock(&tiqn->tiqn_tpg_lock); | ||
3241 | eob: | ||
3242 | if (end_of_buf) | ||
3243 | break; | ||
3244 | } | ||
3245 | spin_unlock(&tiqn_lock); | ||
3246 | |||
3247 | cmd->buf_ptr = payload; | ||
3248 | |||
3249 | return payload_len; | ||
3250 | } | ||
3251 | |||
3252 | /* | ||
3253 | * FIXME: Add support for F_BIT and C_BIT when the length is longer than | ||
3254 | * MaxRecvDataSegmentLength. | ||
3255 | */ | ||
3256 | static int iscsit_send_text_rsp( | ||
3257 | struct iscsi_cmd *cmd, | ||
3258 | struct iscsi_conn *conn) | ||
3259 | { | ||
3260 | struct iscsi_text_rsp *hdr; | ||
3261 | struct kvec *iov; | ||
3262 | u32 padding = 0, tx_size = 0; | ||
3263 | int text_length, iov_count = 0; | ||
3264 | |||
3265 | text_length = iscsit_build_sendtargets_response(cmd); | ||
3266 | if (text_length < 0) | ||
3267 | return text_length; | ||
3268 | |||
3269 | padding = ((-text_length) & 3); | ||
3270 | if (padding != 0) { | ||
3271 | memset(cmd->buf_ptr + text_length, 0, padding); | ||
3272 | pr_debug("Attaching %u additional bytes for" | ||
3273 | " padding.\n", padding); | ||
3274 | } | ||
3275 | |||
3276 | hdr = (struct iscsi_text_rsp *) cmd->pdu; | ||
3277 | memset(hdr, 0, ISCSI_HDR_LEN); | ||
3278 | hdr->opcode = ISCSI_OP_TEXT_RSP; | ||
3279 | hdr->flags |= ISCSI_FLAG_CMD_FINAL; | ||
3280 | hton24(hdr->dlength, text_length); | ||
3281 | hdr->itt = cpu_to_be32(cmd->init_task_tag); | ||
3282 | hdr->ttt = cpu_to_be32(cmd->targ_xfer_tag); | ||
3283 | cmd->stat_sn = conn->stat_sn++; | ||
3284 | hdr->statsn = cpu_to_be32(cmd->stat_sn); | ||
3285 | |||
3286 | iscsit_increment_maxcmdsn(cmd, conn->sess); | ||
3287 | hdr->exp_cmdsn = cpu_to_be32(conn->sess->exp_cmd_sn); | ||
3288 | hdr->max_cmdsn = cpu_to_be32(conn->sess->max_cmd_sn); | ||
3289 | |||
3290 | iov = &cmd->iov_misc[0]; | ||
3291 | |||
3292 | iov[iov_count].iov_base = cmd->pdu; | ||
3293 | iov[iov_count++].iov_len = ISCSI_HDR_LEN; | ||
3294 | iov[iov_count].iov_base = cmd->buf_ptr; | ||
3295 | iov[iov_count++].iov_len = text_length + padding; | ||
3296 | |||
3297 | tx_size += (ISCSI_HDR_LEN + text_length + padding); | ||
3298 | |||
3299 | if (conn->conn_ops->HeaderDigest) { | ||
3300 | u32 *header_digest = (u32 *)&cmd->pdu[ISCSI_HDR_LEN]; | ||
3301 | |||
3302 | iscsit_do_crypto_hash_buf(&conn->conn_tx_hash, | ||
3303 | (unsigned char *)hdr, ISCSI_HDR_LEN, | ||
3304 | 0, NULL, (u8 *)header_digest); | ||
3305 | |||
3306 | iov[0].iov_len += ISCSI_CRC_LEN; | ||
3307 | tx_size += ISCSI_CRC_LEN; | ||
3308 | pr_debug("Attaching CRC32 HeaderDigest for" | ||
3309 | " Text Response PDU 0x%08x\n", *header_digest); | ||
3310 | } | ||
3311 | |||
3312 | if (conn->conn_ops->DataDigest) { | ||
3313 | iscsit_do_crypto_hash_buf(&conn->conn_tx_hash, | ||
3314 | cmd->buf_ptr, (text_length + padding), | ||
3315 | 0, NULL, (u8 *)&cmd->data_crc); | ||
3316 | |||
3317 | iov[iov_count].iov_base = &cmd->data_crc; | ||
3318 | iov[iov_count++].iov_len = ISCSI_CRC_LEN; | ||
3319 | tx_size += ISCSI_CRC_LEN; | ||
3320 | |||
3321 | pr_debug("Attaching DataDigest for %u bytes of text" | ||
3322 | " data, CRC 0x%08x\n", (text_length + padding), | ||
3323 | cmd->data_crc); | ||
3324 | } | ||
3325 | |||
3326 | cmd->iov_misc_count = iov_count; | ||
3327 | cmd->tx_size = tx_size; | ||
3328 | |||
3329 | pr_debug("Built Text Response: ITT: 0x%08x, StatSN: 0x%08x," | ||
3330 | " Length: %u, CID: %hu\n", cmd->init_task_tag, cmd->stat_sn, | ||
3331 | text_length, conn->cid); | ||
3332 | return 0; | ||
3333 | } | ||
3334 | |||
3335 | static int iscsit_send_reject( | ||
3336 | struct iscsi_cmd *cmd, | ||
3337 | struct iscsi_conn *conn) | ||
3338 | { | ||
3339 | u32 iov_count = 0, tx_size = 0; | ||
3340 | struct iscsi_reject *hdr; | ||
3341 | struct kvec *iov; | ||
3342 | |||
3343 | hdr = (struct iscsi_reject *) cmd->pdu; | ||
3344 | hdr->opcode = ISCSI_OP_REJECT; | ||
3345 | hdr->flags |= ISCSI_FLAG_CMD_FINAL; | ||
3346 | hton24(hdr->dlength, ISCSI_HDR_LEN); | ||
3347 | cmd->stat_sn = conn->stat_sn++; | ||
3348 | hdr->statsn = cpu_to_be32(cmd->stat_sn); | ||
3349 | hdr->exp_cmdsn = cpu_to_be32(conn->sess->exp_cmd_sn); | ||
3350 | hdr->max_cmdsn = cpu_to_be32(conn->sess->max_cmd_sn); | ||
3351 | |||
3352 | iov = &cmd->iov_misc[0]; | ||
3353 | |||
3354 | iov[iov_count].iov_base = cmd->pdu; | ||
3355 | iov[iov_count++].iov_len = ISCSI_HDR_LEN; | ||
3356 | iov[iov_count].iov_base = cmd->buf_ptr; | ||
3357 | iov[iov_count++].iov_len = ISCSI_HDR_LEN; | ||
3358 | |||
3359 | tx_size = (ISCSI_HDR_LEN + ISCSI_HDR_LEN); | ||
3360 | |||
3361 | if (conn->conn_ops->HeaderDigest) { | ||
3362 | u32 *header_digest = (u32 *)&cmd->pdu[ISCSI_HDR_LEN]; | ||
3363 | |||
3364 | iscsit_do_crypto_hash_buf(&conn->conn_tx_hash, | ||
3365 | (unsigned char *)hdr, ISCSI_HDR_LEN, | ||
3366 | 0, NULL, (u8 *)header_digest); | ||
3367 | |||
3368 | iov[0].iov_len += ISCSI_CRC_LEN; | ||
3369 | tx_size += ISCSI_CRC_LEN; | ||
3370 | pr_debug("Attaching CRC32 HeaderDigest for" | ||
3371 | " REJECT PDU 0x%08x\n", *header_digest); | ||
3372 | } | ||
3373 | |||
3374 | if (conn->conn_ops->DataDigest) { | ||
3375 | iscsit_do_crypto_hash_buf(&conn->conn_tx_hash, | ||
3376 | (unsigned char *)cmd->buf_ptr, ISCSI_HDR_LEN, | ||
3377 | 0, NULL, (u8 *)&cmd->data_crc); | ||
3378 | |||
3379 | iov[iov_count].iov_base = &cmd->data_crc; | ||
3380 | iov[iov_count++].iov_len = ISCSI_CRC_LEN; | ||
3381 | tx_size += ISCSI_CRC_LEN; | ||
3382 | pr_debug("Attaching CRC32 DataDigest for REJECT" | ||
3383 | " PDU 0x%08x\n", cmd->data_crc); | ||
3384 | } | ||
3385 | |||
3386 | cmd->iov_misc_count = iov_count; | ||
3387 | cmd->tx_size = tx_size; | ||
3388 | |||
3389 | pr_debug("Built Reject PDU StatSN: 0x%08x, Reason: 0x%02x," | ||
3390 | " CID: %hu\n", ntohl(hdr->statsn), hdr->reason, conn->cid); | ||
3391 | |||
3392 | return 0; | ||
3393 | } | ||
3394 | |||
3395 | static void iscsit_tx_thread_wait_for_tcp(struct iscsi_conn *conn) | ||
3396 | { | ||
3397 | if ((conn->sock->sk->sk_shutdown & SEND_SHUTDOWN) || | ||
3398 | (conn->sock->sk->sk_shutdown & RCV_SHUTDOWN)) { | ||
3399 | wait_for_completion_interruptible_timeout( | ||
3400 | &conn->tx_half_close_comp, | ||
3401 | ISCSI_TX_THREAD_TCP_TIMEOUT * HZ); | ||
3402 | } | ||
3403 | } | ||
3404 | |||
3405 | #ifdef CONFIG_SMP | ||
3406 | |||
3407 | void iscsit_thread_get_cpumask(struct iscsi_conn *conn) | ||
3408 | { | ||
3409 | struct iscsi_thread_set *ts = conn->thread_set; | ||
3410 | int ord, cpu; | ||
3411 | /* | ||
3412 | * thread_id is assigned from iscsit_global->ts_bitmap from | ||
3413 | * within iscsi_thread_set.c:iscsi_allocate_thread_sets() | ||
3414 | * | ||
3415 | * Here we use thread_id to determine which CPU that this | ||
3416 | * iSCSI connection's iscsi_thread_set will be scheduled to | ||
3417 | * execute upon. | ||
3418 | */ | ||
3419 | ord = ts->thread_id % cpumask_weight(cpu_online_mask); | ||
3420 | #if 0 | ||
3421 | pr_debug(">>>>>>>>>>>>>>>>>>>> Generated ord: %d from" | ||
3422 | " thread_id: %d\n", ord, ts->thread_id); | ||
3423 | #endif | ||
3424 | for_each_online_cpu(cpu) { | ||
3425 | if (ord-- == 0) { | ||
3426 | cpumask_set_cpu(cpu, conn->conn_cpumask); | ||
3427 | return; | ||
3428 | } | ||
3429 | } | ||
3430 | /* | ||
3431 | * This should never be reached.. | ||
3432 | */ | ||
3433 | dump_stack(); | ||
3434 | cpumask_setall(conn->conn_cpumask); | ||
3435 | } | ||
3436 | |||
3437 | static inline void iscsit_thread_check_cpumask( | ||
3438 | struct iscsi_conn *conn, | ||
3439 | struct task_struct *p, | ||
3440 | int mode) | ||
3441 | { | ||
3442 | char buf[128]; | ||
3443 | /* | ||
3444 | * mode == 1 signals iscsi_target_tx_thread() usage. | ||
3445 | * mode == 0 signals iscsi_target_rx_thread() usage. | ||
3446 | */ | ||
3447 | if (mode == 1) { | ||
3448 | if (!conn->conn_tx_reset_cpumask) | ||
3449 | return; | ||
3450 | conn->conn_tx_reset_cpumask = 0; | ||
3451 | } else { | ||
3452 | if (!conn->conn_rx_reset_cpumask) | ||
3453 | return; | ||
3454 | conn->conn_rx_reset_cpumask = 0; | ||
3455 | } | ||
3456 | /* | ||
3457 | * Update the CPU mask for this single kthread so that | ||
3458 | * both TX and RX kthreads are scheduled to run on the | ||
3459 | * same CPU. | ||
3460 | */ | ||
3461 | memset(buf, 0, 128); | ||
3462 | cpumask_scnprintf(buf, 128, conn->conn_cpumask); | ||
3463 | #if 0 | ||
3464 | pr_debug(">>>>>>>>>>>>>> Calling set_cpus_allowed_ptr():" | ||
3465 | " %s for %s\n", buf, p->comm); | ||
3466 | #endif | ||
3467 | set_cpus_allowed_ptr(p, conn->conn_cpumask); | ||
3468 | } | ||
3469 | |||
3470 | #else | ||
3471 | #define iscsit_thread_get_cpumask(X) ({}) | ||
3472 | #define iscsit_thread_check_cpumask(X, Y, Z) ({}) | ||
3473 | #endif /* CONFIG_SMP */ | ||
3474 | |||
3475 | int iscsi_target_tx_thread(void *arg) | ||
3476 | { | ||
3477 | u8 state; | ||
3478 | int eodr = 0; | ||
3479 | int ret = 0; | ||
3480 | int sent_status = 0; | ||
3481 | int use_misc = 0; | ||
3482 | int map_sg = 0; | ||
3483 | struct iscsi_cmd *cmd = NULL; | ||
3484 | struct iscsi_conn *conn; | ||
3485 | struct iscsi_queue_req *qr = NULL; | ||
3486 | struct se_cmd *se_cmd; | ||
3487 | struct iscsi_thread_set *ts = (struct iscsi_thread_set *)arg; | ||
3488 | /* | ||
3489 | * Allow ourselves to be interrupted by SIGINT so that a | ||
3490 | * connection recovery / failure event can be triggered externally. | ||
3491 | */ | ||
3492 | allow_signal(SIGINT); | ||
3493 | |||
3494 | restart: | ||
3495 | conn = iscsi_tx_thread_pre_handler(ts); | ||
3496 | if (!conn) | ||
3497 | goto out; | ||
3498 | |||
3499 | eodr = map_sg = ret = sent_status = use_misc = 0; | ||
3500 | |||
3501 | while (!kthread_should_stop()) { | ||
3502 | /* | ||
3503 | * Ensure that both TX and RX per connection kthreads | ||
3504 | * are scheduled to run on the same CPU. | ||
3505 | */ | ||
3506 | iscsit_thread_check_cpumask(conn, current, 1); | ||
3507 | |||
3508 | schedule_timeout_interruptible(MAX_SCHEDULE_TIMEOUT); | ||
3509 | |||
3510 | if ((ts->status == ISCSI_THREAD_SET_RESET) || | ||
3511 | signal_pending(current)) | ||
3512 | goto transport_err; | ||
3513 | |||
3514 | get_immediate: | ||
3515 | qr = iscsit_get_cmd_from_immediate_queue(conn); | ||
3516 | if (qr) { | ||
3517 | atomic_set(&conn->check_immediate_queue, 0); | ||
3518 | cmd = qr->cmd; | ||
3519 | state = qr->state; | ||
3520 | kmem_cache_free(lio_qr_cache, qr); | ||
3521 | |||
3522 | spin_lock_bh(&cmd->istate_lock); | ||
3523 | switch (state) { | ||
3524 | case ISTATE_SEND_R2T: | ||
3525 | spin_unlock_bh(&cmd->istate_lock); | ||
3526 | ret = iscsit_send_r2t(cmd, conn); | ||
3527 | break; | ||
3528 | case ISTATE_REMOVE: | ||
3529 | spin_unlock_bh(&cmd->istate_lock); | ||
3530 | |||
3531 | if (cmd->data_direction == DMA_TO_DEVICE) | ||
3532 | iscsit_stop_dataout_timer(cmd); | ||
3533 | |||
3534 | spin_lock_bh(&conn->cmd_lock); | ||
3535 | list_del(&cmd->i_list); | ||
3536 | spin_unlock_bh(&conn->cmd_lock); | ||
3537 | /* | ||
3538 | * Determine if a struct se_cmd is assoicated with | ||
3539 | * this struct iscsi_cmd. | ||
3540 | */ | ||
3541 | if (!(cmd->se_cmd.se_cmd_flags & SCF_SE_LUN_CMD) && | ||
3542 | !(cmd->tmr_req)) | ||
3543 | iscsit_release_cmd(cmd); | ||
3544 | else | ||
3545 | transport_generic_free_cmd(&cmd->se_cmd, | ||
3546 | 1, 0); | ||
3547 | goto get_immediate; | ||
3548 | case ISTATE_SEND_NOPIN_WANT_RESPONSE: | ||
3549 | spin_unlock_bh(&cmd->istate_lock); | ||
3550 | iscsit_mod_nopin_response_timer(conn); | ||
3551 | ret = iscsit_send_unsolicited_nopin(cmd, | ||
3552 | conn, 1); | ||
3553 | break; | ||
3554 | case ISTATE_SEND_NOPIN_NO_RESPONSE: | ||
3555 | spin_unlock_bh(&cmd->istate_lock); | ||
3556 | ret = iscsit_send_unsolicited_nopin(cmd, | ||
3557 | conn, 0); | ||
3558 | break; | ||
3559 | default: | ||
3560 | pr_err("Unknown Opcode: 0x%02x ITT:" | ||
3561 | " 0x%08x, i_state: %d on CID: %hu\n", | ||
3562 | cmd->iscsi_opcode, cmd->init_task_tag, state, | ||
3563 | conn->cid); | ||
3564 | spin_unlock_bh(&cmd->istate_lock); | ||
3565 | goto transport_err; | ||
3566 | } | ||
3567 | if (ret < 0) { | ||
3568 | conn->tx_immediate_queue = 0; | ||
3569 | goto transport_err; | ||
3570 | } | ||
3571 | |||
3572 | if (iscsit_send_tx_data(cmd, conn, 1) < 0) { | ||
3573 | conn->tx_immediate_queue = 0; | ||
3574 | iscsit_tx_thread_wait_for_tcp(conn); | ||
3575 | goto transport_err; | ||
3576 | } | ||
3577 | |||
3578 | spin_lock_bh(&cmd->istate_lock); | ||
3579 | switch (state) { | ||
3580 | case ISTATE_SEND_R2T: | ||
3581 | spin_unlock_bh(&cmd->istate_lock); | ||
3582 | spin_lock_bh(&cmd->dataout_timeout_lock); | ||
3583 | iscsit_start_dataout_timer(cmd, conn); | ||
3584 | spin_unlock_bh(&cmd->dataout_timeout_lock); | ||
3585 | break; | ||
3586 | case ISTATE_SEND_NOPIN_WANT_RESPONSE: | ||
3587 | cmd->i_state = ISTATE_SENT_NOPIN_WANT_RESPONSE; | ||
3588 | spin_unlock_bh(&cmd->istate_lock); | ||
3589 | break; | ||
3590 | case ISTATE_SEND_NOPIN_NO_RESPONSE: | ||
3591 | cmd->i_state = ISTATE_SENT_STATUS; | ||
3592 | spin_unlock_bh(&cmd->istate_lock); | ||
3593 | break; | ||
3594 | default: | ||
3595 | pr_err("Unknown Opcode: 0x%02x ITT:" | ||
3596 | " 0x%08x, i_state: %d on CID: %hu\n", | ||
3597 | cmd->iscsi_opcode, cmd->init_task_tag, | ||
3598 | state, conn->cid); | ||
3599 | spin_unlock_bh(&cmd->istate_lock); | ||
3600 | goto transport_err; | ||
3601 | } | ||
3602 | goto get_immediate; | ||
3603 | } else | ||
3604 | conn->tx_immediate_queue = 0; | ||
3605 | |||
3606 | get_response: | ||
3607 | qr = iscsit_get_cmd_from_response_queue(conn); | ||
3608 | if (qr) { | ||
3609 | cmd = qr->cmd; | ||
3610 | state = qr->state; | ||
3611 | kmem_cache_free(lio_qr_cache, qr); | ||
3612 | |||
3613 | spin_lock_bh(&cmd->istate_lock); | ||
3614 | check_rsp_state: | ||
3615 | switch (state) { | ||
3616 | case ISTATE_SEND_DATAIN: | ||
3617 | spin_unlock_bh(&cmd->istate_lock); | ||
3618 | ret = iscsit_send_data_in(cmd, conn, | ||
3619 | &eodr); | ||
3620 | map_sg = 1; | ||
3621 | break; | ||
3622 | case ISTATE_SEND_STATUS: | ||
3623 | case ISTATE_SEND_STATUS_RECOVERY: | ||
3624 | spin_unlock_bh(&cmd->istate_lock); | ||
3625 | use_misc = 1; | ||
3626 | ret = iscsit_send_status(cmd, conn); | ||
3627 | break; | ||
3628 | case ISTATE_SEND_LOGOUTRSP: | ||
3629 | spin_unlock_bh(&cmd->istate_lock); | ||
3630 | use_misc = 1; | ||
3631 | ret = iscsit_send_logout_response(cmd, conn); | ||
3632 | break; | ||
3633 | case ISTATE_SEND_ASYNCMSG: | ||
3634 | spin_unlock_bh(&cmd->istate_lock); | ||
3635 | use_misc = 1; | ||
3636 | ret = iscsit_send_conn_drop_async_message( | ||
3637 | cmd, conn); | ||
3638 | break; | ||
3639 | case ISTATE_SEND_NOPIN: | ||
3640 | spin_unlock_bh(&cmd->istate_lock); | ||
3641 | use_misc = 1; | ||
3642 | ret = iscsit_send_nopin_response(cmd, conn); | ||
3643 | break; | ||
3644 | case ISTATE_SEND_REJECT: | ||
3645 | spin_unlock_bh(&cmd->istate_lock); | ||
3646 | use_misc = 1; | ||
3647 | ret = iscsit_send_reject(cmd, conn); | ||
3648 | break; | ||
3649 | case ISTATE_SEND_TASKMGTRSP: | ||
3650 | spin_unlock_bh(&cmd->istate_lock); | ||
3651 | use_misc = 1; | ||
3652 | ret = iscsit_send_task_mgt_rsp(cmd, conn); | ||
3653 | if (ret != 0) | ||
3654 | break; | ||
3655 | ret = iscsit_tmr_post_handler(cmd, conn); | ||
3656 | if (ret != 0) | ||
3657 | iscsit_fall_back_to_erl0(conn->sess); | ||
3658 | break; | ||
3659 | case ISTATE_SEND_TEXTRSP: | ||
3660 | spin_unlock_bh(&cmd->istate_lock); | ||
3661 | use_misc = 1; | ||
3662 | ret = iscsit_send_text_rsp(cmd, conn); | ||
3663 | break; | ||
3664 | default: | ||
3665 | pr_err("Unknown Opcode: 0x%02x ITT:" | ||
3666 | " 0x%08x, i_state: %d on CID: %hu\n", | ||
3667 | cmd->iscsi_opcode, cmd->init_task_tag, | ||
3668 | state, conn->cid); | ||
3669 | spin_unlock_bh(&cmd->istate_lock); | ||
3670 | goto transport_err; | ||
3671 | } | ||
3672 | if (ret < 0) { | ||
3673 | conn->tx_response_queue = 0; | ||
3674 | goto transport_err; | ||
3675 | } | ||
3676 | |||
3677 | se_cmd = &cmd->se_cmd; | ||
3678 | |||
3679 | if (map_sg && !conn->conn_ops->IFMarker) { | ||
3680 | if (iscsit_fe_sendpage_sg(cmd, conn) < 0) { | ||
3681 | conn->tx_response_queue = 0; | ||
3682 | iscsit_tx_thread_wait_for_tcp(conn); | ||
3683 | iscsit_unmap_iovec(cmd); | ||
3684 | goto transport_err; | ||
3685 | } | ||
3686 | } else { | ||
3687 | if (iscsit_send_tx_data(cmd, conn, use_misc) < 0) { | ||
3688 | conn->tx_response_queue = 0; | ||
3689 | iscsit_tx_thread_wait_for_tcp(conn); | ||
3690 | iscsit_unmap_iovec(cmd); | ||
3691 | goto transport_err; | ||
3692 | } | ||
3693 | } | ||
3694 | map_sg = 0; | ||
3695 | iscsit_unmap_iovec(cmd); | ||
3696 | |||
3697 | spin_lock_bh(&cmd->istate_lock); | ||
3698 | switch (state) { | ||
3699 | case ISTATE_SEND_DATAIN: | ||
3700 | if (!eodr) | ||
3701 | goto check_rsp_state; | ||
3702 | |||
3703 | if (eodr == 1) { | ||
3704 | cmd->i_state = ISTATE_SENT_LAST_DATAIN; | ||
3705 | sent_status = 1; | ||
3706 | eodr = use_misc = 0; | ||
3707 | } else if (eodr == 2) { | ||
3708 | cmd->i_state = state = | ||
3709 | ISTATE_SEND_STATUS; | ||
3710 | sent_status = 0; | ||
3711 | eodr = use_misc = 0; | ||
3712 | goto check_rsp_state; | ||
3713 | } | ||
3714 | break; | ||
3715 | case ISTATE_SEND_STATUS: | ||
3716 | use_misc = 0; | ||
3717 | sent_status = 1; | ||
3718 | break; | ||
3719 | case ISTATE_SEND_ASYNCMSG: | ||
3720 | case ISTATE_SEND_NOPIN: | ||
3721 | case ISTATE_SEND_STATUS_RECOVERY: | ||
3722 | case ISTATE_SEND_TEXTRSP: | ||
3723 | use_misc = 0; | ||
3724 | sent_status = 1; | ||
3725 | break; | ||
3726 | case ISTATE_SEND_REJECT: | ||
3727 | use_misc = 0; | ||
3728 | if (cmd->cmd_flags & ICF_REJECT_FAIL_CONN) { | ||
3729 | cmd->cmd_flags &= ~ICF_REJECT_FAIL_CONN; | ||
3730 | spin_unlock_bh(&cmd->istate_lock); | ||
3731 | complete(&cmd->reject_comp); | ||
3732 | goto transport_err; | ||
3733 | } | ||
3734 | complete(&cmd->reject_comp); | ||
3735 | break; | ||
3736 | case ISTATE_SEND_TASKMGTRSP: | ||
3737 | use_misc = 0; | ||
3738 | sent_status = 1; | ||
3739 | break; | ||
3740 | case ISTATE_SEND_LOGOUTRSP: | ||
3741 | spin_unlock_bh(&cmd->istate_lock); | ||
3742 | if (!iscsit_logout_post_handler(cmd, conn)) | ||
3743 | goto restart; | ||
3744 | spin_lock_bh(&cmd->istate_lock); | ||
3745 | use_misc = 0; | ||
3746 | sent_status = 1; | ||
3747 | break; | ||
3748 | default: | ||
3749 | pr_err("Unknown Opcode: 0x%02x ITT:" | ||
3750 | " 0x%08x, i_state: %d on CID: %hu\n", | ||
3751 | cmd->iscsi_opcode, cmd->init_task_tag, | ||
3752 | cmd->i_state, conn->cid); | ||
3753 | spin_unlock_bh(&cmd->istate_lock); | ||
3754 | goto transport_err; | ||
3755 | } | ||
3756 | |||
3757 | if (sent_status) { | ||
3758 | cmd->i_state = ISTATE_SENT_STATUS; | ||
3759 | sent_status = 0; | ||
3760 | } | ||
3761 | spin_unlock_bh(&cmd->istate_lock); | ||
3762 | |||
3763 | if (atomic_read(&conn->check_immediate_queue)) | ||
3764 | goto get_immediate; | ||
3765 | |||
3766 | goto get_response; | ||
3767 | } else | ||
3768 | conn->tx_response_queue = 0; | ||
3769 | } | ||
3770 | |||
3771 | transport_err: | ||
3772 | iscsit_take_action_for_connection_exit(conn); | ||
3773 | goto restart; | ||
3774 | out: | ||
3775 | return 0; | ||
3776 | } | ||
3777 | |||
3778 | int iscsi_target_rx_thread(void *arg) | ||
3779 | { | ||
3780 | int ret; | ||
3781 | u8 buffer[ISCSI_HDR_LEN], opcode; | ||
3782 | u32 checksum = 0, digest = 0; | ||
3783 | struct iscsi_conn *conn = NULL; | ||
3784 | struct iscsi_thread_set *ts = (struct iscsi_thread_set *)arg; | ||
3785 | struct kvec iov; | ||
3786 | /* | ||
3787 | * Allow ourselves to be interrupted by SIGINT so that a | ||
3788 | * connection recovery / failure event can be triggered externally. | ||
3789 | */ | ||
3790 | allow_signal(SIGINT); | ||
3791 | |||
3792 | restart: | ||
3793 | conn = iscsi_rx_thread_pre_handler(ts); | ||
3794 | if (!conn) | ||
3795 | goto out; | ||
3796 | |||
3797 | while (!kthread_should_stop()) { | ||
3798 | /* | ||
3799 | * Ensure that both TX and RX per connection kthreads | ||
3800 | * are scheduled to run on the same CPU. | ||
3801 | */ | ||
3802 | iscsit_thread_check_cpumask(conn, current, 0); | ||
3803 | |||
3804 | memset(buffer, 0, ISCSI_HDR_LEN); | ||
3805 | memset(&iov, 0, sizeof(struct kvec)); | ||
3806 | |||
3807 | iov.iov_base = buffer; | ||
3808 | iov.iov_len = ISCSI_HDR_LEN; | ||
3809 | |||
3810 | ret = rx_data(conn, &iov, 1, ISCSI_HDR_LEN); | ||
3811 | if (ret != ISCSI_HDR_LEN) { | ||
3812 | iscsit_rx_thread_wait_for_tcp(conn); | ||
3813 | goto transport_err; | ||
3814 | } | ||
3815 | |||
3816 | /* | ||
3817 | * Set conn->bad_hdr for use with REJECT PDUs. | ||
3818 | */ | ||
3819 | memcpy(&conn->bad_hdr, &buffer, ISCSI_HDR_LEN); | ||
3820 | |||
3821 | if (conn->conn_ops->HeaderDigest) { | ||
3822 | iov.iov_base = &digest; | ||
3823 | iov.iov_len = ISCSI_CRC_LEN; | ||
3824 | |||
3825 | ret = rx_data(conn, &iov, 1, ISCSI_CRC_LEN); | ||
3826 | if (ret != ISCSI_CRC_LEN) { | ||
3827 | iscsit_rx_thread_wait_for_tcp(conn); | ||
3828 | goto transport_err; | ||
3829 | } | ||
3830 | |||
3831 | iscsit_do_crypto_hash_buf(&conn->conn_rx_hash, | ||
3832 | buffer, ISCSI_HDR_LEN, | ||
3833 | 0, NULL, (u8 *)&checksum); | ||
3834 | |||
3835 | if (digest != checksum) { | ||
3836 | pr_err("HeaderDigest CRC32C failed," | ||
3837 | " received 0x%08x, computed 0x%08x\n", | ||
3838 | digest, checksum); | ||
3839 | /* | ||
3840 | * Set the PDU to 0xff so it will intentionally | ||
3841 | * hit default in the switch below. | ||
3842 | */ | ||
3843 | memset(buffer, 0xff, ISCSI_HDR_LEN); | ||
3844 | spin_lock_bh(&conn->sess->session_stats_lock); | ||
3845 | conn->sess->conn_digest_errors++; | ||
3846 | spin_unlock_bh(&conn->sess->session_stats_lock); | ||
3847 | } else { | ||
3848 | pr_debug("Got HeaderDigest CRC32C" | ||
3849 | " 0x%08x\n", checksum); | ||
3850 | } | ||
3851 | } | ||
3852 | |||
3853 | if (conn->conn_state == TARG_CONN_STATE_IN_LOGOUT) | ||
3854 | goto transport_err; | ||
3855 | |||
3856 | opcode = buffer[0] & ISCSI_OPCODE_MASK; | ||
3857 | |||
3858 | if (conn->sess->sess_ops->SessionType && | ||
3859 | ((!(opcode & ISCSI_OP_TEXT)) || | ||
3860 | (!(opcode & ISCSI_OP_LOGOUT)))) { | ||
3861 | pr_err("Received illegal iSCSI Opcode: 0x%02x" | ||
3862 | " while in Discovery Session, rejecting.\n", opcode); | ||
3863 | iscsit_add_reject(ISCSI_REASON_PROTOCOL_ERROR, 1, | ||
3864 | buffer, conn); | ||
3865 | goto transport_err; | ||
3866 | } | ||
3867 | |||
3868 | switch (opcode) { | ||
3869 | case ISCSI_OP_SCSI_CMD: | ||
3870 | if (iscsit_handle_scsi_cmd(conn, buffer) < 0) | ||
3871 | goto transport_err; | ||
3872 | break; | ||
3873 | case ISCSI_OP_SCSI_DATA_OUT: | ||
3874 | if (iscsit_handle_data_out(conn, buffer) < 0) | ||
3875 | goto transport_err; | ||
3876 | break; | ||
3877 | case ISCSI_OP_NOOP_OUT: | ||
3878 | if (iscsit_handle_nop_out(conn, buffer) < 0) | ||
3879 | goto transport_err; | ||
3880 | break; | ||
3881 | case ISCSI_OP_SCSI_TMFUNC: | ||
3882 | if (iscsit_handle_task_mgt_cmd(conn, buffer) < 0) | ||
3883 | goto transport_err; | ||
3884 | break; | ||
3885 | case ISCSI_OP_TEXT: | ||
3886 | if (iscsit_handle_text_cmd(conn, buffer) < 0) | ||
3887 | goto transport_err; | ||
3888 | break; | ||
3889 | case ISCSI_OP_LOGOUT: | ||
3890 | ret = iscsit_handle_logout_cmd(conn, buffer); | ||
3891 | if (ret > 0) { | ||
3892 | wait_for_completion_timeout(&conn->conn_logout_comp, | ||
3893 | SECONDS_FOR_LOGOUT_COMP * HZ); | ||
3894 | goto transport_err; | ||
3895 | } else if (ret < 0) | ||
3896 | goto transport_err; | ||
3897 | break; | ||
3898 | case ISCSI_OP_SNACK: | ||
3899 | if (iscsit_handle_snack(conn, buffer) < 0) | ||
3900 | goto transport_err; | ||
3901 | break; | ||
3902 | default: | ||
3903 | pr_err("Got unknown iSCSI OpCode: 0x%02x\n", | ||
3904 | opcode); | ||
3905 | if (!conn->sess->sess_ops->ErrorRecoveryLevel) { | ||
3906 | pr_err("Cannot recover from unknown" | ||
3907 | " opcode while ERL=0, closing iSCSI connection" | ||
3908 | ".\n"); | ||
3909 | goto transport_err; | ||
3910 | } | ||
3911 | if (!conn->conn_ops->OFMarker) { | ||
3912 | pr_err("Unable to recover from unknown" | ||
3913 | " opcode while OFMarker=No, closing iSCSI" | ||
3914 | " connection.\n"); | ||
3915 | goto transport_err; | ||
3916 | } | ||
3917 | if (iscsit_recover_from_unknown_opcode(conn) < 0) { | ||
3918 | pr_err("Unable to recover from unknown" | ||
3919 | " opcode, closing iSCSI connection.\n"); | ||
3920 | goto transport_err; | ||
3921 | } | ||
3922 | break; | ||
3923 | } | ||
3924 | } | ||
3925 | |||
3926 | transport_err: | ||
3927 | if (!signal_pending(current)) | ||
3928 | atomic_set(&conn->transport_failed, 1); | ||
3929 | iscsit_take_action_for_connection_exit(conn); | ||
3930 | goto restart; | ||
3931 | out: | ||
3932 | return 0; | ||
3933 | } | ||
3934 | |||
3935 | static void iscsit_release_commands_from_conn(struct iscsi_conn *conn) | ||
3936 | { | ||
3937 | struct iscsi_cmd *cmd = NULL, *cmd_tmp = NULL; | ||
3938 | struct iscsi_session *sess = conn->sess; | ||
3939 | struct se_cmd *se_cmd; | ||
3940 | /* | ||
3941 | * We expect this function to only ever be called from either RX or TX | ||
3942 | * thread context via iscsit_close_connection() once the other context | ||
3943 | * has been reset -> returned sleeping pre-handler state. | ||
3944 | */ | ||
3945 | spin_lock_bh(&conn->cmd_lock); | ||
3946 | list_for_each_entry_safe(cmd, cmd_tmp, &conn->conn_cmd_list, i_list) { | ||
3947 | if (!(cmd->se_cmd.se_cmd_flags & SCF_SE_LUN_CMD)) { | ||
3948 | |||
3949 | list_del(&cmd->i_list); | ||
3950 | spin_unlock_bh(&conn->cmd_lock); | ||
3951 | iscsit_increment_maxcmdsn(cmd, sess); | ||
3952 | se_cmd = &cmd->se_cmd; | ||
3953 | /* | ||
3954 | * Special cases for active iSCSI TMR, and | ||
3955 | * transport_lookup_cmd_lun() failing from | ||
3956 | * iscsit_get_lun_for_cmd() in iscsit_handle_scsi_cmd(). | ||
3957 | */ | ||
3958 | if (cmd->tmr_req && se_cmd->transport_wait_for_tasks) | ||
3959 | se_cmd->transport_wait_for_tasks(se_cmd, 1, 1); | ||
3960 | else if (cmd->se_cmd.se_cmd_flags & SCF_SE_LUN_CMD) | ||
3961 | transport_release_cmd(se_cmd); | ||
3962 | else | ||
3963 | iscsit_release_cmd(cmd); | ||
3964 | |||
3965 | spin_lock_bh(&conn->cmd_lock); | ||
3966 | continue; | ||
3967 | } | ||
3968 | list_del(&cmd->i_list); | ||
3969 | spin_unlock_bh(&conn->cmd_lock); | ||
3970 | |||
3971 | iscsit_increment_maxcmdsn(cmd, sess); | ||
3972 | se_cmd = &cmd->se_cmd; | ||
3973 | |||
3974 | if (se_cmd->transport_wait_for_tasks) | ||
3975 | se_cmd->transport_wait_for_tasks(se_cmd, 1, 1); | ||
3976 | |||
3977 | spin_lock_bh(&conn->cmd_lock); | ||
3978 | } | ||
3979 | spin_unlock_bh(&conn->cmd_lock); | ||
3980 | } | ||
3981 | |||
3982 | static void iscsit_stop_timers_for_cmds( | ||
3983 | struct iscsi_conn *conn) | ||
3984 | { | ||
3985 | struct iscsi_cmd *cmd; | ||
3986 | |||
3987 | spin_lock_bh(&conn->cmd_lock); | ||
3988 | list_for_each_entry(cmd, &conn->conn_cmd_list, i_list) { | ||
3989 | if (cmd->data_direction == DMA_TO_DEVICE) | ||
3990 | iscsit_stop_dataout_timer(cmd); | ||
3991 | } | ||
3992 | spin_unlock_bh(&conn->cmd_lock); | ||
3993 | } | ||
3994 | |||
3995 | int iscsit_close_connection( | ||
3996 | struct iscsi_conn *conn) | ||
3997 | { | ||
3998 | int conn_logout = (conn->conn_state == TARG_CONN_STATE_IN_LOGOUT); | ||
3999 | struct iscsi_session *sess = conn->sess; | ||
4000 | |||
4001 | pr_debug("Closing iSCSI connection CID %hu on SID:" | ||
4002 | " %u\n", conn->cid, sess->sid); | ||
4003 | /* | ||
4004 | * Always up conn_logout_comp just in case the RX Thread is sleeping | ||
4005 | * and the logout response never got sent because the connection | ||
4006 | * failed. | ||
4007 | */ | ||
4008 | complete(&conn->conn_logout_comp); | ||
4009 | |||
4010 | iscsi_release_thread_set(conn); | ||
4011 | |||
4012 | iscsit_stop_timers_for_cmds(conn); | ||
4013 | iscsit_stop_nopin_response_timer(conn); | ||
4014 | iscsit_stop_nopin_timer(conn); | ||
4015 | iscsit_free_queue_reqs_for_conn(conn); | ||
4016 | |||
4017 | /* | ||
4018 | * During Connection recovery drop unacknowledged out of order | ||
4019 | * commands for this connection, and prepare the other commands | ||
4020 | * for realligence. | ||
4021 | * | ||
4022 | * During normal operation clear the out of order commands (but | ||
4023 | * do not free the struct iscsi_ooo_cmdsn's) and release all | ||
4024 | * struct iscsi_cmds. | ||
4025 | */ | ||
4026 | if (atomic_read(&conn->connection_recovery)) { | ||
4027 | iscsit_discard_unacknowledged_ooo_cmdsns_for_conn(conn); | ||
4028 | iscsit_prepare_cmds_for_realligance(conn); | ||
4029 | } else { | ||
4030 | iscsit_clear_ooo_cmdsns_for_conn(conn); | ||
4031 | iscsit_release_commands_from_conn(conn); | ||
4032 | } | ||
4033 | |||
4034 | /* | ||
4035 | * Handle decrementing session or connection usage count if | ||
4036 | * a logout response was not able to be sent because the | ||
4037 | * connection failed. Fall back to Session Recovery here. | ||
4038 | */ | ||
4039 | if (atomic_read(&conn->conn_logout_remove)) { | ||
4040 | if (conn->conn_logout_reason == ISCSI_LOGOUT_REASON_CLOSE_SESSION) { | ||
4041 | iscsit_dec_conn_usage_count(conn); | ||
4042 | iscsit_dec_session_usage_count(sess); | ||
4043 | } | ||
4044 | if (conn->conn_logout_reason == ISCSI_LOGOUT_REASON_CLOSE_CONNECTION) | ||
4045 | iscsit_dec_conn_usage_count(conn); | ||
4046 | |||
4047 | atomic_set(&conn->conn_logout_remove, 0); | ||
4048 | atomic_set(&sess->session_reinstatement, 0); | ||
4049 | atomic_set(&sess->session_fall_back_to_erl0, 1); | ||
4050 | } | ||
4051 | |||
4052 | spin_lock_bh(&sess->conn_lock); | ||
4053 | list_del(&conn->conn_list); | ||
4054 | |||
4055 | /* | ||
4056 | * Attempt to let the Initiator know this connection failed by | ||
4057 | * sending an Connection Dropped Async Message on another | ||
4058 | * active connection. | ||
4059 | */ | ||
4060 | if (atomic_read(&conn->connection_recovery)) | ||
4061 | iscsit_build_conn_drop_async_message(conn); | ||
4062 | |||
4063 | spin_unlock_bh(&sess->conn_lock); | ||
4064 | |||
4065 | /* | ||
4066 | * If connection reinstatement is being performed on this connection, | ||
4067 | * up the connection reinstatement semaphore that is being blocked on | ||
4068 | * in iscsit_cause_connection_reinstatement(). | ||
4069 | */ | ||
4070 | spin_lock_bh(&conn->state_lock); | ||
4071 | if (atomic_read(&conn->sleep_on_conn_wait_comp)) { | ||
4072 | spin_unlock_bh(&conn->state_lock); | ||
4073 | complete(&conn->conn_wait_comp); | ||
4074 | wait_for_completion(&conn->conn_post_wait_comp); | ||
4075 | spin_lock_bh(&conn->state_lock); | ||
4076 | } | ||
4077 | |||
4078 | /* | ||
4079 | * If connection reinstatement is being performed on this connection | ||
4080 | * by receiving a REMOVECONNFORRECOVERY logout request, up the | ||
4081 | * connection wait rcfr semaphore that is being blocked on | ||
4082 | * an iscsit_connection_reinstatement_rcfr(). | ||
4083 | */ | ||
4084 | if (atomic_read(&conn->connection_wait_rcfr)) { | ||
4085 | spin_unlock_bh(&conn->state_lock); | ||
4086 | complete(&conn->conn_wait_rcfr_comp); | ||
4087 | wait_for_completion(&conn->conn_post_wait_comp); | ||
4088 | spin_lock_bh(&conn->state_lock); | ||
4089 | } | ||
4090 | atomic_set(&conn->connection_reinstatement, 1); | ||
4091 | spin_unlock_bh(&conn->state_lock); | ||
4092 | |||
4093 | /* | ||
4094 | * If any other processes are accessing this connection pointer we | ||
4095 | * must wait until they have completed. | ||
4096 | */ | ||
4097 | iscsit_check_conn_usage_count(conn); | ||
4098 | |||
4099 | if (conn->conn_rx_hash.tfm) | ||
4100 | crypto_free_hash(conn->conn_rx_hash.tfm); | ||
4101 | if (conn->conn_tx_hash.tfm) | ||
4102 | crypto_free_hash(conn->conn_tx_hash.tfm); | ||
4103 | |||
4104 | if (conn->conn_cpumask) | ||
4105 | free_cpumask_var(conn->conn_cpumask); | ||
4106 | |||
4107 | kfree(conn->conn_ops); | ||
4108 | conn->conn_ops = NULL; | ||
4109 | |||
4110 | if (conn->sock) { | ||
4111 | if (conn->conn_flags & CONNFLAG_SCTP_STRUCT_FILE) { | ||
4112 | kfree(conn->sock->file); | ||
4113 | conn->sock->file = NULL; | ||
4114 | } | ||
4115 | sock_release(conn->sock); | ||
4116 | } | ||
4117 | conn->thread_set = NULL; | ||
4118 | |||
4119 | pr_debug("Moving to TARG_CONN_STATE_FREE.\n"); | ||
4120 | conn->conn_state = TARG_CONN_STATE_FREE; | ||
4121 | kfree(conn); | ||
4122 | |||
4123 | spin_lock_bh(&sess->conn_lock); | ||
4124 | atomic_dec(&sess->nconn); | ||
4125 | pr_debug("Decremented iSCSI connection count to %hu from node:" | ||
4126 | " %s\n", atomic_read(&sess->nconn), | ||
4127 | sess->sess_ops->InitiatorName); | ||
4128 | /* | ||
4129 | * Make sure that if one connection fails in an non ERL=2 iSCSI | ||
4130 | * Session that they all fail. | ||
4131 | */ | ||
4132 | if ((sess->sess_ops->ErrorRecoveryLevel != 2) && !conn_logout && | ||
4133 | !atomic_read(&sess->session_logout)) | ||
4134 | atomic_set(&sess->session_fall_back_to_erl0, 1); | ||
4135 | |||
4136 | /* | ||
4137 | * If this was not the last connection in the session, and we are | ||
4138 | * performing session reinstatement or falling back to ERL=0, call | ||
4139 | * iscsit_stop_session() without sleeping to shutdown the other | ||
4140 | * active connections. | ||
4141 | */ | ||
4142 | if (atomic_read(&sess->nconn)) { | ||
4143 | if (!atomic_read(&sess->session_reinstatement) && | ||
4144 | !atomic_read(&sess->session_fall_back_to_erl0)) { | ||
4145 | spin_unlock_bh(&sess->conn_lock); | ||
4146 | return 0; | ||
4147 | } | ||
4148 | if (!atomic_read(&sess->session_stop_active)) { | ||
4149 | atomic_set(&sess->session_stop_active, 1); | ||
4150 | spin_unlock_bh(&sess->conn_lock); | ||
4151 | iscsit_stop_session(sess, 0, 0); | ||
4152 | return 0; | ||
4153 | } | ||
4154 | spin_unlock_bh(&sess->conn_lock); | ||
4155 | return 0; | ||
4156 | } | ||
4157 | |||
4158 | /* | ||
4159 | * If this was the last connection in the session and one of the | ||
4160 | * following is occurring: | ||
4161 | * | ||
4162 | * Session Reinstatement is not being performed, and are falling back | ||
4163 | * to ERL=0 call iscsit_close_session(). | ||
4164 | * | ||
4165 | * Session Logout was requested. iscsit_close_session() will be called | ||
4166 | * elsewhere. | ||
4167 | * | ||
4168 | * Session Continuation is not being performed, start the Time2Retain | ||
4169 | * handler and check if sleep_on_sess_wait_sem is active. | ||
4170 | */ | ||
4171 | if (!atomic_read(&sess->session_reinstatement) && | ||
4172 | atomic_read(&sess->session_fall_back_to_erl0)) { | ||
4173 | spin_unlock_bh(&sess->conn_lock); | ||
4174 | iscsit_close_session(sess); | ||
4175 | |||
4176 | return 0; | ||
4177 | } else if (atomic_read(&sess->session_logout)) { | ||
4178 | pr_debug("Moving to TARG_SESS_STATE_FREE.\n"); | ||
4179 | sess->session_state = TARG_SESS_STATE_FREE; | ||
4180 | spin_unlock_bh(&sess->conn_lock); | ||
4181 | |||
4182 | if (atomic_read(&sess->sleep_on_sess_wait_comp)) | ||
4183 | complete(&sess->session_wait_comp); | ||
4184 | |||
4185 | return 0; | ||
4186 | } else { | ||
4187 | pr_debug("Moving to TARG_SESS_STATE_FAILED.\n"); | ||
4188 | sess->session_state = TARG_SESS_STATE_FAILED; | ||
4189 | |||
4190 | if (!atomic_read(&sess->session_continuation)) { | ||
4191 | spin_unlock_bh(&sess->conn_lock); | ||
4192 | iscsit_start_time2retain_handler(sess); | ||
4193 | } else | ||
4194 | spin_unlock_bh(&sess->conn_lock); | ||
4195 | |||
4196 | if (atomic_read(&sess->sleep_on_sess_wait_comp)) | ||
4197 | complete(&sess->session_wait_comp); | ||
4198 | |||
4199 | return 0; | ||
4200 | } | ||
4201 | spin_unlock_bh(&sess->conn_lock); | ||
4202 | |||
4203 | return 0; | ||
4204 | } | ||
4205 | |||
4206 | int iscsit_close_session(struct iscsi_session *sess) | ||
4207 | { | ||
4208 | struct iscsi_portal_group *tpg = ISCSI_TPG_S(sess); | ||
4209 | struct se_portal_group *se_tpg = &tpg->tpg_se_tpg; | ||
4210 | |||
4211 | if (atomic_read(&sess->nconn)) { | ||
4212 | pr_err("%d connection(s) still exist for iSCSI session" | ||
4213 | " to %s\n", atomic_read(&sess->nconn), | ||
4214 | sess->sess_ops->InitiatorName); | ||
4215 | BUG(); | ||
4216 | } | ||
4217 | |||
4218 | spin_lock_bh(&se_tpg->session_lock); | ||
4219 | atomic_set(&sess->session_logout, 1); | ||
4220 | atomic_set(&sess->session_reinstatement, 1); | ||
4221 | iscsit_stop_time2retain_timer(sess); | ||
4222 | spin_unlock_bh(&se_tpg->session_lock); | ||
4223 | |||
4224 | /* | ||
4225 | * transport_deregister_session_configfs() will clear the | ||
4226 | * struct se_node_acl->nacl_sess pointer now as a iscsi_np process context | ||
4227 | * can be setting it again with __transport_register_session() in | ||
4228 | * iscsi_post_login_handler() again after the iscsit_stop_session() | ||
4229 | * completes in iscsi_np context. | ||
4230 | */ | ||
4231 | transport_deregister_session_configfs(sess->se_sess); | ||
4232 | |||
4233 | /* | ||
4234 | * If any other processes are accessing this session pointer we must | ||
4235 | * wait until they have completed. If we are in an interrupt (the | ||
4236 | * time2retain handler) and contain and active session usage count we | ||
4237 | * restart the timer and exit. | ||
4238 | */ | ||
4239 | if (!in_interrupt()) { | ||
4240 | if (iscsit_check_session_usage_count(sess) == 1) | ||
4241 | iscsit_stop_session(sess, 1, 1); | ||
4242 | } else { | ||
4243 | if (iscsit_check_session_usage_count(sess) == 2) { | ||
4244 | atomic_set(&sess->session_logout, 0); | ||
4245 | iscsit_start_time2retain_handler(sess); | ||
4246 | return 0; | ||
4247 | } | ||
4248 | } | ||
4249 | |||
4250 | transport_deregister_session(sess->se_sess); | ||
4251 | |||
4252 | if (sess->sess_ops->ErrorRecoveryLevel == 2) | ||
4253 | iscsit_free_connection_recovery_entires(sess); | ||
4254 | |||
4255 | iscsit_free_all_ooo_cmdsns(sess); | ||
4256 | |||
4257 | spin_lock_bh(&se_tpg->session_lock); | ||
4258 | pr_debug("Moving to TARG_SESS_STATE_FREE.\n"); | ||
4259 | sess->session_state = TARG_SESS_STATE_FREE; | ||
4260 | pr_debug("Released iSCSI session from node: %s\n", | ||
4261 | sess->sess_ops->InitiatorName); | ||
4262 | tpg->nsessions--; | ||
4263 | if (tpg->tpg_tiqn) | ||
4264 | tpg->tpg_tiqn->tiqn_nsessions--; | ||
4265 | |||
4266 | pr_debug("Decremented number of active iSCSI Sessions on" | ||
4267 | " iSCSI TPG: %hu to %u\n", tpg->tpgt, tpg->nsessions); | ||
4268 | |||
4269 | spin_lock(&sess_idr_lock); | ||
4270 | idr_remove(&sess_idr, sess->session_index); | ||
4271 | spin_unlock(&sess_idr_lock); | ||
4272 | |||
4273 | kfree(sess->sess_ops); | ||
4274 | sess->sess_ops = NULL; | ||
4275 | spin_unlock_bh(&se_tpg->session_lock); | ||
4276 | |||
4277 | kfree(sess); | ||
4278 | return 0; | ||
4279 | } | ||
4280 | |||
4281 | static void iscsit_logout_post_handler_closesession( | ||
4282 | struct iscsi_conn *conn) | ||
4283 | { | ||
4284 | struct iscsi_session *sess = conn->sess; | ||
4285 | |||
4286 | iscsi_set_thread_clear(conn, ISCSI_CLEAR_TX_THREAD); | ||
4287 | iscsi_set_thread_set_signal(conn, ISCSI_SIGNAL_TX_THREAD); | ||
4288 | |||
4289 | atomic_set(&conn->conn_logout_remove, 0); | ||
4290 | complete(&conn->conn_logout_comp); | ||
4291 | |||
4292 | iscsit_dec_conn_usage_count(conn); | ||
4293 | iscsit_stop_session(sess, 1, 1); | ||
4294 | iscsit_dec_session_usage_count(sess); | ||
4295 | iscsit_close_session(sess); | ||
4296 | } | ||
4297 | |||
4298 | static void iscsit_logout_post_handler_samecid( | ||
4299 | struct iscsi_conn *conn) | ||
4300 | { | ||
4301 | iscsi_set_thread_clear(conn, ISCSI_CLEAR_TX_THREAD); | ||
4302 | iscsi_set_thread_set_signal(conn, ISCSI_SIGNAL_TX_THREAD); | ||
4303 | |||
4304 | atomic_set(&conn->conn_logout_remove, 0); | ||
4305 | complete(&conn->conn_logout_comp); | ||
4306 | |||
4307 | iscsit_cause_connection_reinstatement(conn, 1); | ||
4308 | iscsit_dec_conn_usage_count(conn); | ||
4309 | } | ||
4310 | |||
4311 | static void iscsit_logout_post_handler_diffcid( | ||
4312 | struct iscsi_conn *conn, | ||
4313 | u16 cid) | ||
4314 | { | ||
4315 | struct iscsi_conn *l_conn; | ||
4316 | struct iscsi_session *sess = conn->sess; | ||
4317 | |||
4318 | if (!sess) | ||
4319 | return; | ||
4320 | |||
4321 | spin_lock_bh(&sess->conn_lock); | ||
4322 | list_for_each_entry(l_conn, &sess->sess_conn_list, conn_list) { | ||
4323 | if (l_conn->cid == cid) { | ||
4324 | iscsit_inc_conn_usage_count(l_conn); | ||
4325 | break; | ||
4326 | } | ||
4327 | } | ||
4328 | spin_unlock_bh(&sess->conn_lock); | ||
4329 | |||
4330 | if (!l_conn) | ||
4331 | return; | ||
4332 | |||
4333 | if (l_conn->sock) | ||
4334 | l_conn->sock->ops->shutdown(l_conn->sock, RCV_SHUTDOWN); | ||
4335 | |||
4336 | spin_lock_bh(&l_conn->state_lock); | ||
4337 | pr_debug("Moving to TARG_CONN_STATE_IN_LOGOUT.\n"); | ||
4338 | l_conn->conn_state = TARG_CONN_STATE_IN_LOGOUT; | ||
4339 | spin_unlock_bh(&l_conn->state_lock); | ||
4340 | |||
4341 | iscsit_cause_connection_reinstatement(l_conn, 1); | ||
4342 | iscsit_dec_conn_usage_count(l_conn); | ||
4343 | } | ||
4344 | |||
4345 | /* | ||
4346 | * Return of 0 causes the TX thread to restart. | ||
4347 | */ | ||
4348 | static int iscsit_logout_post_handler( | ||
4349 | struct iscsi_cmd *cmd, | ||
4350 | struct iscsi_conn *conn) | ||
4351 | { | ||
4352 | int ret = 0; | ||
4353 | |||
4354 | switch (cmd->logout_reason) { | ||
4355 | case ISCSI_LOGOUT_REASON_CLOSE_SESSION: | ||
4356 | switch (cmd->logout_response) { | ||
4357 | case ISCSI_LOGOUT_SUCCESS: | ||
4358 | case ISCSI_LOGOUT_CLEANUP_FAILED: | ||
4359 | default: | ||
4360 | iscsit_logout_post_handler_closesession(conn); | ||
4361 | break; | ||
4362 | } | ||
4363 | ret = 0; | ||
4364 | break; | ||
4365 | case ISCSI_LOGOUT_REASON_CLOSE_CONNECTION: | ||
4366 | if (conn->cid == cmd->logout_cid) { | ||
4367 | switch (cmd->logout_response) { | ||
4368 | case ISCSI_LOGOUT_SUCCESS: | ||
4369 | case ISCSI_LOGOUT_CLEANUP_FAILED: | ||
4370 | default: | ||
4371 | iscsit_logout_post_handler_samecid(conn); | ||
4372 | break; | ||
4373 | } | ||
4374 | ret = 0; | ||
4375 | } else { | ||
4376 | switch (cmd->logout_response) { | ||
4377 | case ISCSI_LOGOUT_SUCCESS: | ||
4378 | iscsit_logout_post_handler_diffcid(conn, | ||
4379 | cmd->logout_cid); | ||
4380 | break; | ||
4381 | case ISCSI_LOGOUT_CID_NOT_FOUND: | ||
4382 | case ISCSI_LOGOUT_CLEANUP_FAILED: | ||
4383 | default: | ||
4384 | break; | ||
4385 | } | ||
4386 | ret = 1; | ||
4387 | } | ||
4388 | break; | ||
4389 | case ISCSI_LOGOUT_REASON_RECOVERY: | ||
4390 | switch (cmd->logout_response) { | ||
4391 | case ISCSI_LOGOUT_SUCCESS: | ||
4392 | case ISCSI_LOGOUT_CID_NOT_FOUND: | ||
4393 | case ISCSI_LOGOUT_RECOVERY_UNSUPPORTED: | ||
4394 | case ISCSI_LOGOUT_CLEANUP_FAILED: | ||
4395 | default: | ||
4396 | break; | ||
4397 | } | ||
4398 | ret = 1; | ||
4399 | break; | ||
4400 | default: | ||
4401 | break; | ||
4402 | |||
4403 | } | ||
4404 | return ret; | ||
4405 | } | ||
4406 | |||
4407 | void iscsit_fail_session(struct iscsi_session *sess) | ||
4408 | { | ||
4409 | struct iscsi_conn *conn; | ||
4410 | |||
4411 | spin_lock_bh(&sess->conn_lock); | ||
4412 | list_for_each_entry(conn, &sess->sess_conn_list, conn_list) { | ||
4413 | pr_debug("Moving to TARG_CONN_STATE_CLEANUP_WAIT.\n"); | ||
4414 | conn->conn_state = TARG_CONN_STATE_CLEANUP_WAIT; | ||
4415 | } | ||
4416 | spin_unlock_bh(&sess->conn_lock); | ||
4417 | |||
4418 | pr_debug("Moving to TARG_SESS_STATE_FAILED.\n"); | ||
4419 | sess->session_state = TARG_SESS_STATE_FAILED; | ||
4420 | } | ||
4421 | |||
4422 | int iscsit_free_session(struct iscsi_session *sess) | ||
4423 | { | ||
4424 | u16 conn_count = atomic_read(&sess->nconn); | ||
4425 | struct iscsi_conn *conn, *conn_tmp = NULL; | ||
4426 | int is_last; | ||
4427 | |||
4428 | spin_lock_bh(&sess->conn_lock); | ||
4429 | atomic_set(&sess->sleep_on_sess_wait_comp, 1); | ||
4430 | |||
4431 | list_for_each_entry_safe(conn, conn_tmp, &sess->sess_conn_list, | ||
4432 | conn_list) { | ||
4433 | if (conn_count == 0) | ||
4434 | break; | ||
4435 | |||
4436 | if (list_is_last(&conn->conn_list, &sess->sess_conn_list)) { | ||
4437 | is_last = 1; | ||
4438 | } else { | ||
4439 | iscsit_inc_conn_usage_count(conn_tmp); | ||
4440 | is_last = 0; | ||
4441 | } | ||
4442 | iscsit_inc_conn_usage_count(conn); | ||
4443 | |||
4444 | spin_unlock_bh(&sess->conn_lock); | ||
4445 | iscsit_cause_connection_reinstatement(conn, 1); | ||
4446 | spin_lock_bh(&sess->conn_lock); | ||
4447 | |||
4448 | iscsit_dec_conn_usage_count(conn); | ||
4449 | if (is_last == 0) | ||
4450 | iscsit_dec_conn_usage_count(conn_tmp); | ||
4451 | |||
4452 | conn_count--; | ||
4453 | } | ||
4454 | |||
4455 | if (atomic_read(&sess->nconn)) { | ||
4456 | spin_unlock_bh(&sess->conn_lock); | ||
4457 | wait_for_completion(&sess->session_wait_comp); | ||
4458 | } else | ||
4459 | spin_unlock_bh(&sess->conn_lock); | ||
4460 | |||
4461 | iscsit_close_session(sess); | ||
4462 | return 0; | ||
4463 | } | ||
4464 | |||
4465 | void iscsit_stop_session( | ||
4466 | struct iscsi_session *sess, | ||
4467 | int session_sleep, | ||
4468 | int connection_sleep) | ||
4469 | { | ||
4470 | u16 conn_count = atomic_read(&sess->nconn); | ||
4471 | struct iscsi_conn *conn, *conn_tmp = NULL; | ||
4472 | int is_last; | ||
4473 | |||
4474 | spin_lock_bh(&sess->conn_lock); | ||
4475 | if (session_sleep) | ||
4476 | atomic_set(&sess->sleep_on_sess_wait_comp, 1); | ||
4477 | |||
4478 | if (connection_sleep) { | ||
4479 | list_for_each_entry_safe(conn, conn_tmp, &sess->sess_conn_list, | ||
4480 | conn_list) { | ||
4481 | if (conn_count == 0) | ||
4482 | break; | ||
4483 | |||
4484 | if (list_is_last(&conn->conn_list, &sess->sess_conn_list)) { | ||
4485 | is_last = 1; | ||
4486 | } else { | ||
4487 | iscsit_inc_conn_usage_count(conn_tmp); | ||
4488 | is_last = 0; | ||
4489 | } | ||
4490 | iscsit_inc_conn_usage_count(conn); | ||
4491 | |||
4492 | spin_unlock_bh(&sess->conn_lock); | ||
4493 | iscsit_cause_connection_reinstatement(conn, 1); | ||
4494 | spin_lock_bh(&sess->conn_lock); | ||
4495 | |||
4496 | iscsit_dec_conn_usage_count(conn); | ||
4497 | if (is_last == 0) | ||
4498 | iscsit_dec_conn_usage_count(conn_tmp); | ||
4499 | conn_count--; | ||
4500 | } | ||
4501 | } else { | ||
4502 | list_for_each_entry(conn, &sess->sess_conn_list, conn_list) | ||
4503 | iscsit_cause_connection_reinstatement(conn, 0); | ||
4504 | } | ||
4505 | |||
4506 | if (session_sleep && atomic_read(&sess->nconn)) { | ||
4507 | spin_unlock_bh(&sess->conn_lock); | ||
4508 | wait_for_completion(&sess->session_wait_comp); | ||
4509 | } else | ||
4510 | spin_unlock_bh(&sess->conn_lock); | ||
4511 | } | ||
4512 | |||
4513 | int iscsit_release_sessions_for_tpg(struct iscsi_portal_group *tpg, int force) | ||
4514 | { | ||
4515 | struct iscsi_session *sess; | ||
4516 | struct se_portal_group *se_tpg = &tpg->tpg_se_tpg; | ||
4517 | struct se_session *se_sess, *se_sess_tmp; | ||
4518 | int session_count = 0; | ||
4519 | |||
4520 | spin_lock_bh(&se_tpg->session_lock); | ||
4521 | if (tpg->nsessions && !force) { | ||
4522 | spin_unlock_bh(&se_tpg->session_lock); | ||
4523 | return -1; | ||
4524 | } | ||
4525 | |||
4526 | list_for_each_entry_safe(se_sess, se_sess_tmp, &se_tpg->tpg_sess_list, | ||
4527 | sess_list) { | ||
4528 | sess = (struct iscsi_session *)se_sess->fabric_sess_ptr; | ||
4529 | |||
4530 | spin_lock(&sess->conn_lock); | ||
4531 | if (atomic_read(&sess->session_fall_back_to_erl0) || | ||
4532 | atomic_read(&sess->session_logout) || | ||
4533 | (sess->time2retain_timer_flags & ISCSI_TF_EXPIRED)) { | ||
4534 | spin_unlock(&sess->conn_lock); | ||
4535 | continue; | ||
4536 | } | ||
4537 | atomic_set(&sess->session_reinstatement, 1); | ||
4538 | spin_unlock(&sess->conn_lock); | ||
4539 | spin_unlock_bh(&se_tpg->session_lock); | ||
4540 | |||
4541 | iscsit_free_session(sess); | ||
4542 | spin_lock_bh(&se_tpg->session_lock); | ||
4543 | |||
4544 | session_count++; | ||
4545 | } | ||
4546 | spin_unlock_bh(&se_tpg->session_lock); | ||
4547 | |||
4548 | pr_debug("Released %d iSCSI Session(s) from Target Portal" | ||
4549 | " Group: %hu\n", session_count, tpg->tpgt); | ||
4550 | return 0; | ||
4551 | } | ||
4552 | |||
4553 | MODULE_DESCRIPTION("iSCSI-Target Driver for mainline target infrastructure"); | ||
4554 | MODULE_VERSION("4.1.x"); | ||
4555 | MODULE_AUTHOR("nab@Linux-iSCSI.org"); | ||
4556 | MODULE_LICENSE("GPL"); | ||
4557 | |||
4558 | module_init(iscsi_target_init_module); | ||
4559 | module_exit(iscsi_target_cleanup_module); | ||
diff --git a/drivers/target/iscsi/iscsi_target.h b/drivers/target/iscsi/iscsi_target.h new file mode 100644 index 000000000000..5db2ddeed5eb --- /dev/null +++ b/drivers/target/iscsi/iscsi_target.h | |||
@@ -0,0 +1,42 @@ | |||
1 | #ifndef ISCSI_TARGET_H | ||
2 | #define ISCSI_TARGET_H | ||
3 | |||
4 | extern struct iscsi_tiqn *iscsit_get_tiqn_for_login(unsigned char *); | ||
5 | extern struct iscsi_tiqn *iscsit_get_tiqn(unsigned char *, int); | ||
6 | extern void iscsit_put_tiqn_for_login(struct iscsi_tiqn *); | ||
7 | extern struct iscsi_tiqn *iscsit_add_tiqn(unsigned char *); | ||
8 | extern void iscsit_del_tiqn(struct iscsi_tiqn *); | ||
9 | extern int iscsit_access_np(struct iscsi_np *, struct iscsi_portal_group *); | ||
10 | extern int iscsit_deaccess_np(struct iscsi_np *, struct iscsi_portal_group *); | ||
11 | extern struct iscsi_np *iscsit_add_np(struct __kernel_sockaddr_storage *, | ||
12 | char *, int); | ||
13 | extern int iscsit_reset_np_thread(struct iscsi_np *, struct iscsi_tpg_np *, | ||
14 | struct iscsi_portal_group *); | ||
15 | extern int iscsit_del_np(struct iscsi_np *); | ||
16 | extern int iscsit_add_reject_from_cmd(u8, int, int, unsigned char *, struct iscsi_cmd *); | ||
17 | extern int iscsit_logout_closesession(struct iscsi_cmd *, struct iscsi_conn *); | ||
18 | extern int iscsit_logout_closeconnection(struct iscsi_cmd *, struct iscsi_conn *); | ||
19 | extern int iscsit_logout_removeconnforrecovery(struct iscsi_cmd *, struct iscsi_conn *); | ||
20 | extern int iscsit_send_async_msg(struct iscsi_conn *, u16, u8, u8); | ||
21 | extern int iscsit_send_r2t(struct iscsi_cmd *, struct iscsi_conn *); | ||
22 | extern int iscsit_build_r2ts_for_cmd(struct iscsi_cmd *, struct iscsi_conn *, int); | ||
23 | extern void iscsit_thread_get_cpumask(struct iscsi_conn *); | ||
24 | extern int iscsi_target_tx_thread(void *); | ||
25 | extern int iscsi_target_rx_thread(void *); | ||
26 | extern int iscsit_close_connection(struct iscsi_conn *); | ||
27 | extern int iscsit_close_session(struct iscsi_session *); | ||
28 | extern void iscsit_fail_session(struct iscsi_session *); | ||
29 | extern int iscsit_free_session(struct iscsi_session *); | ||
30 | extern void iscsit_stop_session(struct iscsi_session *, int, int); | ||
31 | extern int iscsit_release_sessions_for_tpg(struct iscsi_portal_group *, int); | ||
32 | |||
33 | extern struct iscsit_global *iscsit_global; | ||
34 | extern struct target_fabric_configfs *lio_target_fabric_configfs; | ||
35 | |||
36 | extern struct kmem_cache *lio_dr_cache; | ||
37 | extern struct kmem_cache *lio_ooo_cache; | ||
38 | extern struct kmem_cache *lio_cmd_cache; | ||
39 | extern struct kmem_cache *lio_qr_cache; | ||
40 | extern struct kmem_cache *lio_r2t_cache; | ||
41 | |||
42 | #endif /*** ISCSI_TARGET_H ***/ | ||
diff --git a/drivers/target/iscsi/iscsi_target_auth.c b/drivers/target/iscsi/iscsi_target_auth.c new file mode 100644 index 000000000000..11fd74307811 --- /dev/null +++ b/drivers/target/iscsi/iscsi_target_auth.c | |||
@@ -0,0 +1,490 @@ | |||
1 | /******************************************************************************* | ||
2 | * This file houses the main functions for the iSCSI CHAP support | ||
3 | * | ||
4 | * \u00a9 Copyright 2007-2011 RisingTide Systems LLC. | ||
5 | * | ||
6 | * Licensed to the Linux Foundation under the General Public License (GPL) version 2. | ||
7 | * | ||
8 | * Author: Nicholas A. Bellinger <nab@linux-iscsi.org> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | ******************************************************************************/ | ||
20 | |||
21 | #include <linux/string.h> | ||
22 | #include <linux/crypto.h> | ||
23 | #include <linux/err.h> | ||
24 | #include <linux/scatterlist.h> | ||
25 | |||
26 | #include "iscsi_target_core.h" | ||
27 | #include "iscsi_target_nego.h" | ||
28 | #include "iscsi_target_auth.h" | ||
29 | |||
30 | static unsigned char chap_asciihex_to_binaryhex(unsigned char val[2]) | ||
31 | { | ||
32 | unsigned char result = 0; | ||
33 | /* | ||
34 | * MSB | ||
35 | */ | ||
36 | if ((val[0] >= 'a') && (val[0] <= 'f')) | ||
37 | result = ((val[0] - 'a' + 10) & 0xf) << 4; | ||
38 | else | ||
39 | if ((val[0] >= 'A') && (val[0] <= 'F')) | ||
40 | result = ((val[0] - 'A' + 10) & 0xf) << 4; | ||
41 | else /* digit */ | ||
42 | result = ((val[0] - '0') & 0xf) << 4; | ||
43 | /* | ||
44 | * LSB | ||
45 | */ | ||
46 | if ((val[1] >= 'a') && (val[1] <= 'f')) | ||
47 | result |= ((val[1] - 'a' + 10) & 0xf); | ||
48 | else | ||
49 | if ((val[1] >= 'A') && (val[1] <= 'F')) | ||
50 | result |= ((val[1] - 'A' + 10) & 0xf); | ||
51 | else /* digit */ | ||
52 | result |= ((val[1] - '0') & 0xf); | ||
53 | |||
54 | return result; | ||
55 | } | ||
56 | |||
57 | static int chap_string_to_hex(unsigned char *dst, unsigned char *src, int len) | ||
58 | { | ||
59 | int i, j = 0; | ||
60 | |||
61 | for (i = 0; i < len; i += 2) { | ||
62 | dst[j++] = (unsigned char) chap_asciihex_to_binaryhex(&src[i]); | ||
63 | } | ||
64 | |||
65 | dst[j] = '\0'; | ||
66 | return j; | ||
67 | } | ||
68 | |||
69 | static void chap_binaryhex_to_asciihex(char *dst, char *src, int src_len) | ||
70 | { | ||
71 | int i; | ||
72 | |||
73 | for (i = 0; i < src_len; i++) { | ||
74 | sprintf(&dst[i*2], "%02x", (int) src[i] & 0xff); | ||
75 | } | ||
76 | } | ||
77 | |||
78 | static void chap_set_random(char *data, int length) | ||
79 | { | ||
80 | long r; | ||
81 | unsigned n; | ||
82 | |||
83 | while (length > 0) { | ||
84 | get_random_bytes(&r, sizeof(long)); | ||
85 | r = r ^ (r >> 8); | ||
86 | r = r ^ (r >> 4); | ||
87 | n = r & 0x7; | ||
88 | |||
89 | get_random_bytes(&r, sizeof(long)); | ||
90 | r = r ^ (r >> 8); | ||
91 | r = r ^ (r >> 5); | ||
92 | n = (n << 3) | (r & 0x7); | ||
93 | |||
94 | get_random_bytes(&r, sizeof(long)); | ||
95 | r = r ^ (r >> 8); | ||
96 | r = r ^ (r >> 5); | ||
97 | n = (n << 2) | (r & 0x3); | ||
98 | |||
99 | *data++ = n; | ||
100 | length--; | ||
101 | } | ||
102 | } | ||
103 | |||
104 | static void chap_gen_challenge( | ||
105 | struct iscsi_conn *conn, | ||
106 | int caller, | ||
107 | char *c_str, | ||
108 | unsigned int *c_len) | ||
109 | { | ||
110 | unsigned char challenge_asciihex[CHAP_CHALLENGE_LENGTH * 2 + 1]; | ||
111 | struct iscsi_chap *chap = (struct iscsi_chap *) conn->auth_protocol; | ||
112 | |||
113 | memset(challenge_asciihex, 0, CHAP_CHALLENGE_LENGTH * 2 + 1); | ||
114 | |||
115 | chap_set_random(chap->challenge, CHAP_CHALLENGE_LENGTH); | ||
116 | chap_binaryhex_to_asciihex(challenge_asciihex, chap->challenge, | ||
117 | CHAP_CHALLENGE_LENGTH); | ||
118 | /* | ||
119 | * Set CHAP_C, and copy the generated challenge into c_str. | ||
120 | */ | ||
121 | *c_len += sprintf(c_str + *c_len, "CHAP_C=0x%s", challenge_asciihex); | ||
122 | *c_len += 1; | ||
123 | |||
124 | pr_debug("[%s] Sending CHAP_C=0x%s\n\n", (caller) ? "server" : "client", | ||
125 | challenge_asciihex); | ||
126 | } | ||
127 | |||
128 | |||
129 | static struct iscsi_chap *chap_server_open( | ||
130 | struct iscsi_conn *conn, | ||
131 | struct iscsi_node_auth *auth, | ||
132 | const char *a_str, | ||
133 | char *aic_str, | ||
134 | unsigned int *aic_len) | ||
135 | { | ||
136 | struct iscsi_chap *chap; | ||
137 | |||
138 | if (!(auth->naf_flags & NAF_USERID_SET) || | ||
139 | !(auth->naf_flags & NAF_PASSWORD_SET)) { | ||
140 | pr_err("CHAP user or password not set for" | ||
141 | " Initiator ACL\n"); | ||
142 | return NULL; | ||
143 | } | ||
144 | |||
145 | conn->auth_protocol = kzalloc(sizeof(struct iscsi_chap), GFP_KERNEL); | ||
146 | if (!conn->auth_protocol) | ||
147 | return NULL; | ||
148 | |||
149 | chap = (struct iscsi_chap *) conn->auth_protocol; | ||
150 | /* | ||
151 | * We only support MD5 MDA presently. | ||
152 | */ | ||
153 | if (strncmp(a_str, "CHAP_A=5", 8)) { | ||
154 | pr_err("CHAP_A is not MD5.\n"); | ||
155 | return NULL; | ||
156 | } | ||
157 | pr_debug("[server] Got CHAP_A=5\n"); | ||
158 | /* | ||
159 | * Send back CHAP_A set to MD5. | ||
160 | */ | ||
161 | *aic_len = sprintf(aic_str, "CHAP_A=5"); | ||
162 | *aic_len += 1; | ||
163 | chap->digest_type = CHAP_DIGEST_MD5; | ||
164 | pr_debug("[server] Sending CHAP_A=%d\n", chap->digest_type); | ||
165 | /* | ||
166 | * Set Identifier. | ||
167 | */ | ||
168 | chap->id = ISCSI_TPG_C(conn)->tpg_chap_id++; | ||
169 | *aic_len += sprintf(aic_str + *aic_len, "CHAP_I=%d", chap->id); | ||
170 | *aic_len += 1; | ||
171 | pr_debug("[server] Sending CHAP_I=%d\n", chap->id); | ||
172 | /* | ||
173 | * Generate Challenge. | ||
174 | */ | ||
175 | chap_gen_challenge(conn, 1, aic_str, aic_len); | ||
176 | |||
177 | return chap; | ||
178 | } | ||
179 | |||
180 | static void chap_close(struct iscsi_conn *conn) | ||
181 | { | ||
182 | kfree(conn->auth_protocol); | ||
183 | conn->auth_protocol = NULL; | ||
184 | } | ||
185 | |||
186 | static int chap_server_compute_md5( | ||
187 | struct iscsi_conn *conn, | ||
188 | struct iscsi_node_auth *auth, | ||
189 | char *nr_in_ptr, | ||
190 | char *nr_out_ptr, | ||
191 | unsigned int *nr_out_len) | ||
192 | { | ||
193 | char *endptr; | ||
194 | unsigned char id, digest[MD5_SIGNATURE_SIZE]; | ||
195 | unsigned char type, response[MD5_SIGNATURE_SIZE * 2 + 2]; | ||
196 | unsigned char identifier[10], *challenge = NULL; | ||
197 | unsigned char *challenge_binhex = NULL; | ||
198 | unsigned char client_digest[MD5_SIGNATURE_SIZE]; | ||
199 | unsigned char server_digest[MD5_SIGNATURE_SIZE]; | ||
200 | unsigned char chap_n[MAX_CHAP_N_SIZE], chap_r[MAX_RESPONSE_LENGTH]; | ||
201 | struct iscsi_chap *chap = (struct iscsi_chap *) conn->auth_protocol; | ||
202 | struct crypto_hash *tfm; | ||
203 | struct hash_desc desc; | ||
204 | struct scatterlist sg; | ||
205 | int auth_ret = -1, ret, challenge_len; | ||
206 | |||
207 | memset(identifier, 0, 10); | ||
208 | memset(chap_n, 0, MAX_CHAP_N_SIZE); | ||
209 | memset(chap_r, 0, MAX_RESPONSE_LENGTH); | ||
210 | memset(digest, 0, MD5_SIGNATURE_SIZE); | ||
211 | memset(response, 0, MD5_SIGNATURE_SIZE * 2 + 2); | ||
212 | memset(client_digest, 0, MD5_SIGNATURE_SIZE); | ||
213 | memset(server_digest, 0, MD5_SIGNATURE_SIZE); | ||
214 | |||
215 | challenge = kzalloc(CHAP_CHALLENGE_STR_LEN, GFP_KERNEL); | ||
216 | if (!challenge) { | ||
217 | pr_err("Unable to allocate challenge buffer\n"); | ||
218 | goto out; | ||
219 | } | ||
220 | |||
221 | challenge_binhex = kzalloc(CHAP_CHALLENGE_STR_LEN, GFP_KERNEL); | ||
222 | if (!challenge_binhex) { | ||
223 | pr_err("Unable to allocate challenge_binhex buffer\n"); | ||
224 | goto out; | ||
225 | } | ||
226 | /* | ||
227 | * Extract CHAP_N. | ||
228 | */ | ||
229 | if (extract_param(nr_in_ptr, "CHAP_N", MAX_CHAP_N_SIZE, chap_n, | ||
230 | &type) < 0) { | ||
231 | pr_err("Could not find CHAP_N.\n"); | ||
232 | goto out; | ||
233 | } | ||
234 | if (type == HEX) { | ||
235 | pr_err("Could not find CHAP_N.\n"); | ||
236 | goto out; | ||
237 | } | ||
238 | |||
239 | if (memcmp(chap_n, auth->userid, strlen(auth->userid)) != 0) { | ||
240 | pr_err("CHAP_N values do not match!\n"); | ||
241 | goto out; | ||
242 | } | ||
243 | pr_debug("[server] Got CHAP_N=%s\n", chap_n); | ||
244 | /* | ||
245 | * Extract CHAP_R. | ||
246 | */ | ||
247 | if (extract_param(nr_in_ptr, "CHAP_R", MAX_RESPONSE_LENGTH, chap_r, | ||
248 | &type) < 0) { | ||
249 | pr_err("Could not find CHAP_R.\n"); | ||
250 | goto out; | ||
251 | } | ||
252 | if (type != HEX) { | ||
253 | pr_err("Could not find CHAP_R.\n"); | ||
254 | goto out; | ||
255 | } | ||
256 | |||
257 | pr_debug("[server] Got CHAP_R=%s\n", chap_r); | ||
258 | chap_string_to_hex(client_digest, chap_r, strlen(chap_r)); | ||
259 | |||
260 | tfm = crypto_alloc_hash("md5", 0, CRYPTO_ALG_ASYNC); | ||
261 | if (IS_ERR(tfm)) { | ||
262 | pr_err("Unable to allocate struct crypto_hash\n"); | ||
263 | goto out; | ||
264 | } | ||
265 | desc.tfm = tfm; | ||
266 | desc.flags = 0; | ||
267 | |||
268 | ret = crypto_hash_init(&desc); | ||
269 | if (ret < 0) { | ||
270 | pr_err("crypto_hash_init() failed\n"); | ||
271 | crypto_free_hash(tfm); | ||
272 | goto out; | ||
273 | } | ||
274 | |||
275 | sg_init_one(&sg, (void *)&chap->id, 1); | ||
276 | ret = crypto_hash_update(&desc, &sg, 1); | ||
277 | if (ret < 0) { | ||
278 | pr_err("crypto_hash_update() failed for id\n"); | ||
279 | crypto_free_hash(tfm); | ||
280 | goto out; | ||
281 | } | ||
282 | |||
283 | sg_init_one(&sg, (void *)&auth->password, strlen(auth->password)); | ||
284 | ret = crypto_hash_update(&desc, &sg, strlen(auth->password)); | ||
285 | if (ret < 0) { | ||
286 | pr_err("crypto_hash_update() failed for password\n"); | ||
287 | crypto_free_hash(tfm); | ||
288 | goto out; | ||
289 | } | ||
290 | |||
291 | sg_init_one(&sg, (void *)chap->challenge, CHAP_CHALLENGE_LENGTH); | ||
292 | ret = crypto_hash_update(&desc, &sg, CHAP_CHALLENGE_LENGTH); | ||
293 | if (ret < 0) { | ||
294 | pr_err("crypto_hash_update() failed for challenge\n"); | ||
295 | crypto_free_hash(tfm); | ||
296 | goto out; | ||
297 | } | ||
298 | |||
299 | ret = crypto_hash_final(&desc, server_digest); | ||
300 | if (ret < 0) { | ||
301 | pr_err("crypto_hash_final() failed for server digest\n"); | ||
302 | crypto_free_hash(tfm); | ||
303 | goto out; | ||
304 | } | ||
305 | crypto_free_hash(tfm); | ||
306 | |||
307 | chap_binaryhex_to_asciihex(response, server_digest, MD5_SIGNATURE_SIZE); | ||
308 | pr_debug("[server] MD5 Server Digest: %s\n", response); | ||
309 | |||
310 | if (memcmp(server_digest, client_digest, MD5_SIGNATURE_SIZE) != 0) { | ||
311 | pr_debug("[server] MD5 Digests do not match!\n\n"); | ||
312 | goto out; | ||
313 | } else | ||
314 | pr_debug("[server] MD5 Digests match, CHAP connetication" | ||
315 | " successful.\n\n"); | ||
316 | /* | ||
317 | * One way authentication has succeeded, return now if mutual | ||
318 | * authentication is not enabled. | ||
319 | */ | ||
320 | if (!auth->authenticate_target) { | ||
321 | kfree(challenge); | ||
322 | kfree(challenge_binhex); | ||
323 | return 0; | ||
324 | } | ||
325 | /* | ||
326 | * Get CHAP_I. | ||
327 | */ | ||
328 | if (extract_param(nr_in_ptr, "CHAP_I", 10, identifier, &type) < 0) { | ||
329 | pr_err("Could not find CHAP_I.\n"); | ||
330 | goto out; | ||
331 | } | ||
332 | |||
333 | if (type == HEX) | ||
334 | id = (unsigned char)simple_strtoul((char *)&identifier[2], | ||
335 | &endptr, 0); | ||
336 | else | ||
337 | id = (unsigned char)simple_strtoul(identifier, &endptr, 0); | ||
338 | /* | ||
339 | * RFC 1994 says Identifier is no more than octet (8 bits). | ||
340 | */ | ||
341 | pr_debug("[server] Got CHAP_I=%d\n", id); | ||
342 | /* | ||
343 | * Get CHAP_C. | ||
344 | */ | ||
345 | if (extract_param(nr_in_ptr, "CHAP_C", CHAP_CHALLENGE_STR_LEN, | ||
346 | challenge, &type) < 0) { | ||
347 | pr_err("Could not find CHAP_C.\n"); | ||
348 | goto out; | ||
349 | } | ||
350 | |||
351 | if (type != HEX) { | ||
352 | pr_err("Could not find CHAP_C.\n"); | ||
353 | goto out; | ||
354 | } | ||
355 | pr_debug("[server] Got CHAP_C=%s\n", challenge); | ||
356 | challenge_len = chap_string_to_hex(challenge_binhex, challenge, | ||
357 | strlen(challenge)); | ||
358 | if (!challenge_len) { | ||
359 | pr_err("Unable to convert incoming challenge\n"); | ||
360 | goto out; | ||
361 | } | ||
362 | /* | ||
363 | * Generate CHAP_N and CHAP_R for mutual authentication. | ||
364 | */ | ||
365 | tfm = crypto_alloc_hash("md5", 0, CRYPTO_ALG_ASYNC); | ||
366 | if (IS_ERR(tfm)) { | ||
367 | pr_err("Unable to allocate struct crypto_hash\n"); | ||
368 | goto out; | ||
369 | } | ||
370 | desc.tfm = tfm; | ||
371 | desc.flags = 0; | ||
372 | |||
373 | ret = crypto_hash_init(&desc); | ||
374 | if (ret < 0) { | ||
375 | pr_err("crypto_hash_init() failed\n"); | ||
376 | crypto_free_hash(tfm); | ||
377 | goto out; | ||
378 | } | ||
379 | |||
380 | sg_init_one(&sg, (void *)&id, 1); | ||
381 | ret = crypto_hash_update(&desc, &sg, 1); | ||
382 | if (ret < 0) { | ||
383 | pr_err("crypto_hash_update() failed for id\n"); | ||
384 | crypto_free_hash(tfm); | ||
385 | goto out; | ||
386 | } | ||
387 | |||
388 | sg_init_one(&sg, (void *)auth->password_mutual, | ||
389 | strlen(auth->password_mutual)); | ||
390 | ret = crypto_hash_update(&desc, &sg, strlen(auth->password_mutual)); | ||
391 | if (ret < 0) { | ||
392 | pr_err("crypto_hash_update() failed for" | ||
393 | " password_mutual\n"); | ||
394 | crypto_free_hash(tfm); | ||
395 | goto out; | ||
396 | } | ||
397 | /* | ||
398 | * Convert received challenge to binary hex. | ||
399 | */ | ||
400 | sg_init_one(&sg, (void *)challenge_binhex, challenge_len); | ||
401 | ret = crypto_hash_update(&desc, &sg, challenge_len); | ||
402 | if (ret < 0) { | ||
403 | pr_err("crypto_hash_update() failed for ma challenge\n"); | ||
404 | crypto_free_hash(tfm); | ||
405 | goto out; | ||
406 | } | ||
407 | |||
408 | ret = crypto_hash_final(&desc, digest); | ||
409 | if (ret < 0) { | ||
410 | pr_err("crypto_hash_final() failed for ma digest\n"); | ||
411 | crypto_free_hash(tfm); | ||
412 | goto out; | ||
413 | } | ||
414 | crypto_free_hash(tfm); | ||
415 | /* | ||
416 | * Generate CHAP_N and CHAP_R. | ||
417 | */ | ||
418 | *nr_out_len = sprintf(nr_out_ptr, "CHAP_N=%s", auth->userid_mutual); | ||
419 | *nr_out_len += 1; | ||
420 | pr_debug("[server] Sending CHAP_N=%s\n", auth->userid_mutual); | ||
421 | /* | ||
422 | * Convert response from binary hex to ascii hext. | ||
423 | */ | ||
424 | chap_binaryhex_to_asciihex(response, digest, MD5_SIGNATURE_SIZE); | ||
425 | *nr_out_len += sprintf(nr_out_ptr + *nr_out_len, "CHAP_R=0x%s", | ||
426 | response); | ||
427 | *nr_out_len += 1; | ||
428 | pr_debug("[server] Sending CHAP_R=0x%s\n", response); | ||
429 | auth_ret = 0; | ||
430 | out: | ||
431 | kfree(challenge); | ||
432 | kfree(challenge_binhex); | ||
433 | return auth_ret; | ||
434 | } | ||
435 | |||
436 | static int chap_got_response( | ||
437 | struct iscsi_conn *conn, | ||
438 | struct iscsi_node_auth *auth, | ||
439 | char *nr_in_ptr, | ||
440 | char *nr_out_ptr, | ||
441 | unsigned int *nr_out_len) | ||
442 | { | ||
443 | struct iscsi_chap *chap = (struct iscsi_chap *) conn->auth_protocol; | ||
444 | |||
445 | switch (chap->digest_type) { | ||
446 | case CHAP_DIGEST_MD5: | ||
447 | if (chap_server_compute_md5(conn, auth, nr_in_ptr, | ||
448 | nr_out_ptr, nr_out_len) < 0) | ||
449 | return -1; | ||
450 | return 0; | ||
451 | default: | ||
452 | pr_err("Unknown CHAP digest type %d!\n", | ||
453 | chap->digest_type); | ||
454 | return -1; | ||
455 | } | ||
456 | } | ||
457 | |||
458 | u32 chap_main_loop( | ||
459 | struct iscsi_conn *conn, | ||
460 | struct iscsi_node_auth *auth, | ||
461 | char *in_text, | ||
462 | char *out_text, | ||
463 | int *in_len, | ||
464 | int *out_len) | ||
465 | { | ||
466 | struct iscsi_chap *chap = (struct iscsi_chap *) conn->auth_protocol; | ||
467 | |||
468 | if (!chap) { | ||
469 | chap = chap_server_open(conn, auth, in_text, out_text, out_len); | ||
470 | if (!chap) | ||
471 | return 2; | ||
472 | chap->chap_state = CHAP_STAGE_SERVER_AIC; | ||
473 | return 0; | ||
474 | } else if (chap->chap_state == CHAP_STAGE_SERVER_AIC) { | ||
475 | convert_null_to_semi(in_text, *in_len); | ||
476 | if (chap_got_response(conn, auth, in_text, out_text, | ||
477 | out_len) < 0) { | ||
478 | chap_close(conn); | ||
479 | return 2; | ||
480 | } | ||
481 | if (auth->authenticate_target) | ||
482 | chap->chap_state = CHAP_STAGE_SERVER_NR; | ||
483 | else | ||
484 | *out_len = 0; | ||
485 | chap_close(conn); | ||
486 | return 1; | ||
487 | } | ||
488 | |||
489 | return 2; | ||
490 | } | ||
diff --git a/drivers/target/iscsi/iscsi_target_auth.h b/drivers/target/iscsi/iscsi_target_auth.h new file mode 100644 index 000000000000..2f463c09626d --- /dev/null +++ b/drivers/target/iscsi/iscsi_target_auth.h | |||
@@ -0,0 +1,31 @@ | |||
1 | #ifndef _ISCSI_CHAP_H_ | ||
2 | #define _ISCSI_CHAP_H_ | ||
3 | |||
4 | #define CHAP_DIGEST_MD5 5 | ||
5 | #define CHAP_DIGEST_SHA 6 | ||
6 | |||
7 | #define CHAP_CHALLENGE_LENGTH 16 | ||
8 | #define CHAP_CHALLENGE_STR_LEN 4096 | ||
9 | #define MAX_RESPONSE_LENGTH 64 /* sufficient for MD5 */ | ||
10 | #define MAX_CHAP_N_SIZE 512 | ||
11 | |||
12 | #define MD5_SIGNATURE_SIZE 16 /* 16 bytes in a MD5 message digest */ | ||
13 | |||
14 | #define CHAP_STAGE_CLIENT_A 1 | ||
15 | #define CHAP_STAGE_SERVER_AIC 2 | ||
16 | #define CHAP_STAGE_CLIENT_NR 3 | ||
17 | #define CHAP_STAGE_CLIENT_NRIC 4 | ||
18 | #define CHAP_STAGE_SERVER_NR 5 | ||
19 | |||
20 | extern u32 chap_main_loop(struct iscsi_conn *, struct iscsi_node_auth *, char *, char *, | ||
21 | int *, int *); | ||
22 | |||
23 | struct iscsi_chap { | ||
24 | unsigned char digest_type; | ||
25 | unsigned char id; | ||
26 | unsigned char challenge[CHAP_CHALLENGE_LENGTH]; | ||
27 | unsigned int authenticate_target; | ||
28 | unsigned int chap_state; | ||
29 | } ____cacheline_aligned; | ||
30 | |||
31 | #endif /*** _ISCSI_CHAP_H_ ***/ | ||
diff --git a/drivers/target/iscsi/iscsi_target_configfs.c b/drivers/target/iscsi/iscsi_target_configfs.c new file mode 100644 index 000000000000..32bb92c44450 --- /dev/null +++ b/drivers/target/iscsi/iscsi_target_configfs.c | |||
@@ -0,0 +1,1882 @@ | |||
1 | /******************************************************************************* | ||
2 | * This file contains the configfs implementation for iSCSI Target mode | ||
3 | * from the LIO-Target Project. | ||
4 | * | ||
5 | * \u00a9 Copyright 2007-2011 RisingTide Systems LLC. | ||
6 | * | ||
7 | * Licensed to the Linux Foundation under the General Public License (GPL) version 2. | ||
8 | * | ||
9 | * Author: Nicholas A. Bellinger <nab@linux-iscsi.org> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or | ||
14 | * (at your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | * GNU General Public License for more details. | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #include <linux/configfs.h> | ||
23 | #include <target/target_core_base.h> | ||
24 | #include <target/target_core_transport.h> | ||
25 | #include <target/target_core_fabric_ops.h> | ||
26 | #include <target/target_core_fabric_configfs.h> | ||
27 | #include <target/target_core_fabric_lib.h> | ||
28 | #include <target/target_core_device.h> | ||
29 | #include <target/target_core_tpg.h> | ||
30 | #include <target/target_core_configfs.h> | ||
31 | #include <target/configfs_macros.h> | ||
32 | |||
33 | #include "iscsi_target_core.h" | ||
34 | #include "iscsi_target_parameters.h" | ||
35 | #include "iscsi_target_device.h" | ||
36 | #include "iscsi_target_erl0.h" | ||
37 | #include "iscsi_target_nodeattrib.h" | ||
38 | #include "iscsi_target_tpg.h" | ||
39 | #include "iscsi_target_util.h" | ||
40 | #include "iscsi_target.h" | ||
41 | #include "iscsi_target_stat.h" | ||
42 | #include "iscsi_target_configfs.h" | ||
43 | |||
44 | struct target_fabric_configfs *lio_target_fabric_configfs; | ||
45 | |||
46 | struct lio_target_configfs_attribute { | ||
47 | struct configfs_attribute attr; | ||
48 | ssize_t (*show)(void *, char *); | ||
49 | ssize_t (*store)(void *, const char *, size_t); | ||
50 | }; | ||
51 | |||
52 | struct iscsi_portal_group *lio_get_tpg_from_tpg_item( | ||
53 | struct config_item *item, | ||
54 | struct iscsi_tiqn **tiqn_out) | ||
55 | { | ||
56 | struct se_portal_group *se_tpg = container_of(to_config_group(item), | ||
57 | struct se_portal_group, tpg_group); | ||
58 | struct iscsi_portal_group *tpg = | ||
59 | (struct iscsi_portal_group *)se_tpg->se_tpg_fabric_ptr; | ||
60 | int ret; | ||
61 | |||
62 | if (!tpg) { | ||
63 | pr_err("Unable to locate struct iscsi_portal_group " | ||
64 | "pointer\n"); | ||
65 | return NULL; | ||
66 | } | ||
67 | ret = iscsit_get_tpg(tpg); | ||
68 | if (ret < 0) | ||
69 | return NULL; | ||
70 | |||
71 | *tiqn_out = tpg->tpg_tiqn; | ||
72 | return tpg; | ||
73 | } | ||
74 | |||
75 | /* Start items for lio_target_portal_cit */ | ||
76 | |||
77 | static ssize_t lio_target_np_show_sctp( | ||
78 | struct se_tpg_np *se_tpg_np, | ||
79 | char *page) | ||
80 | { | ||
81 | struct iscsi_tpg_np *tpg_np = container_of(se_tpg_np, | ||
82 | struct iscsi_tpg_np, se_tpg_np); | ||
83 | struct iscsi_tpg_np *tpg_np_sctp; | ||
84 | ssize_t rb; | ||
85 | |||
86 | tpg_np_sctp = iscsit_tpg_locate_child_np(tpg_np, ISCSI_SCTP_TCP); | ||
87 | if (tpg_np_sctp) | ||
88 | rb = sprintf(page, "1\n"); | ||
89 | else | ||
90 | rb = sprintf(page, "0\n"); | ||
91 | |||
92 | return rb; | ||
93 | } | ||
94 | |||
95 | static ssize_t lio_target_np_store_sctp( | ||
96 | struct se_tpg_np *se_tpg_np, | ||
97 | const char *page, | ||
98 | size_t count) | ||
99 | { | ||
100 | struct iscsi_np *np; | ||
101 | struct iscsi_portal_group *tpg; | ||
102 | struct iscsi_tpg_np *tpg_np = container_of(se_tpg_np, | ||
103 | struct iscsi_tpg_np, se_tpg_np); | ||
104 | struct iscsi_tpg_np *tpg_np_sctp = NULL; | ||
105 | char *endptr; | ||
106 | u32 op; | ||
107 | int ret; | ||
108 | |||
109 | op = simple_strtoul(page, &endptr, 0); | ||
110 | if ((op != 1) && (op != 0)) { | ||
111 | pr_err("Illegal value for tpg_enable: %u\n", op); | ||
112 | return -EINVAL; | ||
113 | } | ||
114 | np = tpg_np->tpg_np; | ||
115 | if (!np) { | ||
116 | pr_err("Unable to locate struct iscsi_np from" | ||
117 | " struct iscsi_tpg_np\n"); | ||
118 | return -EINVAL; | ||
119 | } | ||
120 | |||
121 | tpg = tpg_np->tpg; | ||
122 | if (iscsit_get_tpg(tpg) < 0) | ||
123 | return -EINVAL; | ||
124 | |||
125 | if (op) { | ||
126 | /* | ||
127 | * Use existing np->np_sockaddr for SCTP network portal reference | ||
128 | */ | ||
129 | tpg_np_sctp = iscsit_tpg_add_network_portal(tpg, &np->np_sockaddr, | ||
130 | np->np_ip, tpg_np, ISCSI_SCTP_TCP); | ||
131 | if (!tpg_np_sctp || IS_ERR(tpg_np_sctp)) | ||
132 | goto out; | ||
133 | } else { | ||
134 | tpg_np_sctp = iscsit_tpg_locate_child_np(tpg_np, ISCSI_SCTP_TCP); | ||
135 | if (!tpg_np_sctp) | ||
136 | goto out; | ||
137 | |||
138 | ret = iscsit_tpg_del_network_portal(tpg, tpg_np_sctp); | ||
139 | if (ret < 0) | ||
140 | goto out; | ||
141 | } | ||
142 | |||
143 | iscsit_put_tpg(tpg); | ||
144 | return count; | ||
145 | out: | ||
146 | iscsit_put_tpg(tpg); | ||
147 | return -EINVAL; | ||
148 | } | ||
149 | |||
150 | TF_NP_BASE_ATTR(lio_target, sctp, S_IRUGO | S_IWUSR); | ||
151 | |||
152 | static struct configfs_attribute *lio_target_portal_attrs[] = { | ||
153 | &lio_target_np_sctp.attr, | ||
154 | NULL, | ||
155 | }; | ||
156 | |||
157 | /* Stop items for lio_target_portal_cit */ | ||
158 | |||
159 | /* Start items for lio_target_np_cit */ | ||
160 | |||
161 | #define MAX_PORTAL_LEN 256 | ||
162 | |||
163 | struct se_tpg_np *lio_target_call_addnptotpg( | ||
164 | struct se_portal_group *se_tpg, | ||
165 | struct config_group *group, | ||
166 | const char *name) | ||
167 | { | ||
168 | struct iscsi_portal_group *tpg; | ||
169 | struct iscsi_tpg_np *tpg_np; | ||
170 | char *str, *str2, *ip_str, *port_str; | ||
171 | struct __kernel_sockaddr_storage sockaddr; | ||
172 | struct sockaddr_in *sock_in; | ||
173 | struct sockaddr_in6 *sock_in6; | ||
174 | unsigned long port; | ||
175 | int ret; | ||
176 | char buf[MAX_PORTAL_LEN + 1]; | ||
177 | |||
178 | if (strlen(name) > MAX_PORTAL_LEN) { | ||
179 | pr_err("strlen(name): %d exceeds MAX_PORTAL_LEN: %d\n", | ||
180 | (int)strlen(name), MAX_PORTAL_LEN); | ||
181 | return ERR_PTR(-EOVERFLOW); | ||
182 | } | ||
183 | memset(buf, 0, MAX_PORTAL_LEN + 1); | ||
184 | snprintf(buf, MAX_PORTAL_LEN, "%s", name); | ||
185 | |||
186 | memset(&sockaddr, 0, sizeof(struct __kernel_sockaddr_storage)); | ||
187 | |||
188 | str = strstr(buf, "["); | ||
189 | if (str) { | ||
190 | const char *end; | ||
191 | |||
192 | str2 = strstr(str, "]"); | ||
193 | if (!str2) { | ||
194 | pr_err("Unable to locate trailing \"]\"" | ||
195 | " in IPv6 iSCSI network portal address\n"); | ||
196 | return ERR_PTR(-EINVAL); | ||
197 | } | ||
198 | str++; /* Skip over leading "[" */ | ||
199 | *str2 = '\0'; /* Terminate the IPv6 address */ | ||
200 | str2++; /* Skip over the "]" */ | ||
201 | port_str = strstr(str2, ":"); | ||
202 | if (!port_str) { | ||
203 | pr_err("Unable to locate \":port\"" | ||
204 | " in IPv6 iSCSI network portal address\n"); | ||
205 | return ERR_PTR(-EINVAL); | ||
206 | } | ||
207 | *port_str = '\0'; /* Terminate string for IP */ | ||
208 | port_str++; /* Skip over ":" */ | ||
209 | |||
210 | ret = strict_strtoul(port_str, 0, &port); | ||
211 | if (ret < 0) { | ||
212 | pr_err("strict_strtoul() failed for port_str: %d\n", ret); | ||
213 | return ERR_PTR(ret); | ||
214 | } | ||
215 | sock_in6 = (struct sockaddr_in6 *)&sockaddr; | ||
216 | sock_in6->sin6_family = AF_INET6; | ||
217 | sock_in6->sin6_port = htons((unsigned short)port); | ||
218 | ret = in6_pton(str, IPV6_ADDRESS_SPACE, | ||
219 | (void *)&sock_in6->sin6_addr.in6_u, -1, &end); | ||
220 | if (ret <= 0) { | ||
221 | pr_err("in6_pton returned: %d\n", ret); | ||
222 | return ERR_PTR(-EINVAL); | ||
223 | } | ||
224 | } else { | ||
225 | str = ip_str = &buf[0]; | ||
226 | port_str = strstr(ip_str, ":"); | ||
227 | if (!port_str) { | ||
228 | pr_err("Unable to locate \":port\"" | ||
229 | " in IPv4 iSCSI network portal address\n"); | ||
230 | return ERR_PTR(-EINVAL); | ||
231 | } | ||
232 | *port_str = '\0'; /* Terminate string for IP */ | ||
233 | port_str++; /* Skip over ":" */ | ||
234 | |||
235 | ret = strict_strtoul(port_str, 0, &port); | ||
236 | if (ret < 0) { | ||
237 | pr_err("strict_strtoul() failed for port_str: %d\n", ret); | ||
238 | return ERR_PTR(ret); | ||
239 | } | ||
240 | sock_in = (struct sockaddr_in *)&sockaddr; | ||
241 | sock_in->sin_family = AF_INET; | ||
242 | sock_in->sin_port = htons((unsigned short)port); | ||
243 | sock_in->sin_addr.s_addr = in_aton(ip_str); | ||
244 | } | ||
245 | tpg = container_of(se_tpg, struct iscsi_portal_group, tpg_se_tpg); | ||
246 | ret = iscsit_get_tpg(tpg); | ||
247 | if (ret < 0) | ||
248 | return ERR_PTR(-EINVAL); | ||
249 | |||
250 | pr_debug("LIO_Target_ConfigFS: REGISTER -> %s TPGT: %hu" | ||
251 | " PORTAL: %s\n", | ||
252 | config_item_name(&se_tpg->se_tpg_wwn->wwn_group.cg_item), | ||
253 | tpg->tpgt, name); | ||
254 | /* | ||
255 | * Assume ISCSI_TCP by default. Other network portals for other | ||
256 | * iSCSI fabrics: | ||
257 | * | ||
258 | * Traditional iSCSI over SCTP (initial support) | ||
259 | * iSER/TCP (TODO, hardware available) | ||
260 | * iSER/SCTP (TODO, software emulation with osc-iwarp) | ||
261 | * iSER/IB (TODO, hardware available) | ||
262 | * | ||
263 | * can be enabled with atributes under | ||
264 | * sys/kernel/config/iscsi/$IQN/$TPG/np/$IP:$PORT/ | ||
265 | * | ||
266 | */ | ||
267 | tpg_np = iscsit_tpg_add_network_portal(tpg, &sockaddr, str, NULL, | ||
268 | ISCSI_TCP); | ||
269 | if (IS_ERR(tpg_np)) { | ||
270 | iscsit_put_tpg(tpg); | ||
271 | return ERR_PTR(PTR_ERR(tpg_np)); | ||
272 | } | ||
273 | pr_debug("LIO_Target_ConfigFS: addnptotpg done!\n"); | ||
274 | |||
275 | iscsit_put_tpg(tpg); | ||
276 | return &tpg_np->se_tpg_np; | ||
277 | } | ||
278 | |||
279 | static void lio_target_call_delnpfromtpg( | ||
280 | struct se_tpg_np *se_tpg_np) | ||
281 | { | ||
282 | struct iscsi_portal_group *tpg; | ||
283 | struct iscsi_tpg_np *tpg_np; | ||
284 | struct se_portal_group *se_tpg; | ||
285 | int ret; | ||
286 | |||
287 | tpg_np = container_of(se_tpg_np, struct iscsi_tpg_np, se_tpg_np); | ||
288 | tpg = tpg_np->tpg; | ||
289 | ret = iscsit_get_tpg(tpg); | ||
290 | if (ret < 0) | ||
291 | return; | ||
292 | |||
293 | se_tpg = &tpg->tpg_se_tpg; | ||
294 | pr_debug("LIO_Target_ConfigFS: DEREGISTER -> %s TPGT: %hu" | ||
295 | " PORTAL: %s:%hu\n", config_item_name(&se_tpg->se_tpg_wwn->wwn_group.cg_item), | ||
296 | tpg->tpgt, tpg_np->tpg_np->np_ip, tpg_np->tpg_np->np_port); | ||
297 | |||
298 | ret = iscsit_tpg_del_network_portal(tpg, tpg_np); | ||
299 | if (ret < 0) | ||
300 | goto out; | ||
301 | |||
302 | pr_debug("LIO_Target_ConfigFS: delnpfromtpg done!\n"); | ||
303 | out: | ||
304 | iscsit_put_tpg(tpg); | ||
305 | } | ||
306 | |||
307 | /* End items for lio_target_np_cit */ | ||
308 | |||
309 | /* Start items for lio_target_nacl_attrib_cit */ | ||
310 | |||
311 | #define DEF_NACL_ATTRIB(name) \ | ||
312 | static ssize_t iscsi_nacl_attrib_show_##name( \ | ||
313 | struct se_node_acl *se_nacl, \ | ||
314 | char *page) \ | ||
315 | { \ | ||
316 | struct iscsi_node_acl *nacl = container_of(se_nacl, struct iscsi_node_acl, \ | ||
317 | se_node_acl); \ | ||
318 | \ | ||
319 | return sprintf(page, "%u\n", ISCSI_NODE_ATTRIB(nacl)->name); \ | ||
320 | } \ | ||
321 | \ | ||
322 | static ssize_t iscsi_nacl_attrib_store_##name( \ | ||
323 | struct se_node_acl *se_nacl, \ | ||
324 | const char *page, \ | ||
325 | size_t count) \ | ||
326 | { \ | ||
327 | struct iscsi_node_acl *nacl = container_of(se_nacl, struct iscsi_node_acl, \ | ||
328 | se_node_acl); \ | ||
329 | char *endptr; \ | ||
330 | u32 val; \ | ||
331 | int ret; \ | ||
332 | \ | ||
333 | val = simple_strtoul(page, &endptr, 0); \ | ||
334 | ret = iscsit_na_##name(nacl, val); \ | ||
335 | if (ret < 0) \ | ||
336 | return ret; \ | ||
337 | \ | ||
338 | return count; \ | ||
339 | } | ||
340 | |||
341 | #define NACL_ATTR(_name, _mode) TF_NACL_ATTRIB_ATTR(iscsi, _name, _mode); | ||
342 | /* | ||
343 | * Define iscsi_node_attrib_s_dataout_timeout | ||
344 | */ | ||
345 | DEF_NACL_ATTRIB(dataout_timeout); | ||
346 | NACL_ATTR(dataout_timeout, S_IRUGO | S_IWUSR); | ||
347 | /* | ||
348 | * Define iscsi_node_attrib_s_dataout_timeout_retries | ||
349 | */ | ||
350 | DEF_NACL_ATTRIB(dataout_timeout_retries); | ||
351 | NACL_ATTR(dataout_timeout_retries, S_IRUGO | S_IWUSR); | ||
352 | /* | ||
353 | * Define iscsi_node_attrib_s_default_erl | ||
354 | */ | ||
355 | DEF_NACL_ATTRIB(default_erl); | ||
356 | NACL_ATTR(default_erl, S_IRUGO | S_IWUSR); | ||
357 | /* | ||
358 | * Define iscsi_node_attrib_s_nopin_timeout | ||
359 | */ | ||
360 | DEF_NACL_ATTRIB(nopin_timeout); | ||
361 | NACL_ATTR(nopin_timeout, S_IRUGO | S_IWUSR); | ||
362 | /* | ||
363 | * Define iscsi_node_attrib_s_nopin_response_timeout | ||
364 | */ | ||
365 | DEF_NACL_ATTRIB(nopin_response_timeout); | ||
366 | NACL_ATTR(nopin_response_timeout, S_IRUGO | S_IWUSR); | ||
367 | /* | ||
368 | * Define iscsi_node_attrib_s_random_datain_pdu_offsets | ||
369 | */ | ||
370 | DEF_NACL_ATTRIB(random_datain_pdu_offsets); | ||
371 | NACL_ATTR(random_datain_pdu_offsets, S_IRUGO | S_IWUSR); | ||
372 | /* | ||
373 | * Define iscsi_node_attrib_s_random_datain_seq_offsets | ||
374 | */ | ||
375 | DEF_NACL_ATTRIB(random_datain_seq_offsets); | ||
376 | NACL_ATTR(random_datain_seq_offsets, S_IRUGO | S_IWUSR); | ||
377 | /* | ||
378 | * Define iscsi_node_attrib_s_random_r2t_offsets | ||
379 | */ | ||
380 | DEF_NACL_ATTRIB(random_r2t_offsets); | ||
381 | NACL_ATTR(random_r2t_offsets, S_IRUGO | S_IWUSR); | ||
382 | |||
383 | static struct configfs_attribute *lio_target_nacl_attrib_attrs[] = { | ||
384 | &iscsi_nacl_attrib_dataout_timeout.attr, | ||
385 | &iscsi_nacl_attrib_dataout_timeout_retries.attr, | ||
386 | &iscsi_nacl_attrib_default_erl.attr, | ||
387 | &iscsi_nacl_attrib_nopin_timeout.attr, | ||
388 | &iscsi_nacl_attrib_nopin_response_timeout.attr, | ||
389 | &iscsi_nacl_attrib_random_datain_pdu_offsets.attr, | ||
390 | &iscsi_nacl_attrib_random_datain_seq_offsets.attr, | ||
391 | &iscsi_nacl_attrib_random_r2t_offsets.attr, | ||
392 | NULL, | ||
393 | }; | ||
394 | |||
395 | /* End items for lio_target_nacl_attrib_cit */ | ||
396 | |||
397 | /* Start items for lio_target_nacl_auth_cit */ | ||
398 | |||
399 | #define __DEF_NACL_AUTH_STR(prefix, name, flags) \ | ||
400 | static ssize_t __iscsi_##prefix##_show_##name( \ | ||
401 | struct iscsi_node_acl *nacl, \ | ||
402 | char *page) \ | ||
403 | { \ | ||
404 | struct iscsi_node_auth *auth = &nacl->node_auth; \ | ||
405 | \ | ||
406 | if (!capable(CAP_SYS_ADMIN)) \ | ||
407 | return -EPERM; \ | ||
408 | return snprintf(page, PAGE_SIZE, "%s\n", auth->name); \ | ||
409 | } \ | ||
410 | \ | ||
411 | static ssize_t __iscsi_##prefix##_store_##name( \ | ||
412 | struct iscsi_node_acl *nacl, \ | ||
413 | const char *page, \ | ||
414 | size_t count) \ | ||
415 | { \ | ||
416 | struct iscsi_node_auth *auth = &nacl->node_auth; \ | ||
417 | \ | ||
418 | if (!capable(CAP_SYS_ADMIN)) \ | ||
419 | return -EPERM; \ | ||
420 | \ | ||
421 | snprintf(auth->name, PAGE_SIZE, "%s", page); \ | ||
422 | if (!strncmp("NULL", auth->name, 4)) \ | ||
423 | auth->naf_flags &= ~flags; \ | ||
424 | else \ | ||
425 | auth->naf_flags |= flags; \ | ||
426 | \ | ||
427 | if ((auth->naf_flags & NAF_USERID_IN_SET) && \ | ||
428 | (auth->naf_flags & NAF_PASSWORD_IN_SET)) \ | ||
429 | auth->authenticate_target = 1; \ | ||
430 | else \ | ||
431 | auth->authenticate_target = 0; \ | ||
432 | \ | ||
433 | return count; \ | ||
434 | } | ||
435 | |||
436 | #define __DEF_NACL_AUTH_INT(prefix, name) \ | ||
437 | static ssize_t __iscsi_##prefix##_show_##name( \ | ||
438 | struct iscsi_node_acl *nacl, \ | ||
439 | char *page) \ | ||
440 | { \ | ||
441 | struct iscsi_node_auth *auth = &nacl->node_auth; \ | ||
442 | \ | ||
443 | if (!capable(CAP_SYS_ADMIN)) \ | ||
444 | return -EPERM; \ | ||
445 | \ | ||
446 | return snprintf(page, PAGE_SIZE, "%d\n", auth->name); \ | ||
447 | } | ||
448 | |||
449 | #define DEF_NACL_AUTH_STR(name, flags) \ | ||
450 | __DEF_NACL_AUTH_STR(nacl_auth, name, flags) \ | ||
451 | static ssize_t iscsi_nacl_auth_show_##name( \ | ||
452 | struct se_node_acl *nacl, \ | ||
453 | char *page) \ | ||
454 | { \ | ||
455 | return __iscsi_nacl_auth_show_##name(container_of(nacl, \ | ||
456 | struct iscsi_node_acl, se_node_acl), page); \ | ||
457 | } \ | ||
458 | static ssize_t iscsi_nacl_auth_store_##name( \ | ||
459 | struct se_node_acl *nacl, \ | ||
460 | const char *page, \ | ||
461 | size_t count) \ | ||
462 | { \ | ||
463 | return __iscsi_nacl_auth_store_##name(container_of(nacl, \ | ||
464 | struct iscsi_node_acl, se_node_acl), page, count); \ | ||
465 | } | ||
466 | |||
467 | #define DEF_NACL_AUTH_INT(name) \ | ||
468 | __DEF_NACL_AUTH_INT(nacl_auth, name) \ | ||
469 | static ssize_t iscsi_nacl_auth_show_##name( \ | ||
470 | struct se_node_acl *nacl, \ | ||
471 | char *page) \ | ||
472 | { \ | ||
473 | return __iscsi_nacl_auth_show_##name(container_of(nacl, \ | ||
474 | struct iscsi_node_acl, se_node_acl), page); \ | ||
475 | } | ||
476 | |||
477 | #define AUTH_ATTR(_name, _mode) TF_NACL_AUTH_ATTR(iscsi, _name, _mode); | ||
478 | #define AUTH_ATTR_RO(_name) TF_NACL_AUTH_ATTR_RO(iscsi, _name); | ||
479 | |||
480 | /* | ||
481 | * One-way authentication userid | ||
482 | */ | ||
483 | DEF_NACL_AUTH_STR(userid, NAF_USERID_SET); | ||
484 | AUTH_ATTR(userid, S_IRUGO | S_IWUSR); | ||
485 | /* | ||
486 | * One-way authentication password | ||
487 | */ | ||
488 | DEF_NACL_AUTH_STR(password, NAF_PASSWORD_SET); | ||
489 | AUTH_ATTR(password, S_IRUGO | S_IWUSR); | ||
490 | /* | ||
491 | * Enforce mutual authentication | ||
492 | */ | ||
493 | DEF_NACL_AUTH_INT(authenticate_target); | ||
494 | AUTH_ATTR_RO(authenticate_target); | ||
495 | /* | ||
496 | * Mutual authentication userid | ||
497 | */ | ||
498 | DEF_NACL_AUTH_STR(userid_mutual, NAF_USERID_IN_SET); | ||
499 | AUTH_ATTR(userid_mutual, S_IRUGO | S_IWUSR); | ||
500 | /* | ||
501 | * Mutual authentication password | ||
502 | */ | ||
503 | DEF_NACL_AUTH_STR(password_mutual, NAF_PASSWORD_IN_SET); | ||
504 | AUTH_ATTR(password_mutual, S_IRUGO | S_IWUSR); | ||
505 | |||
506 | static struct configfs_attribute *lio_target_nacl_auth_attrs[] = { | ||
507 | &iscsi_nacl_auth_userid.attr, | ||
508 | &iscsi_nacl_auth_password.attr, | ||
509 | &iscsi_nacl_auth_authenticate_target.attr, | ||
510 | &iscsi_nacl_auth_userid_mutual.attr, | ||
511 | &iscsi_nacl_auth_password_mutual.attr, | ||
512 | NULL, | ||
513 | }; | ||
514 | |||
515 | /* End items for lio_target_nacl_auth_cit */ | ||
516 | |||
517 | /* Start items for lio_target_nacl_param_cit */ | ||
518 | |||
519 | #define DEF_NACL_PARAM(name) \ | ||
520 | static ssize_t iscsi_nacl_param_show_##name( \ | ||
521 | struct se_node_acl *se_nacl, \ | ||
522 | char *page) \ | ||
523 | { \ | ||
524 | struct iscsi_session *sess; \ | ||
525 | struct se_session *se_sess; \ | ||
526 | ssize_t rb; \ | ||
527 | \ | ||
528 | spin_lock_bh(&se_nacl->nacl_sess_lock); \ | ||
529 | se_sess = se_nacl->nacl_sess; \ | ||
530 | if (!se_sess) { \ | ||
531 | rb = snprintf(page, PAGE_SIZE, \ | ||
532 | "No Active iSCSI Session\n"); \ | ||
533 | } else { \ | ||
534 | sess = se_sess->fabric_sess_ptr; \ | ||
535 | rb = snprintf(page, PAGE_SIZE, "%u\n", \ | ||
536 | (u32)sess->sess_ops->name); \ | ||
537 | } \ | ||
538 | spin_unlock_bh(&se_nacl->nacl_sess_lock); \ | ||
539 | \ | ||
540 | return rb; \ | ||
541 | } | ||
542 | |||
543 | #define NACL_PARAM_ATTR(_name) TF_NACL_PARAM_ATTR_RO(iscsi, _name); | ||
544 | |||
545 | DEF_NACL_PARAM(MaxConnections); | ||
546 | NACL_PARAM_ATTR(MaxConnections); | ||
547 | |||
548 | DEF_NACL_PARAM(InitialR2T); | ||
549 | NACL_PARAM_ATTR(InitialR2T); | ||
550 | |||
551 | DEF_NACL_PARAM(ImmediateData); | ||
552 | NACL_PARAM_ATTR(ImmediateData); | ||
553 | |||
554 | DEF_NACL_PARAM(MaxBurstLength); | ||
555 | NACL_PARAM_ATTR(MaxBurstLength); | ||
556 | |||
557 | DEF_NACL_PARAM(FirstBurstLength); | ||
558 | NACL_PARAM_ATTR(FirstBurstLength); | ||
559 | |||
560 | DEF_NACL_PARAM(DefaultTime2Wait); | ||
561 | NACL_PARAM_ATTR(DefaultTime2Wait); | ||
562 | |||
563 | DEF_NACL_PARAM(DefaultTime2Retain); | ||
564 | NACL_PARAM_ATTR(DefaultTime2Retain); | ||
565 | |||
566 | DEF_NACL_PARAM(MaxOutstandingR2T); | ||
567 | NACL_PARAM_ATTR(MaxOutstandingR2T); | ||
568 | |||
569 | DEF_NACL_PARAM(DataPDUInOrder); | ||
570 | NACL_PARAM_ATTR(DataPDUInOrder); | ||
571 | |||
572 | DEF_NACL_PARAM(DataSequenceInOrder); | ||
573 | NACL_PARAM_ATTR(DataSequenceInOrder); | ||
574 | |||
575 | DEF_NACL_PARAM(ErrorRecoveryLevel); | ||
576 | NACL_PARAM_ATTR(ErrorRecoveryLevel); | ||
577 | |||
578 | static struct configfs_attribute *lio_target_nacl_param_attrs[] = { | ||
579 | &iscsi_nacl_param_MaxConnections.attr, | ||
580 | &iscsi_nacl_param_InitialR2T.attr, | ||
581 | &iscsi_nacl_param_ImmediateData.attr, | ||
582 | &iscsi_nacl_param_MaxBurstLength.attr, | ||
583 | &iscsi_nacl_param_FirstBurstLength.attr, | ||
584 | &iscsi_nacl_param_DefaultTime2Wait.attr, | ||
585 | &iscsi_nacl_param_DefaultTime2Retain.attr, | ||
586 | &iscsi_nacl_param_MaxOutstandingR2T.attr, | ||
587 | &iscsi_nacl_param_DataPDUInOrder.attr, | ||
588 | &iscsi_nacl_param_DataSequenceInOrder.attr, | ||
589 | &iscsi_nacl_param_ErrorRecoveryLevel.attr, | ||
590 | NULL, | ||
591 | }; | ||
592 | |||
593 | /* End items for lio_target_nacl_param_cit */ | ||
594 | |||
595 | /* Start items for lio_target_acl_cit */ | ||
596 | |||
597 | static ssize_t lio_target_nacl_show_info( | ||
598 | struct se_node_acl *se_nacl, | ||
599 | char *page) | ||
600 | { | ||
601 | struct iscsi_session *sess; | ||
602 | struct iscsi_conn *conn; | ||
603 | struct se_session *se_sess; | ||
604 | ssize_t rb = 0; | ||
605 | |||
606 | spin_lock_bh(&se_nacl->nacl_sess_lock); | ||
607 | se_sess = se_nacl->nacl_sess; | ||
608 | if (!se_sess) { | ||
609 | rb += sprintf(page+rb, "No active iSCSI Session for Initiator" | ||
610 | " Endpoint: %s\n", se_nacl->initiatorname); | ||
611 | } else { | ||
612 | sess = se_sess->fabric_sess_ptr; | ||
613 | |||
614 | if (sess->sess_ops->InitiatorName) | ||
615 | rb += sprintf(page+rb, "InitiatorName: %s\n", | ||
616 | sess->sess_ops->InitiatorName); | ||
617 | if (sess->sess_ops->InitiatorAlias) | ||
618 | rb += sprintf(page+rb, "InitiatorAlias: %s\n", | ||
619 | sess->sess_ops->InitiatorAlias); | ||
620 | |||
621 | rb += sprintf(page+rb, "LIO Session ID: %u " | ||
622 | "ISID: 0x%02x %02x %02x %02x %02x %02x " | ||
623 | "TSIH: %hu ", sess->sid, | ||
624 | sess->isid[0], sess->isid[1], sess->isid[2], | ||
625 | sess->isid[3], sess->isid[4], sess->isid[5], | ||
626 | sess->tsih); | ||
627 | rb += sprintf(page+rb, "SessionType: %s\n", | ||
628 | (sess->sess_ops->SessionType) ? | ||
629 | "Discovery" : "Normal"); | ||
630 | rb += sprintf(page+rb, "Session State: "); | ||
631 | switch (sess->session_state) { | ||
632 | case TARG_SESS_STATE_FREE: | ||
633 | rb += sprintf(page+rb, "TARG_SESS_FREE\n"); | ||
634 | break; | ||
635 | case TARG_SESS_STATE_ACTIVE: | ||
636 | rb += sprintf(page+rb, "TARG_SESS_STATE_ACTIVE\n"); | ||
637 | break; | ||
638 | case TARG_SESS_STATE_LOGGED_IN: | ||
639 | rb += sprintf(page+rb, "TARG_SESS_STATE_LOGGED_IN\n"); | ||
640 | break; | ||
641 | case TARG_SESS_STATE_FAILED: | ||
642 | rb += sprintf(page+rb, "TARG_SESS_STATE_FAILED\n"); | ||
643 | break; | ||
644 | case TARG_SESS_STATE_IN_CONTINUE: | ||
645 | rb += sprintf(page+rb, "TARG_SESS_STATE_IN_CONTINUE\n"); | ||
646 | break; | ||
647 | default: | ||
648 | rb += sprintf(page+rb, "ERROR: Unknown Session" | ||
649 | " State!\n"); | ||
650 | break; | ||
651 | } | ||
652 | |||
653 | rb += sprintf(page+rb, "---------------------[iSCSI Session" | ||
654 | " Values]-----------------------\n"); | ||
655 | rb += sprintf(page+rb, " CmdSN/WR : CmdSN/WC : ExpCmdSN" | ||
656 | " : MaxCmdSN : ITT : TTT\n"); | ||
657 | rb += sprintf(page+rb, " 0x%08x 0x%08x 0x%08x 0x%08x" | ||
658 | " 0x%08x 0x%08x\n", | ||
659 | sess->cmdsn_window, | ||
660 | (sess->max_cmd_sn - sess->exp_cmd_sn) + 1, | ||
661 | sess->exp_cmd_sn, sess->max_cmd_sn, | ||
662 | sess->init_task_tag, sess->targ_xfer_tag); | ||
663 | rb += sprintf(page+rb, "----------------------[iSCSI" | ||
664 | " Connections]-------------------------\n"); | ||
665 | |||
666 | spin_lock(&sess->conn_lock); | ||
667 | list_for_each_entry(conn, &sess->sess_conn_list, conn_list) { | ||
668 | rb += sprintf(page+rb, "CID: %hu Connection" | ||
669 | " State: ", conn->cid); | ||
670 | switch (conn->conn_state) { | ||
671 | case TARG_CONN_STATE_FREE: | ||
672 | rb += sprintf(page+rb, | ||
673 | "TARG_CONN_STATE_FREE\n"); | ||
674 | break; | ||
675 | case TARG_CONN_STATE_XPT_UP: | ||
676 | rb += sprintf(page+rb, | ||
677 | "TARG_CONN_STATE_XPT_UP\n"); | ||
678 | break; | ||
679 | case TARG_CONN_STATE_IN_LOGIN: | ||
680 | rb += sprintf(page+rb, | ||
681 | "TARG_CONN_STATE_IN_LOGIN\n"); | ||
682 | break; | ||
683 | case TARG_CONN_STATE_LOGGED_IN: | ||
684 | rb += sprintf(page+rb, | ||
685 | "TARG_CONN_STATE_LOGGED_IN\n"); | ||
686 | break; | ||
687 | case TARG_CONN_STATE_IN_LOGOUT: | ||
688 | rb += sprintf(page+rb, | ||
689 | "TARG_CONN_STATE_IN_LOGOUT\n"); | ||
690 | break; | ||
691 | case TARG_CONN_STATE_LOGOUT_REQUESTED: | ||
692 | rb += sprintf(page+rb, | ||
693 | "TARG_CONN_STATE_LOGOUT_REQUESTED\n"); | ||
694 | break; | ||
695 | case TARG_CONN_STATE_CLEANUP_WAIT: | ||
696 | rb += sprintf(page+rb, | ||
697 | "TARG_CONN_STATE_CLEANUP_WAIT\n"); | ||
698 | break; | ||
699 | default: | ||
700 | rb += sprintf(page+rb, | ||
701 | "ERROR: Unknown Connection State!\n"); | ||
702 | break; | ||
703 | } | ||
704 | |||
705 | rb += sprintf(page+rb, " Address %s %s", conn->login_ip, | ||
706 | (conn->network_transport == ISCSI_TCP) ? | ||
707 | "TCP" : "SCTP"); | ||
708 | rb += sprintf(page+rb, " StatSN: 0x%08x\n", | ||
709 | conn->stat_sn); | ||
710 | } | ||
711 | spin_unlock(&sess->conn_lock); | ||
712 | } | ||
713 | spin_unlock_bh(&se_nacl->nacl_sess_lock); | ||
714 | |||
715 | return rb; | ||
716 | } | ||
717 | |||
718 | TF_NACL_BASE_ATTR_RO(lio_target, info); | ||
719 | |||
720 | static ssize_t lio_target_nacl_show_cmdsn_depth( | ||
721 | struct se_node_acl *se_nacl, | ||
722 | char *page) | ||
723 | { | ||
724 | return sprintf(page, "%u\n", se_nacl->queue_depth); | ||
725 | } | ||
726 | |||
727 | static ssize_t lio_target_nacl_store_cmdsn_depth( | ||
728 | struct se_node_acl *se_nacl, | ||
729 | const char *page, | ||
730 | size_t count) | ||
731 | { | ||
732 | struct se_portal_group *se_tpg = se_nacl->se_tpg; | ||
733 | struct iscsi_portal_group *tpg = container_of(se_tpg, | ||
734 | struct iscsi_portal_group, tpg_se_tpg); | ||
735 | struct config_item *acl_ci, *tpg_ci, *wwn_ci; | ||
736 | char *endptr; | ||
737 | u32 cmdsn_depth = 0; | ||
738 | int ret; | ||
739 | |||
740 | cmdsn_depth = simple_strtoul(page, &endptr, 0); | ||
741 | if (cmdsn_depth > TA_DEFAULT_CMDSN_DEPTH_MAX) { | ||
742 | pr_err("Passed cmdsn_depth: %u exceeds" | ||
743 | " TA_DEFAULT_CMDSN_DEPTH_MAX: %u\n", cmdsn_depth, | ||
744 | TA_DEFAULT_CMDSN_DEPTH_MAX); | ||
745 | return -EINVAL; | ||
746 | } | ||
747 | acl_ci = &se_nacl->acl_group.cg_item; | ||
748 | if (!acl_ci) { | ||
749 | pr_err("Unable to locatel acl_ci\n"); | ||
750 | return -EINVAL; | ||
751 | } | ||
752 | tpg_ci = &acl_ci->ci_parent->ci_group->cg_item; | ||
753 | if (!tpg_ci) { | ||
754 | pr_err("Unable to locate tpg_ci\n"); | ||
755 | return -EINVAL; | ||
756 | } | ||
757 | wwn_ci = &tpg_ci->ci_group->cg_item; | ||
758 | if (!wwn_ci) { | ||
759 | pr_err("Unable to locate config_item wwn_ci\n"); | ||
760 | return -EINVAL; | ||
761 | } | ||
762 | |||
763 | if (iscsit_get_tpg(tpg) < 0) | ||
764 | return -EINVAL; | ||
765 | /* | ||
766 | * iscsit_tpg_set_initiator_node_queue_depth() assumes force=1 | ||
767 | */ | ||
768 | ret = iscsit_tpg_set_initiator_node_queue_depth(tpg, | ||
769 | config_item_name(acl_ci), cmdsn_depth, 1); | ||
770 | |||
771 | pr_debug("LIO_Target_ConfigFS: %s/%s Set CmdSN Window: %u for" | ||
772 | "InitiatorName: %s\n", config_item_name(wwn_ci), | ||
773 | config_item_name(tpg_ci), cmdsn_depth, | ||
774 | config_item_name(acl_ci)); | ||
775 | |||
776 | iscsit_put_tpg(tpg); | ||
777 | return (!ret) ? count : (ssize_t)ret; | ||
778 | } | ||
779 | |||
780 | TF_NACL_BASE_ATTR(lio_target, cmdsn_depth, S_IRUGO | S_IWUSR); | ||
781 | |||
782 | static struct configfs_attribute *lio_target_initiator_attrs[] = { | ||
783 | &lio_target_nacl_info.attr, | ||
784 | &lio_target_nacl_cmdsn_depth.attr, | ||
785 | NULL, | ||
786 | }; | ||
787 | |||
788 | static struct se_node_acl *lio_tpg_alloc_fabric_acl( | ||
789 | struct se_portal_group *se_tpg) | ||
790 | { | ||
791 | struct iscsi_node_acl *acl; | ||
792 | |||
793 | acl = kzalloc(sizeof(struct iscsi_node_acl), GFP_KERNEL); | ||
794 | if (!acl) { | ||
795 | pr_err("Unable to allocate memory for struct iscsi_node_acl\n"); | ||
796 | return NULL; | ||
797 | } | ||
798 | |||
799 | return &acl->se_node_acl; | ||
800 | } | ||
801 | |||
802 | static struct se_node_acl *lio_target_make_nodeacl( | ||
803 | struct se_portal_group *se_tpg, | ||
804 | struct config_group *group, | ||
805 | const char *name) | ||
806 | { | ||
807 | struct config_group *stats_cg; | ||
808 | struct iscsi_node_acl *acl; | ||
809 | struct se_node_acl *se_nacl_new, *se_nacl; | ||
810 | struct iscsi_portal_group *tpg = container_of(se_tpg, | ||
811 | struct iscsi_portal_group, tpg_se_tpg); | ||
812 | u32 cmdsn_depth; | ||
813 | |||
814 | se_nacl_new = lio_tpg_alloc_fabric_acl(se_tpg); | ||
815 | if (!se_nacl_new) | ||
816 | return ERR_PTR(-ENOMEM); | ||
817 | |||
818 | acl = container_of(se_nacl_new, struct iscsi_node_acl, | ||
819 | se_node_acl); | ||
820 | |||
821 | cmdsn_depth = ISCSI_TPG_ATTRIB(tpg)->default_cmdsn_depth; | ||
822 | /* | ||
823 | * se_nacl_new may be released by core_tpg_add_initiator_node_acl() | ||
824 | * when converting a NdoeACL from demo mode -> explict | ||
825 | */ | ||
826 | se_nacl = core_tpg_add_initiator_node_acl(se_tpg, se_nacl_new, | ||
827 | name, cmdsn_depth); | ||
828 | if (IS_ERR(se_nacl)) | ||
829 | return se_nacl; | ||
830 | |||
831 | stats_cg = &acl->se_node_acl.acl_fabric_stat_group; | ||
832 | |||
833 | stats_cg->default_groups = kzalloc(sizeof(struct config_group) * 2, | ||
834 | GFP_KERNEL); | ||
835 | if (!stats_cg->default_groups) { | ||
836 | pr_err("Unable to allocate memory for" | ||
837 | " stats_cg->default_groups\n"); | ||
838 | core_tpg_del_initiator_node_acl(se_tpg, se_nacl, 1); | ||
839 | kfree(acl); | ||
840 | return ERR_PTR(-ENOMEM); | ||
841 | } | ||
842 | |||
843 | stats_cg->default_groups[0] = &NODE_STAT_GRPS(acl)->iscsi_sess_stats_group; | ||
844 | stats_cg->default_groups[1] = NULL; | ||
845 | config_group_init_type_name(&NODE_STAT_GRPS(acl)->iscsi_sess_stats_group, | ||
846 | "iscsi_sess_stats", &iscsi_stat_sess_cit); | ||
847 | |||
848 | return se_nacl; | ||
849 | } | ||
850 | |||
851 | static void lio_target_drop_nodeacl( | ||
852 | struct se_node_acl *se_nacl) | ||
853 | { | ||
854 | struct se_portal_group *se_tpg = se_nacl->se_tpg; | ||
855 | struct iscsi_node_acl *acl = container_of(se_nacl, | ||
856 | struct iscsi_node_acl, se_node_acl); | ||
857 | struct config_item *df_item; | ||
858 | struct config_group *stats_cg; | ||
859 | int i; | ||
860 | |||
861 | stats_cg = &acl->se_node_acl.acl_fabric_stat_group; | ||
862 | for (i = 0; stats_cg->default_groups[i]; i++) { | ||
863 | df_item = &stats_cg->default_groups[i]->cg_item; | ||
864 | stats_cg->default_groups[i] = NULL; | ||
865 | config_item_put(df_item); | ||
866 | } | ||
867 | kfree(stats_cg->default_groups); | ||
868 | |||
869 | core_tpg_del_initiator_node_acl(se_tpg, se_nacl, 1); | ||
870 | kfree(acl); | ||
871 | } | ||
872 | |||
873 | /* End items for lio_target_acl_cit */ | ||
874 | |||
875 | /* Start items for lio_target_tpg_attrib_cit */ | ||
876 | |||
877 | #define DEF_TPG_ATTRIB(name) \ | ||
878 | \ | ||
879 | static ssize_t iscsi_tpg_attrib_show_##name( \ | ||
880 | struct se_portal_group *se_tpg, \ | ||
881 | char *page) \ | ||
882 | { \ | ||
883 | struct iscsi_portal_group *tpg = container_of(se_tpg, \ | ||
884 | struct iscsi_portal_group, tpg_se_tpg); \ | ||
885 | ssize_t rb; \ | ||
886 | \ | ||
887 | if (iscsit_get_tpg(tpg) < 0) \ | ||
888 | return -EINVAL; \ | ||
889 | \ | ||
890 | rb = sprintf(page, "%u\n", ISCSI_TPG_ATTRIB(tpg)->name); \ | ||
891 | iscsit_put_tpg(tpg); \ | ||
892 | return rb; \ | ||
893 | } \ | ||
894 | \ | ||
895 | static ssize_t iscsi_tpg_attrib_store_##name( \ | ||
896 | struct se_portal_group *se_tpg, \ | ||
897 | const char *page, \ | ||
898 | size_t count) \ | ||
899 | { \ | ||
900 | struct iscsi_portal_group *tpg = container_of(se_tpg, \ | ||
901 | struct iscsi_portal_group, tpg_se_tpg); \ | ||
902 | char *endptr; \ | ||
903 | u32 val; \ | ||
904 | int ret; \ | ||
905 | \ | ||
906 | if (iscsit_get_tpg(tpg) < 0) \ | ||
907 | return -EINVAL; \ | ||
908 | \ | ||
909 | val = simple_strtoul(page, &endptr, 0); \ | ||
910 | ret = iscsit_ta_##name(tpg, val); \ | ||
911 | if (ret < 0) \ | ||
912 | goto out; \ | ||
913 | \ | ||
914 | iscsit_put_tpg(tpg); \ | ||
915 | return count; \ | ||
916 | out: \ | ||
917 | iscsit_put_tpg(tpg); \ | ||
918 | return ret; \ | ||
919 | } | ||
920 | |||
921 | #define TPG_ATTR(_name, _mode) TF_TPG_ATTRIB_ATTR(iscsi, _name, _mode); | ||
922 | |||
923 | /* | ||
924 | * Define iscsi_tpg_attrib_s_authentication | ||
925 | */ | ||
926 | DEF_TPG_ATTRIB(authentication); | ||
927 | TPG_ATTR(authentication, S_IRUGO | S_IWUSR); | ||
928 | /* | ||
929 | * Define iscsi_tpg_attrib_s_login_timeout | ||
930 | */ | ||
931 | DEF_TPG_ATTRIB(login_timeout); | ||
932 | TPG_ATTR(login_timeout, S_IRUGO | S_IWUSR); | ||
933 | /* | ||
934 | * Define iscsi_tpg_attrib_s_netif_timeout | ||
935 | */ | ||
936 | DEF_TPG_ATTRIB(netif_timeout); | ||
937 | TPG_ATTR(netif_timeout, S_IRUGO | S_IWUSR); | ||
938 | /* | ||
939 | * Define iscsi_tpg_attrib_s_generate_node_acls | ||
940 | */ | ||
941 | DEF_TPG_ATTRIB(generate_node_acls); | ||
942 | TPG_ATTR(generate_node_acls, S_IRUGO | S_IWUSR); | ||
943 | /* | ||
944 | * Define iscsi_tpg_attrib_s_default_cmdsn_depth | ||
945 | */ | ||
946 | DEF_TPG_ATTRIB(default_cmdsn_depth); | ||
947 | TPG_ATTR(default_cmdsn_depth, S_IRUGO | S_IWUSR); | ||
948 | /* | ||
949 | Define iscsi_tpg_attrib_s_cache_dynamic_acls | ||
950 | */ | ||
951 | DEF_TPG_ATTRIB(cache_dynamic_acls); | ||
952 | TPG_ATTR(cache_dynamic_acls, S_IRUGO | S_IWUSR); | ||
953 | /* | ||
954 | * Define iscsi_tpg_attrib_s_demo_mode_write_protect | ||
955 | */ | ||
956 | DEF_TPG_ATTRIB(demo_mode_write_protect); | ||
957 | TPG_ATTR(demo_mode_write_protect, S_IRUGO | S_IWUSR); | ||
958 | /* | ||
959 | * Define iscsi_tpg_attrib_s_prod_mode_write_protect | ||
960 | */ | ||
961 | DEF_TPG_ATTRIB(prod_mode_write_protect); | ||
962 | TPG_ATTR(prod_mode_write_protect, S_IRUGO | S_IWUSR); | ||
963 | |||
964 | static struct configfs_attribute *lio_target_tpg_attrib_attrs[] = { | ||
965 | &iscsi_tpg_attrib_authentication.attr, | ||
966 | &iscsi_tpg_attrib_login_timeout.attr, | ||
967 | &iscsi_tpg_attrib_netif_timeout.attr, | ||
968 | &iscsi_tpg_attrib_generate_node_acls.attr, | ||
969 | &iscsi_tpg_attrib_default_cmdsn_depth.attr, | ||
970 | &iscsi_tpg_attrib_cache_dynamic_acls.attr, | ||
971 | &iscsi_tpg_attrib_demo_mode_write_protect.attr, | ||
972 | &iscsi_tpg_attrib_prod_mode_write_protect.attr, | ||
973 | NULL, | ||
974 | }; | ||
975 | |||
976 | /* End items for lio_target_tpg_attrib_cit */ | ||
977 | |||
978 | /* Start items for lio_target_tpg_param_cit */ | ||
979 | |||
980 | #define DEF_TPG_PARAM(name) \ | ||
981 | static ssize_t iscsi_tpg_param_show_##name( \ | ||
982 | struct se_portal_group *se_tpg, \ | ||
983 | char *page) \ | ||
984 | { \ | ||
985 | struct iscsi_portal_group *tpg = container_of(se_tpg, \ | ||
986 | struct iscsi_portal_group, tpg_se_tpg); \ | ||
987 | struct iscsi_param *param; \ | ||
988 | ssize_t rb; \ | ||
989 | \ | ||
990 | if (iscsit_get_tpg(tpg) < 0) \ | ||
991 | return -EINVAL; \ | ||
992 | \ | ||
993 | param = iscsi_find_param_from_key(__stringify(name), \ | ||
994 | tpg->param_list); \ | ||
995 | if (!param) { \ | ||
996 | iscsit_put_tpg(tpg); \ | ||
997 | return -EINVAL; \ | ||
998 | } \ | ||
999 | rb = snprintf(page, PAGE_SIZE, "%s\n", param->value); \ | ||
1000 | \ | ||
1001 | iscsit_put_tpg(tpg); \ | ||
1002 | return rb; \ | ||
1003 | } \ | ||
1004 | static ssize_t iscsi_tpg_param_store_##name( \ | ||
1005 | struct se_portal_group *se_tpg, \ | ||
1006 | const char *page, \ | ||
1007 | size_t count) \ | ||
1008 | { \ | ||
1009 | struct iscsi_portal_group *tpg = container_of(se_tpg, \ | ||
1010 | struct iscsi_portal_group, tpg_se_tpg); \ | ||
1011 | char *buf; \ | ||
1012 | int ret; \ | ||
1013 | \ | ||
1014 | buf = kzalloc(PAGE_SIZE, GFP_KERNEL); \ | ||
1015 | if (!buf) \ | ||
1016 | return -ENOMEM; \ | ||
1017 | snprintf(buf, PAGE_SIZE, "%s=%s", __stringify(name), page); \ | ||
1018 | buf[strlen(buf)-1] = '\0'; /* Kill newline */ \ | ||
1019 | \ | ||
1020 | if (iscsit_get_tpg(tpg) < 0) { \ | ||
1021 | kfree(buf); \ | ||
1022 | return -EINVAL; \ | ||
1023 | } \ | ||
1024 | \ | ||
1025 | ret = iscsi_change_param_value(buf, tpg->param_list, 1); \ | ||
1026 | if (ret < 0) \ | ||
1027 | goto out; \ | ||
1028 | \ | ||
1029 | kfree(buf); \ | ||
1030 | iscsit_put_tpg(tpg); \ | ||
1031 | return count; \ | ||
1032 | out: \ | ||
1033 | kfree(buf); \ | ||
1034 | iscsit_put_tpg(tpg); \ | ||
1035 | return -EINVAL; \ | ||
1036 | } | ||
1037 | |||
1038 | #define TPG_PARAM_ATTR(_name, _mode) TF_TPG_PARAM_ATTR(iscsi, _name, _mode); | ||
1039 | |||
1040 | DEF_TPG_PARAM(AuthMethod); | ||
1041 | TPG_PARAM_ATTR(AuthMethod, S_IRUGO | S_IWUSR); | ||
1042 | |||
1043 | DEF_TPG_PARAM(HeaderDigest); | ||
1044 | TPG_PARAM_ATTR(HeaderDigest, S_IRUGO | S_IWUSR); | ||
1045 | |||
1046 | DEF_TPG_PARAM(DataDigest); | ||
1047 | TPG_PARAM_ATTR(DataDigest, S_IRUGO | S_IWUSR); | ||
1048 | |||
1049 | DEF_TPG_PARAM(MaxConnections); | ||
1050 | TPG_PARAM_ATTR(MaxConnections, S_IRUGO | S_IWUSR); | ||
1051 | |||
1052 | DEF_TPG_PARAM(TargetAlias); | ||
1053 | TPG_PARAM_ATTR(TargetAlias, S_IRUGO | S_IWUSR); | ||
1054 | |||
1055 | DEF_TPG_PARAM(InitialR2T); | ||
1056 | TPG_PARAM_ATTR(InitialR2T, S_IRUGO | S_IWUSR); | ||
1057 | |||
1058 | DEF_TPG_PARAM(ImmediateData); | ||
1059 | TPG_PARAM_ATTR(ImmediateData, S_IRUGO | S_IWUSR); | ||
1060 | |||
1061 | DEF_TPG_PARAM(MaxRecvDataSegmentLength); | ||
1062 | TPG_PARAM_ATTR(MaxRecvDataSegmentLength, S_IRUGO | S_IWUSR); | ||
1063 | |||
1064 | DEF_TPG_PARAM(MaxBurstLength); | ||
1065 | TPG_PARAM_ATTR(MaxBurstLength, S_IRUGO | S_IWUSR); | ||
1066 | |||
1067 | DEF_TPG_PARAM(FirstBurstLength); | ||
1068 | TPG_PARAM_ATTR(FirstBurstLength, S_IRUGO | S_IWUSR); | ||
1069 | |||
1070 | DEF_TPG_PARAM(DefaultTime2Wait); | ||
1071 | TPG_PARAM_ATTR(DefaultTime2Wait, S_IRUGO | S_IWUSR); | ||
1072 | |||
1073 | DEF_TPG_PARAM(DefaultTime2Retain); | ||
1074 | TPG_PARAM_ATTR(DefaultTime2Retain, S_IRUGO | S_IWUSR); | ||
1075 | |||
1076 | DEF_TPG_PARAM(MaxOutstandingR2T); | ||
1077 | TPG_PARAM_ATTR(MaxOutstandingR2T, S_IRUGO | S_IWUSR); | ||
1078 | |||
1079 | DEF_TPG_PARAM(DataPDUInOrder); | ||
1080 | TPG_PARAM_ATTR(DataPDUInOrder, S_IRUGO | S_IWUSR); | ||
1081 | |||
1082 | DEF_TPG_PARAM(DataSequenceInOrder); | ||
1083 | TPG_PARAM_ATTR(DataSequenceInOrder, S_IRUGO | S_IWUSR); | ||
1084 | |||
1085 | DEF_TPG_PARAM(ErrorRecoveryLevel); | ||
1086 | TPG_PARAM_ATTR(ErrorRecoveryLevel, S_IRUGO | S_IWUSR); | ||
1087 | |||
1088 | DEF_TPG_PARAM(IFMarker); | ||
1089 | TPG_PARAM_ATTR(IFMarker, S_IRUGO | S_IWUSR); | ||
1090 | |||
1091 | DEF_TPG_PARAM(OFMarker); | ||
1092 | TPG_PARAM_ATTR(OFMarker, S_IRUGO | S_IWUSR); | ||
1093 | |||
1094 | DEF_TPG_PARAM(IFMarkInt); | ||
1095 | TPG_PARAM_ATTR(IFMarkInt, S_IRUGO | S_IWUSR); | ||
1096 | |||
1097 | DEF_TPG_PARAM(OFMarkInt); | ||
1098 | TPG_PARAM_ATTR(OFMarkInt, S_IRUGO | S_IWUSR); | ||
1099 | |||
1100 | static struct configfs_attribute *lio_target_tpg_param_attrs[] = { | ||
1101 | &iscsi_tpg_param_AuthMethod.attr, | ||
1102 | &iscsi_tpg_param_HeaderDigest.attr, | ||
1103 | &iscsi_tpg_param_DataDigest.attr, | ||
1104 | &iscsi_tpg_param_MaxConnections.attr, | ||
1105 | &iscsi_tpg_param_TargetAlias.attr, | ||
1106 | &iscsi_tpg_param_InitialR2T.attr, | ||
1107 | &iscsi_tpg_param_ImmediateData.attr, | ||
1108 | &iscsi_tpg_param_MaxRecvDataSegmentLength.attr, | ||
1109 | &iscsi_tpg_param_MaxBurstLength.attr, | ||
1110 | &iscsi_tpg_param_FirstBurstLength.attr, | ||
1111 | &iscsi_tpg_param_DefaultTime2Wait.attr, | ||
1112 | &iscsi_tpg_param_DefaultTime2Retain.attr, | ||
1113 | &iscsi_tpg_param_MaxOutstandingR2T.attr, | ||
1114 | &iscsi_tpg_param_DataPDUInOrder.attr, | ||
1115 | &iscsi_tpg_param_DataSequenceInOrder.attr, | ||
1116 | &iscsi_tpg_param_ErrorRecoveryLevel.attr, | ||
1117 | &iscsi_tpg_param_IFMarker.attr, | ||
1118 | &iscsi_tpg_param_OFMarker.attr, | ||
1119 | &iscsi_tpg_param_IFMarkInt.attr, | ||
1120 | &iscsi_tpg_param_OFMarkInt.attr, | ||
1121 | NULL, | ||
1122 | }; | ||
1123 | |||
1124 | /* End items for lio_target_tpg_param_cit */ | ||
1125 | |||
1126 | /* Start items for lio_target_tpg_cit */ | ||
1127 | |||
1128 | static ssize_t lio_target_tpg_show_enable( | ||
1129 | struct se_portal_group *se_tpg, | ||
1130 | char *page) | ||
1131 | { | ||
1132 | struct iscsi_portal_group *tpg = container_of(se_tpg, | ||
1133 | struct iscsi_portal_group, tpg_se_tpg); | ||
1134 | ssize_t len; | ||
1135 | |||
1136 | spin_lock(&tpg->tpg_state_lock); | ||
1137 | len = sprintf(page, "%d\n", | ||
1138 | (tpg->tpg_state == TPG_STATE_ACTIVE) ? 1 : 0); | ||
1139 | spin_unlock(&tpg->tpg_state_lock); | ||
1140 | |||
1141 | return len; | ||
1142 | } | ||
1143 | |||
1144 | static ssize_t lio_target_tpg_store_enable( | ||
1145 | struct se_portal_group *se_tpg, | ||
1146 | const char *page, | ||
1147 | size_t count) | ||
1148 | { | ||
1149 | struct iscsi_portal_group *tpg = container_of(se_tpg, | ||
1150 | struct iscsi_portal_group, tpg_se_tpg); | ||
1151 | char *endptr; | ||
1152 | u32 op; | ||
1153 | int ret = 0; | ||
1154 | |||
1155 | op = simple_strtoul(page, &endptr, 0); | ||
1156 | if ((op != 1) && (op != 0)) { | ||
1157 | pr_err("Illegal value for tpg_enable: %u\n", op); | ||
1158 | return -EINVAL; | ||
1159 | } | ||
1160 | |||
1161 | ret = iscsit_get_tpg(tpg); | ||
1162 | if (ret < 0) | ||
1163 | return -EINVAL; | ||
1164 | |||
1165 | if (op) { | ||
1166 | ret = iscsit_tpg_enable_portal_group(tpg); | ||
1167 | if (ret < 0) | ||
1168 | goto out; | ||
1169 | } else { | ||
1170 | /* | ||
1171 | * iscsit_tpg_disable_portal_group() assumes force=1 | ||
1172 | */ | ||
1173 | ret = iscsit_tpg_disable_portal_group(tpg, 1); | ||
1174 | if (ret < 0) | ||
1175 | goto out; | ||
1176 | } | ||
1177 | |||
1178 | iscsit_put_tpg(tpg); | ||
1179 | return count; | ||
1180 | out: | ||
1181 | iscsit_put_tpg(tpg); | ||
1182 | return -EINVAL; | ||
1183 | } | ||
1184 | |||
1185 | TF_TPG_BASE_ATTR(lio_target, enable, S_IRUGO | S_IWUSR); | ||
1186 | |||
1187 | static struct configfs_attribute *lio_target_tpg_attrs[] = { | ||
1188 | &lio_target_tpg_enable.attr, | ||
1189 | NULL, | ||
1190 | }; | ||
1191 | |||
1192 | /* End items for lio_target_tpg_cit */ | ||
1193 | |||
1194 | /* Start items for lio_target_tiqn_cit */ | ||
1195 | |||
1196 | struct se_portal_group *lio_target_tiqn_addtpg( | ||
1197 | struct se_wwn *wwn, | ||
1198 | struct config_group *group, | ||
1199 | const char *name) | ||
1200 | { | ||
1201 | struct iscsi_portal_group *tpg; | ||
1202 | struct iscsi_tiqn *tiqn; | ||
1203 | char *tpgt_str, *end_ptr; | ||
1204 | int ret = 0; | ||
1205 | unsigned short int tpgt; | ||
1206 | |||
1207 | tiqn = container_of(wwn, struct iscsi_tiqn, tiqn_wwn); | ||
1208 | /* | ||
1209 | * Only tpgt_# directory groups can be created below | ||
1210 | * target/iscsi/iqn.superturodiskarry/ | ||
1211 | */ | ||
1212 | tpgt_str = strstr(name, "tpgt_"); | ||
1213 | if (!tpgt_str) { | ||
1214 | pr_err("Unable to locate \"tpgt_#\" directory" | ||
1215 | " group\n"); | ||
1216 | return NULL; | ||
1217 | } | ||
1218 | tpgt_str += 5; /* Skip ahead of "tpgt_" */ | ||
1219 | tpgt = (unsigned short int) simple_strtoul(tpgt_str, &end_ptr, 0); | ||
1220 | |||
1221 | tpg = iscsit_alloc_portal_group(tiqn, tpgt); | ||
1222 | if (!tpg) | ||
1223 | return NULL; | ||
1224 | |||
1225 | ret = core_tpg_register( | ||
1226 | &lio_target_fabric_configfs->tf_ops, | ||
1227 | wwn, &tpg->tpg_se_tpg, (void *)tpg, | ||
1228 | TRANSPORT_TPG_TYPE_NORMAL); | ||
1229 | if (ret < 0) | ||
1230 | return NULL; | ||
1231 | |||
1232 | ret = iscsit_tpg_add_portal_group(tiqn, tpg); | ||
1233 | if (ret != 0) | ||
1234 | goto out; | ||
1235 | |||
1236 | pr_debug("LIO_Target_ConfigFS: REGISTER -> %s\n", tiqn->tiqn); | ||
1237 | pr_debug("LIO_Target_ConfigFS: REGISTER -> Allocated TPG: %s\n", | ||
1238 | name); | ||
1239 | return &tpg->tpg_se_tpg; | ||
1240 | out: | ||
1241 | core_tpg_deregister(&tpg->tpg_se_tpg); | ||
1242 | kfree(tpg); | ||
1243 | return NULL; | ||
1244 | } | ||
1245 | |||
1246 | void lio_target_tiqn_deltpg(struct se_portal_group *se_tpg) | ||
1247 | { | ||
1248 | struct iscsi_portal_group *tpg; | ||
1249 | struct iscsi_tiqn *tiqn; | ||
1250 | |||
1251 | tpg = container_of(se_tpg, struct iscsi_portal_group, tpg_se_tpg); | ||
1252 | tiqn = tpg->tpg_tiqn; | ||
1253 | /* | ||
1254 | * iscsit_tpg_del_portal_group() assumes force=1 | ||
1255 | */ | ||
1256 | pr_debug("LIO_Target_ConfigFS: DEREGISTER -> Releasing TPG\n"); | ||
1257 | iscsit_tpg_del_portal_group(tiqn, tpg, 1); | ||
1258 | } | ||
1259 | |||
1260 | /* End items for lio_target_tiqn_cit */ | ||
1261 | |||
1262 | /* Start LIO-Target TIQN struct contig_item lio_target_cit */ | ||
1263 | |||
1264 | static ssize_t lio_target_wwn_show_attr_lio_version( | ||
1265 | struct target_fabric_configfs *tf, | ||
1266 | char *page) | ||
1267 | { | ||
1268 | return sprintf(page, "RisingTide Systems Linux-iSCSI Target "ISCSIT_VERSION"\n"); | ||
1269 | } | ||
1270 | |||
1271 | TF_WWN_ATTR_RO(lio_target, lio_version); | ||
1272 | |||
1273 | static struct configfs_attribute *lio_target_wwn_attrs[] = { | ||
1274 | &lio_target_wwn_lio_version.attr, | ||
1275 | NULL, | ||
1276 | }; | ||
1277 | |||
1278 | struct se_wwn *lio_target_call_coreaddtiqn( | ||
1279 | struct target_fabric_configfs *tf, | ||
1280 | struct config_group *group, | ||
1281 | const char *name) | ||
1282 | { | ||
1283 | struct config_group *stats_cg; | ||
1284 | struct iscsi_tiqn *tiqn; | ||
1285 | |||
1286 | tiqn = iscsit_add_tiqn((unsigned char *)name); | ||
1287 | if (IS_ERR(tiqn)) | ||
1288 | return ERR_PTR(PTR_ERR(tiqn)); | ||
1289 | /* | ||
1290 | * Setup struct iscsi_wwn_stat_grps for se_wwn->fabric_stat_group. | ||
1291 | */ | ||
1292 | stats_cg = &tiqn->tiqn_wwn.fabric_stat_group; | ||
1293 | |||
1294 | stats_cg->default_groups = kzalloc(sizeof(struct config_group) * 6, | ||
1295 | GFP_KERNEL); | ||
1296 | if (!stats_cg->default_groups) { | ||
1297 | pr_err("Unable to allocate memory for" | ||
1298 | " stats_cg->default_groups\n"); | ||
1299 | iscsit_del_tiqn(tiqn); | ||
1300 | return ERR_PTR(-ENOMEM); | ||
1301 | } | ||
1302 | |||
1303 | stats_cg->default_groups[0] = &WWN_STAT_GRPS(tiqn)->iscsi_instance_group; | ||
1304 | stats_cg->default_groups[1] = &WWN_STAT_GRPS(tiqn)->iscsi_sess_err_group; | ||
1305 | stats_cg->default_groups[2] = &WWN_STAT_GRPS(tiqn)->iscsi_tgt_attr_group; | ||
1306 | stats_cg->default_groups[3] = &WWN_STAT_GRPS(tiqn)->iscsi_login_stats_group; | ||
1307 | stats_cg->default_groups[4] = &WWN_STAT_GRPS(tiqn)->iscsi_logout_stats_group; | ||
1308 | stats_cg->default_groups[5] = NULL; | ||
1309 | config_group_init_type_name(&WWN_STAT_GRPS(tiqn)->iscsi_instance_group, | ||
1310 | "iscsi_instance", &iscsi_stat_instance_cit); | ||
1311 | config_group_init_type_name(&WWN_STAT_GRPS(tiqn)->iscsi_sess_err_group, | ||
1312 | "iscsi_sess_err", &iscsi_stat_sess_err_cit); | ||
1313 | config_group_init_type_name(&WWN_STAT_GRPS(tiqn)->iscsi_tgt_attr_group, | ||
1314 | "iscsi_tgt_attr", &iscsi_stat_tgt_attr_cit); | ||
1315 | config_group_init_type_name(&WWN_STAT_GRPS(tiqn)->iscsi_login_stats_group, | ||
1316 | "iscsi_login_stats", &iscsi_stat_login_cit); | ||
1317 | config_group_init_type_name(&WWN_STAT_GRPS(tiqn)->iscsi_logout_stats_group, | ||
1318 | "iscsi_logout_stats", &iscsi_stat_logout_cit); | ||
1319 | |||
1320 | pr_debug("LIO_Target_ConfigFS: REGISTER -> %s\n", tiqn->tiqn); | ||
1321 | pr_debug("LIO_Target_ConfigFS: REGISTER -> Allocated Node:" | ||
1322 | " %s\n", name); | ||
1323 | return &tiqn->tiqn_wwn; | ||
1324 | } | ||
1325 | |||
1326 | void lio_target_call_coredeltiqn( | ||
1327 | struct se_wwn *wwn) | ||
1328 | { | ||
1329 | struct iscsi_tiqn *tiqn = container_of(wwn, struct iscsi_tiqn, tiqn_wwn); | ||
1330 | struct config_item *df_item; | ||
1331 | struct config_group *stats_cg; | ||
1332 | int i; | ||
1333 | |||
1334 | stats_cg = &tiqn->tiqn_wwn.fabric_stat_group; | ||
1335 | for (i = 0; stats_cg->default_groups[i]; i++) { | ||
1336 | df_item = &stats_cg->default_groups[i]->cg_item; | ||
1337 | stats_cg->default_groups[i] = NULL; | ||
1338 | config_item_put(df_item); | ||
1339 | } | ||
1340 | kfree(stats_cg->default_groups); | ||
1341 | |||
1342 | pr_debug("LIO_Target_ConfigFS: DEREGISTER -> %s\n", | ||
1343 | tiqn->tiqn); | ||
1344 | iscsit_del_tiqn(tiqn); | ||
1345 | } | ||
1346 | |||
1347 | /* End LIO-Target TIQN struct contig_lio_target_cit */ | ||
1348 | |||
1349 | /* Start lio_target_discovery_auth_cit */ | ||
1350 | |||
1351 | #define DEF_DISC_AUTH_STR(name, flags) \ | ||
1352 | __DEF_NACL_AUTH_STR(disc, name, flags) \ | ||
1353 | static ssize_t iscsi_disc_show_##name( \ | ||
1354 | struct target_fabric_configfs *tf, \ | ||
1355 | char *page) \ | ||
1356 | { \ | ||
1357 | return __iscsi_disc_show_##name(&iscsit_global->discovery_acl, \ | ||
1358 | page); \ | ||
1359 | } \ | ||
1360 | static ssize_t iscsi_disc_store_##name( \ | ||
1361 | struct target_fabric_configfs *tf, \ | ||
1362 | const char *page, \ | ||
1363 | size_t count) \ | ||
1364 | { \ | ||
1365 | return __iscsi_disc_store_##name(&iscsit_global->discovery_acl, \ | ||
1366 | page, count); \ | ||
1367 | } | ||
1368 | |||
1369 | #define DEF_DISC_AUTH_INT(name) \ | ||
1370 | __DEF_NACL_AUTH_INT(disc, name) \ | ||
1371 | static ssize_t iscsi_disc_show_##name( \ | ||
1372 | struct target_fabric_configfs *tf, \ | ||
1373 | char *page) \ | ||
1374 | { \ | ||
1375 | return __iscsi_disc_show_##name(&iscsit_global->discovery_acl, \ | ||
1376 | page); \ | ||
1377 | } | ||
1378 | |||
1379 | #define DISC_AUTH_ATTR(_name, _mode) TF_DISC_ATTR(iscsi, _name, _mode) | ||
1380 | #define DISC_AUTH_ATTR_RO(_name) TF_DISC_ATTR_RO(iscsi, _name) | ||
1381 | |||
1382 | /* | ||
1383 | * One-way authentication userid | ||
1384 | */ | ||
1385 | DEF_DISC_AUTH_STR(userid, NAF_USERID_SET); | ||
1386 | DISC_AUTH_ATTR(userid, S_IRUGO | S_IWUSR); | ||
1387 | /* | ||
1388 | * One-way authentication password | ||
1389 | */ | ||
1390 | DEF_DISC_AUTH_STR(password, NAF_PASSWORD_SET); | ||
1391 | DISC_AUTH_ATTR(password, S_IRUGO | S_IWUSR); | ||
1392 | /* | ||
1393 | * Enforce mutual authentication | ||
1394 | */ | ||
1395 | DEF_DISC_AUTH_INT(authenticate_target); | ||
1396 | DISC_AUTH_ATTR_RO(authenticate_target); | ||
1397 | /* | ||
1398 | * Mutual authentication userid | ||
1399 | */ | ||
1400 | DEF_DISC_AUTH_STR(userid_mutual, NAF_USERID_IN_SET); | ||
1401 | DISC_AUTH_ATTR(userid_mutual, S_IRUGO | S_IWUSR); | ||
1402 | /* | ||
1403 | * Mutual authentication password | ||
1404 | */ | ||
1405 | DEF_DISC_AUTH_STR(password_mutual, NAF_PASSWORD_IN_SET); | ||
1406 | DISC_AUTH_ATTR(password_mutual, S_IRUGO | S_IWUSR); | ||
1407 | |||
1408 | /* | ||
1409 | * enforce_discovery_auth | ||
1410 | */ | ||
1411 | static ssize_t iscsi_disc_show_enforce_discovery_auth( | ||
1412 | struct target_fabric_configfs *tf, | ||
1413 | char *page) | ||
1414 | { | ||
1415 | struct iscsi_node_auth *discovery_auth = &iscsit_global->discovery_acl.node_auth; | ||
1416 | |||
1417 | return sprintf(page, "%d\n", discovery_auth->enforce_discovery_auth); | ||
1418 | } | ||
1419 | |||
1420 | static ssize_t iscsi_disc_store_enforce_discovery_auth( | ||
1421 | struct target_fabric_configfs *tf, | ||
1422 | const char *page, | ||
1423 | size_t count) | ||
1424 | { | ||
1425 | struct iscsi_param *param; | ||
1426 | struct iscsi_portal_group *discovery_tpg = iscsit_global->discovery_tpg; | ||
1427 | char *endptr; | ||
1428 | u32 op; | ||
1429 | |||
1430 | op = simple_strtoul(page, &endptr, 0); | ||
1431 | if ((op != 1) && (op != 0)) { | ||
1432 | pr_err("Illegal value for enforce_discovery_auth:" | ||
1433 | " %u\n", op); | ||
1434 | return -EINVAL; | ||
1435 | } | ||
1436 | |||
1437 | if (!discovery_tpg) { | ||
1438 | pr_err("iscsit_global->discovery_tpg is NULL\n"); | ||
1439 | return -EINVAL; | ||
1440 | } | ||
1441 | |||
1442 | param = iscsi_find_param_from_key(AUTHMETHOD, | ||
1443 | discovery_tpg->param_list); | ||
1444 | if (!param) | ||
1445 | return -EINVAL; | ||
1446 | |||
1447 | if (op) { | ||
1448 | /* | ||
1449 | * Reset the AuthMethod key to CHAP. | ||
1450 | */ | ||
1451 | if (iscsi_update_param_value(param, CHAP) < 0) | ||
1452 | return -EINVAL; | ||
1453 | |||
1454 | discovery_tpg->tpg_attrib.authentication = 1; | ||
1455 | iscsit_global->discovery_acl.node_auth.enforce_discovery_auth = 1; | ||
1456 | pr_debug("LIO-CORE[0] Successfully enabled" | ||
1457 | " authentication enforcement for iSCSI" | ||
1458 | " Discovery TPG\n"); | ||
1459 | } else { | ||
1460 | /* | ||
1461 | * Reset the AuthMethod key to CHAP,None | ||
1462 | */ | ||
1463 | if (iscsi_update_param_value(param, "CHAP,None") < 0) | ||
1464 | return -EINVAL; | ||
1465 | |||
1466 | discovery_tpg->tpg_attrib.authentication = 0; | ||
1467 | iscsit_global->discovery_acl.node_auth.enforce_discovery_auth = 0; | ||
1468 | pr_debug("LIO-CORE[0] Successfully disabled" | ||
1469 | " authentication enforcement for iSCSI" | ||
1470 | " Discovery TPG\n"); | ||
1471 | } | ||
1472 | |||
1473 | return count; | ||
1474 | } | ||
1475 | |||
1476 | DISC_AUTH_ATTR(enforce_discovery_auth, S_IRUGO | S_IWUSR); | ||
1477 | |||
1478 | static struct configfs_attribute *lio_target_discovery_auth_attrs[] = { | ||
1479 | &iscsi_disc_userid.attr, | ||
1480 | &iscsi_disc_password.attr, | ||
1481 | &iscsi_disc_authenticate_target.attr, | ||
1482 | &iscsi_disc_userid_mutual.attr, | ||
1483 | &iscsi_disc_password_mutual.attr, | ||
1484 | &iscsi_disc_enforce_discovery_auth.attr, | ||
1485 | NULL, | ||
1486 | }; | ||
1487 | |||
1488 | /* End lio_target_discovery_auth_cit */ | ||
1489 | |||
1490 | /* Start functions for target_core_fabric_ops */ | ||
1491 | |||
1492 | static char *iscsi_get_fabric_name(void) | ||
1493 | { | ||
1494 | return "iSCSI"; | ||
1495 | } | ||
1496 | |||
1497 | static u32 iscsi_get_task_tag(struct se_cmd *se_cmd) | ||
1498 | { | ||
1499 | struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd); | ||
1500 | |||
1501 | return cmd->init_task_tag; | ||
1502 | } | ||
1503 | |||
1504 | static int iscsi_get_cmd_state(struct se_cmd *se_cmd) | ||
1505 | { | ||
1506 | struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd); | ||
1507 | |||
1508 | return cmd->i_state; | ||
1509 | } | ||
1510 | |||
1511 | static int iscsi_is_state_remove(struct se_cmd *se_cmd) | ||
1512 | { | ||
1513 | struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd); | ||
1514 | |||
1515 | return (cmd->i_state == ISTATE_REMOVE); | ||
1516 | } | ||
1517 | |||
1518 | static int lio_sess_logged_in(struct se_session *se_sess) | ||
1519 | { | ||
1520 | struct iscsi_session *sess = se_sess->fabric_sess_ptr; | ||
1521 | int ret; | ||
1522 | /* | ||
1523 | * Called with spin_lock_bh(&tpg_lock); and | ||
1524 | * spin_lock(&se_tpg->session_lock); held. | ||
1525 | */ | ||
1526 | spin_lock(&sess->conn_lock); | ||
1527 | ret = (sess->session_state != TARG_SESS_STATE_LOGGED_IN); | ||
1528 | spin_unlock(&sess->conn_lock); | ||
1529 | |||
1530 | return ret; | ||
1531 | } | ||
1532 | |||
1533 | static u32 lio_sess_get_index(struct se_session *se_sess) | ||
1534 | { | ||
1535 | struct iscsi_session *sess = se_sess->fabric_sess_ptr; | ||
1536 | |||
1537 | return sess->session_index; | ||
1538 | } | ||
1539 | |||
1540 | static u32 lio_sess_get_initiator_sid( | ||
1541 | struct se_session *se_sess, | ||
1542 | unsigned char *buf, | ||
1543 | u32 size) | ||
1544 | { | ||
1545 | struct iscsi_session *sess = se_sess->fabric_sess_ptr; | ||
1546 | /* | ||
1547 | * iSCSI Initiator Session Identifier from RFC-3720. | ||
1548 | */ | ||
1549 | return snprintf(buf, size, "%02x%02x%02x%02x%02x%02x", | ||
1550 | sess->isid[0], sess->isid[1], sess->isid[2], | ||
1551 | sess->isid[3], sess->isid[4], sess->isid[5]); | ||
1552 | } | ||
1553 | |||
1554 | static int lio_queue_data_in(struct se_cmd *se_cmd) | ||
1555 | { | ||
1556 | struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd); | ||
1557 | |||
1558 | cmd->i_state = ISTATE_SEND_DATAIN; | ||
1559 | iscsit_add_cmd_to_response_queue(cmd, cmd->conn, cmd->i_state); | ||
1560 | return 0; | ||
1561 | } | ||
1562 | |||
1563 | static int lio_write_pending(struct se_cmd *se_cmd) | ||
1564 | { | ||
1565 | struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd); | ||
1566 | |||
1567 | if (!cmd->immediate_data && !cmd->unsolicited_data) | ||
1568 | return iscsit_build_r2ts_for_cmd(cmd, cmd->conn, 1); | ||
1569 | |||
1570 | return 0; | ||
1571 | } | ||
1572 | |||
1573 | static int lio_write_pending_status(struct se_cmd *se_cmd) | ||
1574 | { | ||
1575 | struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd); | ||
1576 | int ret; | ||
1577 | |||
1578 | spin_lock_bh(&cmd->istate_lock); | ||
1579 | ret = !(cmd->cmd_flags & ICF_GOT_LAST_DATAOUT); | ||
1580 | spin_unlock_bh(&cmd->istate_lock); | ||
1581 | |||
1582 | return ret; | ||
1583 | } | ||
1584 | |||
1585 | static int lio_queue_status(struct se_cmd *se_cmd) | ||
1586 | { | ||
1587 | struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd); | ||
1588 | |||
1589 | cmd->i_state = ISTATE_SEND_STATUS; | ||
1590 | iscsit_add_cmd_to_response_queue(cmd, cmd->conn, cmd->i_state); | ||
1591 | return 0; | ||
1592 | } | ||
1593 | |||
1594 | static u16 lio_set_fabric_sense_len(struct se_cmd *se_cmd, u32 sense_length) | ||
1595 | { | ||
1596 | unsigned char *buffer = se_cmd->sense_buffer; | ||
1597 | /* | ||
1598 | * From RFC-3720 10.4.7. Data Segment - Sense and Response Data Segment | ||
1599 | * 16-bit SenseLength. | ||
1600 | */ | ||
1601 | buffer[0] = ((sense_length >> 8) & 0xff); | ||
1602 | buffer[1] = (sense_length & 0xff); | ||
1603 | /* | ||
1604 | * Return two byte offset into allocated sense_buffer. | ||
1605 | */ | ||
1606 | return 2; | ||
1607 | } | ||
1608 | |||
1609 | static u16 lio_get_fabric_sense_len(void) | ||
1610 | { | ||
1611 | /* | ||
1612 | * Return two byte offset into allocated sense_buffer. | ||
1613 | */ | ||
1614 | return 2; | ||
1615 | } | ||
1616 | |||
1617 | static int lio_queue_tm_rsp(struct se_cmd *se_cmd) | ||
1618 | { | ||
1619 | struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd); | ||
1620 | |||
1621 | cmd->i_state = ISTATE_SEND_TASKMGTRSP; | ||
1622 | iscsit_add_cmd_to_response_queue(cmd, cmd->conn, cmd->i_state); | ||
1623 | return 0; | ||
1624 | } | ||
1625 | |||
1626 | static char *lio_tpg_get_endpoint_wwn(struct se_portal_group *se_tpg) | ||
1627 | { | ||
1628 | struct iscsi_portal_group *tpg = se_tpg->se_tpg_fabric_ptr; | ||
1629 | |||
1630 | return &tpg->tpg_tiqn->tiqn[0]; | ||
1631 | } | ||
1632 | |||
1633 | static u16 lio_tpg_get_tag(struct se_portal_group *se_tpg) | ||
1634 | { | ||
1635 | struct iscsi_portal_group *tpg = se_tpg->se_tpg_fabric_ptr; | ||
1636 | |||
1637 | return tpg->tpgt; | ||
1638 | } | ||
1639 | |||
1640 | static u32 lio_tpg_get_default_depth(struct se_portal_group *se_tpg) | ||
1641 | { | ||
1642 | struct iscsi_portal_group *tpg = se_tpg->se_tpg_fabric_ptr; | ||
1643 | |||
1644 | return ISCSI_TPG_ATTRIB(tpg)->default_cmdsn_depth; | ||
1645 | } | ||
1646 | |||
1647 | static int lio_tpg_check_demo_mode(struct se_portal_group *se_tpg) | ||
1648 | { | ||
1649 | struct iscsi_portal_group *tpg = se_tpg->se_tpg_fabric_ptr; | ||
1650 | |||
1651 | return ISCSI_TPG_ATTRIB(tpg)->generate_node_acls; | ||
1652 | } | ||
1653 | |||
1654 | static int lio_tpg_check_demo_mode_cache(struct se_portal_group *se_tpg) | ||
1655 | { | ||
1656 | struct iscsi_portal_group *tpg = se_tpg->se_tpg_fabric_ptr; | ||
1657 | |||
1658 | return ISCSI_TPG_ATTRIB(tpg)->cache_dynamic_acls; | ||
1659 | } | ||
1660 | |||
1661 | static int lio_tpg_check_demo_mode_write_protect( | ||
1662 | struct se_portal_group *se_tpg) | ||
1663 | { | ||
1664 | struct iscsi_portal_group *tpg = se_tpg->se_tpg_fabric_ptr; | ||
1665 | |||
1666 | return ISCSI_TPG_ATTRIB(tpg)->demo_mode_write_protect; | ||
1667 | } | ||
1668 | |||
1669 | static int lio_tpg_check_prod_mode_write_protect( | ||
1670 | struct se_portal_group *se_tpg) | ||
1671 | { | ||
1672 | struct iscsi_portal_group *tpg = se_tpg->se_tpg_fabric_ptr; | ||
1673 | |||
1674 | return ISCSI_TPG_ATTRIB(tpg)->prod_mode_write_protect; | ||
1675 | } | ||
1676 | |||
1677 | static void lio_tpg_release_fabric_acl( | ||
1678 | struct se_portal_group *se_tpg, | ||
1679 | struct se_node_acl *se_acl) | ||
1680 | { | ||
1681 | struct iscsi_node_acl *acl = container_of(se_acl, | ||
1682 | struct iscsi_node_acl, se_node_acl); | ||
1683 | kfree(acl); | ||
1684 | } | ||
1685 | |||
1686 | /* | ||
1687 | * Called with spin_lock_bh(struct se_portal_group->session_lock) held.. | ||
1688 | * | ||
1689 | * Also, this function calls iscsit_inc_session_usage_count() on the | ||
1690 | * struct iscsi_session in question. | ||
1691 | */ | ||
1692 | static int lio_tpg_shutdown_session(struct se_session *se_sess) | ||
1693 | { | ||
1694 | struct iscsi_session *sess = se_sess->fabric_sess_ptr; | ||
1695 | |||
1696 | spin_lock(&sess->conn_lock); | ||
1697 | if (atomic_read(&sess->session_fall_back_to_erl0) || | ||
1698 | atomic_read(&sess->session_logout) || | ||
1699 | (sess->time2retain_timer_flags & ISCSI_TF_EXPIRED)) { | ||
1700 | spin_unlock(&sess->conn_lock); | ||
1701 | return 0; | ||
1702 | } | ||
1703 | atomic_set(&sess->session_reinstatement, 1); | ||
1704 | spin_unlock(&sess->conn_lock); | ||
1705 | |||
1706 | iscsit_inc_session_usage_count(sess); | ||
1707 | iscsit_stop_time2retain_timer(sess); | ||
1708 | |||
1709 | return 1; | ||
1710 | } | ||
1711 | |||
1712 | /* | ||
1713 | * Calls iscsit_dec_session_usage_count() as inverse of | ||
1714 | * lio_tpg_shutdown_session() | ||
1715 | */ | ||
1716 | static void lio_tpg_close_session(struct se_session *se_sess) | ||
1717 | { | ||
1718 | struct iscsi_session *sess = se_sess->fabric_sess_ptr; | ||
1719 | /* | ||
1720 | * If the iSCSI Session for the iSCSI Initiator Node exists, | ||
1721 | * forcefully shutdown the iSCSI NEXUS. | ||
1722 | */ | ||
1723 | iscsit_stop_session(sess, 1, 1); | ||
1724 | iscsit_dec_session_usage_count(sess); | ||
1725 | iscsit_close_session(sess); | ||
1726 | } | ||
1727 | |||
1728 | static void lio_tpg_stop_session( | ||
1729 | struct se_session *se_sess, | ||
1730 | int sess_sleep, | ||
1731 | int conn_sleep) | ||
1732 | { | ||
1733 | struct iscsi_session *sess = se_sess->fabric_sess_ptr; | ||
1734 | |||
1735 | iscsit_stop_session(sess, sess_sleep, conn_sleep); | ||
1736 | } | ||
1737 | |||
1738 | static void lio_tpg_fall_back_to_erl0(struct se_session *se_sess) | ||
1739 | { | ||
1740 | struct iscsi_session *sess = se_sess->fabric_sess_ptr; | ||
1741 | |||
1742 | iscsit_fall_back_to_erl0(sess); | ||
1743 | } | ||
1744 | |||
1745 | static u32 lio_tpg_get_inst_index(struct se_portal_group *se_tpg) | ||
1746 | { | ||
1747 | struct iscsi_portal_group *tpg = se_tpg->se_tpg_fabric_ptr; | ||
1748 | |||
1749 | return tpg->tpg_tiqn->tiqn_index; | ||
1750 | } | ||
1751 | |||
1752 | static void lio_set_default_node_attributes(struct se_node_acl *se_acl) | ||
1753 | { | ||
1754 | struct iscsi_node_acl *acl = container_of(se_acl, struct iscsi_node_acl, | ||
1755 | se_node_acl); | ||
1756 | |||
1757 | ISCSI_NODE_ATTRIB(acl)->nacl = acl; | ||
1758 | iscsit_set_default_node_attribues(acl); | ||
1759 | } | ||
1760 | |||
1761 | static void lio_release_cmd(struct se_cmd *se_cmd) | ||
1762 | { | ||
1763 | struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd); | ||
1764 | |||
1765 | iscsit_release_cmd(cmd); | ||
1766 | } | ||
1767 | |||
1768 | /* End functions for target_core_fabric_ops */ | ||
1769 | |||
1770 | int iscsi_target_register_configfs(void) | ||
1771 | { | ||
1772 | struct target_fabric_configfs *fabric; | ||
1773 | int ret; | ||
1774 | |||
1775 | lio_target_fabric_configfs = NULL; | ||
1776 | fabric = target_fabric_configfs_init(THIS_MODULE, "iscsi"); | ||
1777 | if (IS_ERR(fabric)) { | ||
1778 | pr_err("target_fabric_configfs_init() for" | ||
1779 | " LIO-Target failed!\n"); | ||
1780 | return PTR_ERR(fabric); | ||
1781 | } | ||
1782 | /* | ||
1783 | * Setup the fabric API of function pointers used by target_core_mod.. | ||
1784 | */ | ||
1785 | fabric->tf_ops.get_fabric_name = &iscsi_get_fabric_name; | ||
1786 | fabric->tf_ops.get_fabric_proto_ident = &iscsi_get_fabric_proto_ident; | ||
1787 | fabric->tf_ops.tpg_get_wwn = &lio_tpg_get_endpoint_wwn; | ||
1788 | fabric->tf_ops.tpg_get_tag = &lio_tpg_get_tag; | ||
1789 | fabric->tf_ops.tpg_get_default_depth = &lio_tpg_get_default_depth; | ||
1790 | fabric->tf_ops.tpg_get_pr_transport_id = &iscsi_get_pr_transport_id; | ||
1791 | fabric->tf_ops.tpg_get_pr_transport_id_len = | ||
1792 | &iscsi_get_pr_transport_id_len; | ||
1793 | fabric->tf_ops.tpg_parse_pr_out_transport_id = | ||
1794 | &iscsi_parse_pr_out_transport_id; | ||
1795 | fabric->tf_ops.tpg_check_demo_mode = &lio_tpg_check_demo_mode; | ||
1796 | fabric->tf_ops.tpg_check_demo_mode_cache = | ||
1797 | &lio_tpg_check_demo_mode_cache; | ||
1798 | fabric->tf_ops.tpg_check_demo_mode_write_protect = | ||
1799 | &lio_tpg_check_demo_mode_write_protect; | ||
1800 | fabric->tf_ops.tpg_check_prod_mode_write_protect = | ||
1801 | &lio_tpg_check_prod_mode_write_protect; | ||
1802 | fabric->tf_ops.tpg_alloc_fabric_acl = &lio_tpg_alloc_fabric_acl; | ||
1803 | fabric->tf_ops.tpg_release_fabric_acl = &lio_tpg_release_fabric_acl; | ||
1804 | fabric->tf_ops.tpg_get_inst_index = &lio_tpg_get_inst_index; | ||
1805 | fabric->tf_ops.release_cmd = &lio_release_cmd; | ||
1806 | fabric->tf_ops.shutdown_session = &lio_tpg_shutdown_session; | ||
1807 | fabric->tf_ops.close_session = &lio_tpg_close_session; | ||
1808 | fabric->tf_ops.stop_session = &lio_tpg_stop_session; | ||
1809 | fabric->tf_ops.fall_back_to_erl0 = &lio_tpg_fall_back_to_erl0; | ||
1810 | fabric->tf_ops.sess_logged_in = &lio_sess_logged_in; | ||
1811 | fabric->tf_ops.sess_get_index = &lio_sess_get_index; | ||
1812 | fabric->tf_ops.sess_get_initiator_sid = &lio_sess_get_initiator_sid; | ||
1813 | fabric->tf_ops.write_pending = &lio_write_pending; | ||
1814 | fabric->tf_ops.write_pending_status = &lio_write_pending_status; | ||
1815 | fabric->tf_ops.set_default_node_attributes = | ||
1816 | &lio_set_default_node_attributes; | ||
1817 | fabric->tf_ops.get_task_tag = &iscsi_get_task_tag; | ||
1818 | fabric->tf_ops.get_cmd_state = &iscsi_get_cmd_state; | ||
1819 | fabric->tf_ops.queue_data_in = &lio_queue_data_in; | ||
1820 | fabric->tf_ops.queue_status = &lio_queue_status; | ||
1821 | fabric->tf_ops.queue_tm_rsp = &lio_queue_tm_rsp; | ||
1822 | fabric->tf_ops.set_fabric_sense_len = &lio_set_fabric_sense_len; | ||
1823 | fabric->tf_ops.get_fabric_sense_len = &lio_get_fabric_sense_len; | ||
1824 | fabric->tf_ops.is_state_remove = &iscsi_is_state_remove; | ||
1825 | /* | ||
1826 | * Setup function pointers for generic logic in target_core_fabric_configfs.c | ||
1827 | */ | ||
1828 | fabric->tf_ops.fabric_make_wwn = &lio_target_call_coreaddtiqn; | ||
1829 | fabric->tf_ops.fabric_drop_wwn = &lio_target_call_coredeltiqn; | ||
1830 | fabric->tf_ops.fabric_make_tpg = &lio_target_tiqn_addtpg; | ||
1831 | fabric->tf_ops.fabric_drop_tpg = &lio_target_tiqn_deltpg; | ||
1832 | fabric->tf_ops.fabric_post_link = NULL; | ||
1833 | fabric->tf_ops.fabric_pre_unlink = NULL; | ||
1834 | fabric->tf_ops.fabric_make_np = &lio_target_call_addnptotpg; | ||
1835 | fabric->tf_ops.fabric_drop_np = &lio_target_call_delnpfromtpg; | ||
1836 | fabric->tf_ops.fabric_make_nodeacl = &lio_target_make_nodeacl; | ||
1837 | fabric->tf_ops.fabric_drop_nodeacl = &lio_target_drop_nodeacl; | ||
1838 | /* | ||
1839 | * Setup default attribute lists for various fabric->tf_cit_tmpl | ||
1840 | * sturct config_item_type's | ||
1841 | */ | ||
1842 | TF_CIT_TMPL(fabric)->tfc_discovery_cit.ct_attrs = lio_target_discovery_auth_attrs; | ||
1843 | TF_CIT_TMPL(fabric)->tfc_wwn_cit.ct_attrs = lio_target_wwn_attrs; | ||
1844 | TF_CIT_TMPL(fabric)->tfc_tpg_base_cit.ct_attrs = lio_target_tpg_attrs; | ||
1845 | TF_CIT_TMPL(fabric)->tfc_tpg_attrib_cit.ct_attrs = lio_target_tpg_attrib_attrs; | ||
1846 | TF_CIT_TMPL(fabric)->tfc_tpg_param_cit.ct_attrs = lio_target_tpg_param_attrs; | ||
1847 | TF_CIT_TMPL(fabric)->tfc_tpg_np_base_cit.ct_attrs = lio_target_portal_attrs; | ||
1848 | TF_CIT_TMPL(fabric)->tfc_tpg_nacl_base_cit.ct_attrs = lio_target_initiator_attrs; | ||
1849 | TF_CIT_TMPL(fabric)->tfc_tpg_nacl_attrib_cit.ct_attrs = lio_target_nacl_attrib_attrs; | ||
1850 | TF_CIT_TMPL(fabric)->tfc_tpg_nacl_auth_cit.ct_attrs = lio_target_nacl_auth_attrs; | ||
1851 | TF_CIT_TMPL(fabric)->tfc_tpg_nacl_param_cit.ct_attrs = lio_target_nacl_param_attrs; | ||
1852 | |||
1853 | ret = target_fabric_configfs_register(fabric); | ||
1854 | if (ret < 0) { | ||
1855 | pr_err("target_fabric_configfs_register() for" | ||
1856 | " LIO-Target failed!\n"); | ||
1857 | target_fabric_configfs_free(fabric); | ||
1858 | return ret; | ||
1859 | } | ||
1860 | |||
1861 | lio_target_fabric_configfs = fabric; | ||
1862 | pr_debug("LIO_TARGET[0] - Set fabric ->" | ||
1863 | " lio_target_fabric_configfs\n"); | ||
1864 | return 0; | ||
1865 | } | ||
1866 | |||
1867 | |||
1868 | void iscsi_target_deregister_configfs(void) | ||
1869 | { | ||
1870 | if (!lio_target_fabric_configfs) | ||
1871 | return; | ||
1872 | /* | ||
1873 | * Shutdown discovery sessions and disable discovery TPG | ||
1874 | */ | ||
1875 | if (iscsit_global->discovery_tpg) | ||
1876 | iscsit_tpg_disable_portal_group(iscsit_global->discovery_tpg, 1); | ||
1877 | |||
1878 | target_fabric_configfs_deregister(lio_target_fabric_configfs); | ||
1879 | lio_target_fabric_configfs = NULL; | ||
1880 | pr_debug("LIO_TARGET[0] - Cleared" | ||
1881 | " lio_target_fabric_configfs\n"); | ||
1882 | } | ||
diff --git a/drivers/target/iscsi/iscsi_target_configfs.h b/drivers/target/iscsi/iscsi_target_configfs.h new file mode 100644 index 000000000000..8cd5a63c4edc --- /dev/null +++ b/drivers/target/iscsi/iscsi_target_configfs.h | |||
@@ -0,0 +1,7 @@ | |||
1 | #ifndef ISCSI_TARGET_CONFIGFS_H | ||
2 | #define ISCSI_TARGET_CONFIGFS_H | ||
3 | |||
4 | extern int iscsi_target_register_configfs(void); | ||
5 | extern void iscsi_target_deregister_configfs(void); | ||
6 | |||
7 | #endif /* ISCSI_TARGET_CONFIGFS_H */ | ||
diff --git a/drivers/target/iscsi/iscsi_target_core.h b/drivers/target/iscsi/iscsi_target_core.h new file mode 100644 index 000000000000..470ed551eeb5 --- /dev/null +++ b/drivers/target/iscsi/iscsi_target_core.h | |||
@@ -0,0 +1,859 @@ | |||
1 | #ifndef ISCSI_TARGET_CORE_H | ||
2 | #define ISCSI_TARGET_CORE_H | ||
3 | |||
4 | #include <linux/in.h> | ||
5 | #include <linux/configfs.h> | ||
6 | #include <net/sock.h> | ||
7 | #include <net/tcp.h> | ||
8 | #include <scsi/scsi_cmnd.h> | ||
9 | #include <scsi/iscsi_proto.h> | ||
10 | #include <target/target_core_base.h> | ||
11 | |||
12 | #define ISCSIT_VERSION "v4.1.0-rc1" | ||
13 | #define ISCSI_MAX_DATASN_MISSING_COUNT 16 | ||
14 | #define ISCSI_TX_THREAD_TCP_TIMEOUT 2 | ||
15 | #define ISCSI_RX_THREAD_TCP_TIMEOUT 2 | ||
16 | #define SECONDS_FOR_ASYNC_LOGOUT 10 | ||
17 | #define SECONDS_FOR_ASYNC_TEXT 10 | ||
18 | #define SECONDS_FOR_LOGOUT_COMP 15 | ||
19 | #define WHITE_SPACE " \t\v\f\n\r" | ||
20 | |||
21 | /* struct iscsi_node_attrib sanity values */ | ||
22 | #define NA_DATAOUT_TIMEOUT 3 | ||
23 | #define NA_DATAOUT_TIMEOUT_MAX 60 | ||
24 | #define NA_DATAOUT_TIMEOUT_MIX 2 | ||
25 | #define NA_DATAOUT_TIMEOUT_RETRIES 5 | ||
26 | #define NA_DATAOUT_TIMEOUT_RETRIES_MAX 15 | ||
27 | #define NA_DATAOUT_TIMEOUT_RETRIES_MIN 1 | ||
28 | #define NA_NOPIN_TIMEOUT 5 | ||
29 | #define NA_NOPIN_TIMEOUT_MAX 60 | ||
30 | #define NA_NOPIN_TIMEOUT_MIN 3 | ||
31 | #define NA_NOPIN_RESPONSE_TIMEOUT 5 | ||
32 | #define NA_NOPIN_RESPONSE_TIMEOUT_MAX 60 | ||
33 | #define NA_NOPIN_RESPONSE_TIMEOUT_MIN 3 | ||
34 | #define NA_RANDOM_DATAIN_PDU_OFFSETS 0 | ||
35 | #define NA_RANDOM_DATAIN_SEQ_OFFSETS 0 | ||
36 | #define NA_RANDOM_R2T_OFFSETS 0 | ||
37 | #define NA_DEFAULT_ERL 0 | ||
38 | #define NA_DEFAULT_ERL_MAX 2 | ||
39 | #define NA_DEFAULT_ERL_MIN 0 | ||
40 | |||
41 | /* struct iscsi_tpg_attrib sanity values */ | ||
42 | #define TA_AUTHENTICATION 1 | ||
43 | #define TA_LOGIN_TIMEOUT 15 | ||
44 | #define TA_LOGIN_TIMEOUT_MAX 30 | ||
45 | #define TA_LOGIN_TIMEOUT_MIN 5 | ||
46 | #define TA_NETIF_TIMEOUT 2 | ||
47 | #define TA_NETIF_TIMEOUT_MAX 15 | ||
48 | #define TA_NETIF_TIMEOUT_MIN 2 | ||
49 | #define TA_GENERATE_NODE_ACLS 0 | ||
50 | #define TA_DEFAULT_CMDSN_DEPTH 16 | ||
51 | #define TA_DEFAULT_CMDSN_DEPTH_MAX 512 | ||
52 | #define TA_DEFAULT_CMDSN_DEPTH_MIN 1 | ||
53 | #define TA_CACHE_DYNAMIC_ACLS 0 | ||
54 | /* Enabled by default in demo mode (generic_node_acls=1) */ | ||
55 | #define TA_DEMO_MODE_WRITE_PROTECT 1 | ||
56 | /* Disabled by default in production mode w/ explict ACLs */ | ||
57 | #define TA_PROD_MODE_WRITE_PROTECT 0 | ||
58 | #define TA_CACHE_CORE_NPS 0 | ||
59 | |||
60 | enum tpg_np_network_transport_table { | ||
61 | ISCSI_TCP = 0, | ||
62 | ISCSI_SCTP_TCP = 1, | ||
63 | ISCSI_SCTP_UDP = 2, | ||
64 | ISCSI_IWARP_TCP = 3, | ||
65 | ISCSI_IWARP_SCTP = 4, | ||
66 | ISCSI_INFINIBAND = 5, | ||
67 | }; | ||
68 | |||
69 | /* RFC-3720 7.1.4 Standard Connection State Diagram for a Target */ | ||
70 | enum target_conn_state_table { | ||
71 | TARG_CONN_STATE_FREE = 0x1, | ||
72 | TARG_CONN_STATE_XPT_UP = 0x3, | ||
73 | TARG_CONN_STATE_IN_LOGIN = 0x4, | ||
74 | TARG_CONN_STATE_LOGGED_IN = 0x5, | ||
75 | TARG_CONN_STATE_IN_LOGOUT = 0x6, | ||
76 | TARG_CONN_STATE_LOGOUT_REQUESTED = 0x7, | ||
77 | TARG_CONN_STATE_CLEANUP_WAIT = 0x8, | ||
78 | }; | ||
79 | |||
80 | /* RFC-3720 7.3.2 Session State Diagram for a Target */ | ||
81 | enum target_sess_state_table { | ||
82 | TARG_SESS_STATE_FREE = 0x1, | ||
83 | TARG_SESS_STATE_ACTIVE = 0x2, | ||
84 | TARG_SESS_STATE_LOGGED_IN = 0x3, | ||
85 | TARG_SESS_STATE_FAILED = 0x4, | ||
86 | TARG_SESS_STATE_IN_CONTINUE = 0x5, | ||
87 | }; | ||
88 | |||
89 | /* struct iscsi_data_count->type */ | ||
90 | enum data_count_type { | ||
91 | ISCSI_RX_DATA = 1, | ||
92 | ISCSI_TX_DATA = 2, | ||
93 | }; | ||
94 | |||
95 | /* struct iscsi_datain_req->dr_complete */ | ||
96 | enum datain_req_comp_table { | ||
97 | DATAIN_COMPLETE_NORMAL = 1, | ||
98 | DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY = 2, | ||
99 | DATAIN_COMPLETE_CONNECTION_RECOVERY = 3, | ||
100 | }; | ||
101 | |||
102 | /* struct iscsi_datain_req->recovery */ | ||
103 | enum datain_req_rec_table { | ||
104 | DATAIN_WITHIN_COMMAND_RECOVERY = 1, | ||
105 | DATAIN_CONNECTION_RECOVERY = 2, | ||
106 | }; | ||
107 | |||
108 | /* struct iscsi_portal_group->state */ | ||
109 | enum tpg_state_table { | ||
110 | TPG_STATE_FREE = 0, | ||
111 | TPG_STATE_ACTIVE = 1, | ||
112 | TPG_STATE_INACTIVE = 2, | ||
113 | TPG_STATE_COLD_RESET = 3, | ||
114 | }; | ||
115 | |||
116 | /* struct iscsi_tiqn->tiqn_state */ | ||
117 | enum tiqn_state_table { | ||
118 | TIQN_STATE_ACTIVE = 1, | ||
119 | TIQN_STATE_SHUTDOWN = 2, | ||
120 | }; | ||
121 | |||
122 | /* struct iscsi_cmd->cmd_flags */ | ||
123 | enum cmd_flags_table { | ||
124 | ICF_GOT_LAST_DATAOUT = 0x00000001, | ||
125 | ICF_GOT_DATACK_SNACK = 0x00000002, | ||
126 | ICF_NON_IMMEDIATE_UNSOLICITED_DATA = 0x00000004, | ||
127 | ICF_SENT_LAST_R2T = 0x00000008, | ||
128 | ICF_WITHIN_COMMAND_RECOVERY = 0x00000010, | ||
129 | ICF_CONTIG_MEMORY = 0x00000020, | ||
130 | ICF_ATTACHED_TO_RQUEUE = 0x00000040, | ||
131 | ICF_OOO_CMDSN = 0x00000080, | ||
132 | ICF_REJECT_FAIL_CONN = 0x00000100, | ||
133 | }; | ||
134 | |||
135 | /* struct iscsi_cmd->i_state */ | ||
136 | enum cmd_i_state_table { | ||
137 | ISTATE_NO_STATE = 0, | ||
138 | ISTATE_NEW_CMD = 1, | ||
139 | ISTATE_DEFERRED_CMD = 2, | ||
140 | ISTATE_UNSOLICITED_DATA = 3, | ||
141 | ISTATE_RECEIVE_DATAOUT = 4, | ||
142 | ISTATE_RECEIVE_DATAOUT_RECOVERY = 5, | ||
143 | ISTATE_RECEIVED_LAST_DATAOUT = 6, | ||
144 | ISTATE_WITHIN_DATAOUT_RECOVERY = 7, | ||
145 | ISTATE_IN_CONNECTION_RECOVERY = 8, | ||
146 | ISTATE_RECEIVED_TASKMGT = 9, | ||
147 | ISTATE_SEND_ASYNCMSG = 10, | ||
148 | ISTATE_SENT_ASYNCMSG = 11, | ||
149 | ISTATE_SEND_DATAIN = 12, | ||
150 | ISTATE_SEND_LAST_DATAIN = 13, | ||
151 | ISTATE_SENT_LAST_DATAIN = 14, | ||
152 | ISTATE_SEND_LOGOUTRSP = 15, | ||
153 | ISTATE_SENT_LOGOUTRSP = 16, | ||
154 | ISTATE_SEND_NOPIN = 17, | ||
155 | ISTATE_SENT_NOPIN = 18, | ||
156 | ISTATE_SEND_REJECT = 19, | ||
157 | ISTATE_SENT_REJECT = 20, | ||
158 | ISTATE_SEND_R2T = 21, | ||
159 | ISTATE_SENT_R2T = 22, | ||
160 | ISTATE_SEND_R2T_RECOVERY = 23, | ||
161 | ISTATE_SENT_R2T_RECOVERY = 24, | ||
162 | ISTATE_SEND_LAST_R2T = 25, | ||
163 | ISTATE_SENT_LAST_R2T = 26, | ||
164 | ISTATE_SEND_LAST_R2T_RECOVERY = 27, | ||
165 | ISTATE_SENT_LAST_R2T_RECOVERY = 28, | ||
166 | ISTATE_SEND_STATUS = 29, | ||
167 | ISTATE_SEND_STATUS_BROKEN_PC = 30, | ||
168 | ISTATE_SENT_STATUS = 31, | ||
169 | ISTATE_SEND_STATUS_RECOVERY = 32, | ||
170 | ISTATE_SENT_STATUS_RECOVERY = 33, | ||
171 | ISTATE_SEND_TASKMGTRSP = 34, | ||
172 | ISTATE_SENT_TASKMGTRSP = 35, | ||
173 | ISTATE_SEND_TEXTRSP = 36, | ||
174 | ISTATE_SENT_TEXTRSP = 37, | ||
175 | ISTATE_SEND_NOPIN_WANT_RESPONSE = 38, | ||
176 | ISTATE_SENT_NOPIN_WANT_RESPONSE = 39, | ||
177 | ISTATE_SEND_NOPIN_NO_RESPONSE = 40, | ||
178 | ISTATE_REMOVE = 41, | ||
179 | ISTATE_FREE = 42, | ||
180 | }; | ||
181 | |||
182 | /* Used for iscsi_recover_cmdsn() return values */ | ||
183 | enum recover_cmdsn_ret_table { | ||
184 | CMDSN_ERROR_CANNOT_RECOVER = -1, | ||
185 | CMDSN_NORMAL_OPERATION = 0, | ||
186 | CMDSN_LOWER_THAN_EXP = 1, | ||
187 | CMDSN_HIGHER_THAN_EXP = 2, | ||
188 | }; | ||
189 | |||
190 | /* Used for iscsi_handle_immediate_data() return values */ | ||
191 | enum immedate_data_ret_table { | ||
192 | IMMEDIATE_DATA_CANNOT_RECOVER = -1, | ||
193 | IMMEDIATE_DATA_NORMAL_OPERATION = 0, | ||
194 | IMMEDIATE_DATA_ERL1_CRC_FAILURE = 1, | ||
195 | }; | ||
196 | |||
197 | /* Used for iscsi_decide_dataout_action() return values */ | ||
198 | enum dataout_action_ret_table { | ||
199 | DATAOUT_CANNOT_RECOVER = -1, | ||
200 | DATAOUT_NORMAL = 0, | ||
201 | DATAOUT_SEND_R2T = 1, | ||
202 | DATAOUT_SEND_TO_TRANSPORT = 2, | ||
203 | DATAOUT_WITHIN_COMMAND_RECOVERY = 3, | ||
204 | }; | ||
205 | |||
206 | /* Used for struct iscsi_node_auth->naf_flags */ | ||
207 | enum naf_flags_table { | ||
208 | NAF_USERID_SET = 0x01, | ||
209 | NAF_PASSWORD_SET = 0x02, | ||
210 | NAF_USERID_IN_SET = 0x04, | ||
211 | NAF_PASSWORD_IN_SET = 0x08, | ||
212 | }; | ||
213 | |||
214 | /* Used by various struct timer_list to manage iSCSI specific state */ | ||
215 | enum iscsi_timer_flags_table { | ||
216 | ISCSI_TF_RUNNING = 0x01, | ||
217 | ISCSI_TF_STOP = 0x02, | ||
218 | ISCSI_TF_EXPIRED = 0x04, | ||
219 | }; | ||
220 | |||
221 | /* Used for struct iscsi_np->np_flags */ | ||
222 | enum np_flags_table { | ||
223 | NPF_IP_NETWORK = 0x00, | ||
224 | NPF_SCTP_STRUCT_FILE = 0x01 /* Bugfix */ | ||
225 | }; | ||
226 | |||
227 | /* Used for struct iscsi_np->np_thread_state */ | ||
228 | enum np_thread_state_table { | ||
229 | ISCSI_NP_THREAD_ACTIVE = 1, | ||
230 | ISCSI_NP_THREAD_INACTIVE = 2, | ||
231 | ISCSI_NP_THREAD_RESET = 3, | ||
232 | ISCSI_NP_THREAD_SHUTDOWN = 4, | ||
233 | ISCSI_NP_THREAD_EXIT = 5, | ||
234 | }; | ||
235 | |||
236 | struct iscsi_conn_ops { | ||
237 | u8 HeaderDigest; /* [0,1] == [None,CRC32C] */ | ||
238 | u8 DataDigest; /* [0,1] == [None,CRC32C] */ | ||
239 | u32 MaxRecvDataSegmentLength; /* [512..2**24-1] */ | ||
240 | u8 OFMarker; /* [0,1] == [No,Yes] */ | ||
241 | u8 IFMarker; /* [0,1] == [No,Yes] */ | ||
242 | u32 OFMarkInt; /* [1..65535] */ | ||
243 | u32 IFMarkInt; /* [1..65535] */ | ||
244 | }; | ||
245 | |||
246 | struct iscsi_sess_ops { | ||
247 | char InitiatorName[224]; | ||
248 | char InitiatorAlias[256]; | ||
249 | char TargetName[224]; | ||
250 | char TargetAlias[256]; | ||
251 | char TargetAddress[256]; | ||
252 | u16 TargetPortalGroupTag; /* [0..65535] */ | ||
253 | u16 MaxConnections; /* [1..65535] */ | ||
254 | u8 InitialR2T; /* [0,1] == [No,Yes] */ | ||
255 | u8 ImmediateData; /* [0,1] == [No,Yes] */ | ||
256 | u32 MaxBurstLength; /* [512..2**24-1] */ | ||
257 | u32 FirstBurstLength; /* [512..2**24-1] */ | ||
258 | u16 DefaultTime2Wait; /* [0..3600] */ | ||
259 | u16 DefaultTime2Retain; /* [0..3600] */ | ||
260 | u16 MaxOutstandingR2T; /* [1..65535] */ | ||
261 | u8 DataPDUInOrder; /* [0,1] == [No,Yes] */ | ||
262 | u8 DataSequenceInOrder; /* [0,1] == [No,Yes] */ | ||
263 | u8 ErrorRecoveryLevel; /* [0..2] */ | ||
264 | u8 SessionType; /* [0,1] == [Normal,Discovery]*/ | ||
265 | }; | ||
266 | |||
267 | struct iscsi_queue_req { | ||
268 | int state; | ||
269 | struct iscsi_cmd *cmd; | ||
270 | struct list_head qr_list; | ||
271 | }; | ||
272 | |||
273 | struct iscsi_data_count { | ||
274 | int data_length; | ||
275 | int sync_and_steering; | ||
276 | enum data_count_type type; | ||
277 | u32 iov_count; | ||
278 | u32 ss_iov_count; | ||
279 | u32 ss_marker_count; | ||
280 | struct kvec *iov; | ||
281 | }; | ||
282 | |||
283 | struct iscsi_param_list { | ||
284 | struct list_head param_list; | ||
285 | struct list_head extra_response_list; | ||
286 | }; | ||
287 | |||
288 | struct iscsi_datain_req { | ||
289 | enum datain_req_comp_table dr_complete; | ||
290 | int generate_recovery_values; | ||
291 | enum datain_req_rec_table recovery; | ||
292 | u32 begrun; | ||
293 | u32 runlength; | ||
294 | u32 data_length; | ||
295 | u32 data_offset; | ||
296 | u32 data_offset_end; | ||
297 | u32 data_sn; | ||
298 | u32 next_burst_len; | ||
299 | u32 read_data_done; | ||
300 | u32 seq_send_order; | ||
301 | struct list_head dr_list; | ||
302 | } ____cacheline_aligned; | ||
303 | |||
304 | struct iscsi_ooo_cmdsn { | ||
305 | u16 cid; | ||
306 | u32 batch_count; | ||
307 | u32 cmdsn; | ||
308 | u32 exp_cmdsn; | ||
309 | struct iscsi_cmd *cmd; | ||
310 | struct list_head ooo_list; | ||
311 | } ____cacheline_aligned; | ||
312 | |||
313 | struct iscsi_datain { | ||
314 | u8 flags; | ||
315 | u32 data_sn; | ||
316 | u32 length; | ||
317 | u32 offset; | ||
318 | } ____cacheline_aligned; | ||
319 | |||
320 | struct iscsi_r2t { | ||
321 | int seq_complete; | ||
322 | int recovery_r2t; | ||
323 | int sent_r2t; | ||
324 | u32 r2t_sn; | ||
325 | u32 offset; | ||
326 | u32 targ_xfer_tag; | ||
327 | u32 xfer_len; | ||
328 | struct list_head r2t_list; | ||
329 | } ____cacheline_aligned; | ||
330 | |||
331 | struct iscsi_cmd { | ||
332 | enum iscsi_timer_flags_table dataout_timer_flags; | ||
333 | /* DataOUT timeout retries */ | ||
334 | u8 dataout_timeout_retries; | ||
335 | /* Within command recovery count */ | ||
336 | u8 error_recovery_count; | ||
337 | /* iSCSI dependent state for out or order CmdSNs */ | ||
338 | enum cmd_i_state_table deferred_i_state; | ||
339 | /* iSCSI dependent state */ | ||
340 | enum cmd_i_state_table i_state; | ||
341 | /* Command is an immediate command (ISCSI_OP_IMMEDIATE set) */ | ||
342 | u8 immediate_cmd; | ||
343 | /* Immediate data present */ | ||
344 | u8 immediate_data; | ||
345 | /* iSCSI Opcode */ | ||
346 | u8 iscsi_opcode; | ||
347 | /* iSCSI Response Code */ | ||
348 | u8 iscsi_response; | ||
349 | /* Logout reason when iscsi_opcode == ISCSI_INIT_LOGOUT_CMND */ | ||
350 | u8 logout_reason; | ||
351 | /* Logout response code when iscsi_opcode == ISCSI_INIT_LOGOUT_CMND */ | ||
352 | u8 logout_response; | ||
353 | /* MaxCmdSN has been incremented */ | ||
354 | u8 maxcmdsn_inc; | ||
355 | /* Immediate Unsolicited Dataout */ | ||
356 | u8 unsolicited_data; | ||
357 | /* CID contained in logout PDU when opcode == ISCSI_INIT_LOGOUT_CMND */ | ||
358 | u16 logout_cid; | ||
359 | /* Command flags */ | ||
360 | enum cmd_flags_table cmd_flags; | ||
361 | /* Initiator Task Tag assigned from Initiator */ | ||
362 | u32 init_task_tag; | ||
363 | /* Target Transfer Tag assigned from Target */ | ||
364 | u32 targ_xfer_tag; | ||
365 | /* CmdSN assigned from Initiator */ | ||
366 | u32 cmd_sn; | ||
367 | /* ExpStatSN assigned from Initiator */ | ||
368 | u32 exp_stat_sn; | ||
369 | /* StatSN assigned to this ITT */ | ||
370 | u32 stat_sn; | ||
371 | /* DataSN Counter */ | ||
372 | u32 data_sn; | ||
373 | /* R2TSN Counter */ | ||
374 | u32 r2t_sn; | ||
375 | /* Last DataSN acknowledged via DataAck SNACK */ | ||
376 | u32 acked_data_sn; | ||
377 | /* Used for echoing NOPOUT ping data */ | ||
378 | u32 buf_ptr_size; | ||
379 | /* Used to store DataDigest */ | ||
380 | u32 data_crc; | ||
381 | /* Total size in bytes associated with command */ | ||
382 | u32 data_length; | ||
383 | /* Counter for MaxOutstandingR2T */ | ||
384 | u32 outstanding_r2ts; | ||
385 | /* Next R2T Offset when DataSequenceInOrder=Yes */ | ||
386 | u32 r2t_offset; | ||
387 | /* Iovec current and orig count for iscsi_cmd->iov_data */ | ||
388 | u32 iov_data_count; | ||
389 | u32 orig_iov_data_count; | ||
390 | /* Number of miscellaneous iovecs used for IP stack calls */ | ||
391 | u32 iov_misc_count; | ||
392 | /* Number of struct iscsi_pdu in struct iscsi_cmd->pdu_list */ | ||
393 | u32 pdu_count; | ||
394 | /* Next struct iscsi_pdu to send in struct iscsi_cmd->pdu_list */ | ||
395 | u32 pdu_send_order; | ||
396 | /* Current struct iscsi_pdu in struct iscsi_cmd->pdu_list */ | ||
397 | u32 pdu_start; | ||
398 | u32 residual_count; | ||
399 | /* Next struct iscsi_seq to send in struct iscsi_cmd->seq_list */ | ||
400 | u32 seq_send_order; | ||
401 | /* Number of struct iscsi_seq in struct iscsi_cmd->seq_list */ | ||
402 | u32 seq_count; | ||
403 | /* Current struct iscsi_seq in struct iscsi_cmd->seq_list */ | ||
404 | u32 seq_no; | ||
405 | /* Lowest offset in current DataOUT sequence */ | ||
406 | u32 seq_start_offset; | ||
407 | /* Highest offset in current DataOUT sequence */ | ||
408 | u32 seq_end_offset; | ||
409 | /* Total size in bytes received so far of READ data */ | ||
410 | u32 read_data_done; | ||
411 | /* Total size in bytes received so far of WRITE data */ | ||
412 | u32 write_data_done; | ||
413 | /* Counter for FirstBurstLength key */ | ||
414 | u32 first_burst_len; | ||
415 | /* Counter for MaxBurstLength key */ | ||
416 | u32 next_burst_len; | ||
417 | /* Transfer size used for IP stack calls */ | ||
418 | u32 tx_size; | ||
419 | /* Buffer used for various purposes */ | ||
420 | void *buf_ptr; | ||
421 | /* See include/linux/dma-mapping.h */ | ||
422 | enum dma_data_direction data_direction; | ||
423 | /* iSCSI PDU Header + CRC */ | ||
424 | unsigned char pdu[ISCSI_HDR_LEN + ISCSI_CRC_LEN]; | ||
425 | /* Number of times struct iscsi_cmd is present in immediate queue */ | ||
426 | atomic_t immed_queue_count; | ||
427 | atomic_t response_queue_count; | ||
428 | atomic_t transport_sent; | ||
429 | spinlock_t datain_lock; | ||
430 | spinlock_t dataout_timeout_lock; | ||
431 | /* spinlock for protecting struct iscsi_cmd->i_state */ | ||
432 | spinlock_t istate_lock; | ||
433 | /* spinlock for adding within command recovery entries */ | ||
434 | spinlock_t error_lock; | ||
435 | /* spinlock for adding R2Ts */ | ||
436 | spinlock_t r2t_lock; | ||
437 | /* DataIN List */ | ||
438 | struct list_head datain_list; | ||
439 | /* R2T List */ | ||
440 | struct list_head cmd_r2t_list; | ||
441 | struct completion reject_comp; | ||
442 | /* Timer for DataOUT */ | ||
443 | struct timer_list dataout_timer; | ||
444 | /* Iovecs for SCSI data payload RX/TX w/ kernel level sockets */ | ||
445 | struct kvec *iov_data; | ||
446 | /* Iovecs for miscellaneous purposes */ | ||
447 | #define ISCSI_MISC_IOVECS 5 | ||
448 | struct kvec iov_misc[ISCSI_MISC_IOVECS]; | ||
449 | /* Array of struct iscsi_pdu used for DataPDUInOrder=No */ | ||
450 | struct iscsi_pdu *pdu_list; | ||
451 | /* Current struct iscsi_pdu used for DataPDUInOrder=No */ | ||
452 | struct iscsi_pdu *pdu_ptr; | ||
453 | /* Array of struct iscsi_seq used for DataSequenceInOrder=No */ | ||
454 | struct iscsi_seq *seq_list; | ||
455 | /* Current struct iscsi_seq used for DataSequenceInOrder=No */ | ||
456 | struct iscsi_seq *seq_ptr; | ||
457 | /* TMR Request when iscsi_opcode == ISCSI_OP_SCSI_TMFUNC */ | ||
458 | struct iscsi_tmr_req *tmr_req; | ||
459 | /* Connection this command is alligient to */ | ||
460 | struct iscsi_conn *conn; | ||
461 | /* Pointer to connection recovery entry */ | ||
462 | struct iscsi_conn_recovery *cr; | ||
463 | /* Session the command is part of, used for connection recovery */ | ||
464 | struct iscsi_session *sess; | ||
465 | /* list_head for connection list */ | ||
466 | struct list_head i_list; | ||
467 | /* The TCM I/O descriptor that is accessed via container_of() */ | ||
468 | struct se_cmd se_cmd; | ||
469 | /* Sense buffer that will be mapped into outgoing status */ | ||
470 | #define ISCSI_SENSE_BUFFER_LEN (TRANSPORT_SENSE_BUFFER + 2) | ||
471 | unsigned char sense_buffer[ISCSI_SENSE_BUFFER_LEN]; | ||
472 | |||
473 | struct scatterlist *t_mem_sg; | ||
474 | u32 t_mem_sg_nents; | ||
475 | |||
476 | u32 padding; | ||
477 | u8 pad_bytes[4]; | ||
478 | |||
479 | struct scatterlist *first_data_sg; | ||
480 | u32 first_data_sg_off; | ||
481 | u32 kmapped_nents; | ||
482 | |||
483 | } ____cacheline_aligned; | ||
484 | |||
485 | struct iscsi_tmr_req { | ||
486 | bool task_reassign:1; | ||
487 | u32 ref_cmd_sn; | ||
488 | u32 exp_data_sn; | ||
489 | struct iscsi_conn_recovery *conn_recovery; | ||
490 | struct se_tmr_req *se_tmr_req; | ||
491 | }; | ||
492 | |||
493 | struct iscsi_conn { | ||
494 | /* Authentication Successful for this connection */ | ||
495 | u8 auth_complete; | ||
496 | /* State connection is currently in */ | ||
497 | u8 conn_state; | ||
498 | u8 conn_logout_reason; | ||
499 | u8 network_transport; | ||
500 | enum iscsi_timer_flags_table nopin_timer_flags; | ||
501 | enum iscsi_timer_flags_table nopin_response_timer_flags; | ||
502 | u8 tx_immediate_queue; | ||
503 | u8 tx_response_queue; | ||
504 | /* Used to know what thread encountered a transport failure */ | ||
505 | u8 which_thread; | ||
506 | /* connection id assigned by the Initiator */ | ||
507 | u16 cid; | ||
508 | /* Remote TCP Port */ | ||
509 | u16 login_port; | ||
510 | int net_size; | ||
511 | u32 auth_id; | ||
512 | #define CONNFLAG_SCTP_STRUCT_FILE 0x01 | ||
513 | u32 conn_flags; | ||
514 | /* Used for iscsi_tx_login_rsp() */ | ||
515 | u32 login_itt; | ||
516 | u32 exp_statsn; | ||
517 | /* Per connection status sequence number */ | ||
518 | u32 stat_sn; | ||
519 | /* IFMarkInt's Current Value */ | ||
520 | u32 if_marker; | ||
521 | /* OFMarkInt's Current Value */ | ||
522 | u32 of_marker; | ||
523 | /* Used for calculating OFMarker offset to next PDU */ | ||
524 | u32 of_marker_offset; | ||
525 | /* Complete Bad PDU for sending reject */ | ||
526 | unsigned char bad_hdr[ISCSI_HDR_LEN]; | ||
527 | #define IPV6_ADDRESS_SPACE 48 | ||
528 | unsigned char login_ip[IPV6_ADDRESS_SPACE]; | ||
529 | int conn_usage_count; | ||
530 | int conn_waiting_on_uc; | ||
531 | atomic_t check_immediate_queue; | ||
532 | atomic_t conn_logout_remove; | ||
533 | atomic_t connection_exit; | ||
534 | atomic_t connection_recovery; | ||
535 | atomic_t connection_reinstatement; | ||
536 | atomic_t connection_wait; | ||
537 | atomic_t connection_wait_rcfr; | ||
538 | atomic_t sleep_on_conn_wait_comp; | ||
539 | atomic_t transport_failed; | ||
540 | struct completion conn_post_wait_comp; | ||
541 | struct completion conn_wait_comp; | ||
542 | struct completion conn_wait_rcfr_comp; | ||
543 | struct completion conn_waiting_on_uc_comp; | ||
544 | struct completion conn_logout_comp; | ||
545 | struct completion tx_half_close_comp; | ||
546 | struct completion rx_half_close_comp; | ||
547 | /* socket used by this connection */ | ||
548 | struct socket *sock; | ||
549 | struct timer_list nopin_timer; | ||
550 | struct timer_list nopin_response_timer; | ||
551 | struct timer_list transport_timer; | ||
552 | /* Spinlock used for add/deleting cmd's from conn_cmd_list */ | ||
553 | spinlock_t cmd_lock; | ||
554 | spinlock_t conn_usage_lock; | ||
555 | spinlock_t immed_queue_lock; | ||
556 | spinlock_t nopin_timer_lock; | ||
557 | spinlock_t response_queue_lock; | ||
558 | spinlock_t state_lock; | ||
559 | /* libcrypto RX and TX contexts for crc32c */ | ||
560 | struct hash_desc conn_rx_hash; | ||
561 | struct hash_desc conn_tx_hash; | ||
562 | /* Used for scheduling TX and RX connection kthreads */ | ||
563 | cpumask_var_t conn_cpumask; | ||
564 | int conn_rx_reset_cpumask:1; | ||
565 | int conn_tx_reset_cpumask:1; | ||
566 | /* list_head of struct iscsi_cmd for this connection */ | ||
567 | struct list_head conn_cmd_list; | ||
568 | struct list_head immed_queue_list; | ||
569 | struct list_head response_queue_list; | ||
570 | struct iscsi_conn_ops *conn_ops; | ||
571 | struct iscsi_param_list *param_list; | ||
572 | /* Used for per connection auth state machine */ | ||
573 | void *auth_protocol; | ||
574 | struct iscsi_login_thread_s *login_thread; | ||
575 | struct iscsi_portal_group *tpg; | ||
576 | /* Pointer to parent session */ | ||
577 | struct iscsi_session *sess; | ||
578 | /* Pointer to thread_set in use for this conn's threads */ | ||
579 | struct iscsi_thread_set *thread_set; | ||
580 | /* list_head for session connection list */ | ||
581 | struct list_head conn_list; | ||
582 | } ____cacheline_aligned; | ||
583 | |||
584 | struct iscsi_conn_recovery { | ||
585 | u16 cid; | ||
586 | u32 cmd_count; | ||
587 | u32 maxrecvdatasegmentlength; | ||
588 | int ready_for_reallegiance; | ||
589 | struct list_head conn_recovery_cmd_list; | ||
590 | spinlock_t conn_recovery_cmd_lock; | ||
591 | struct timer_list time2retain_timer; | ||
592 | struct iscsi_session *sess; | ||
593 | struct list_head cr_list; | ||
594 | } ____cacheline_aligned; | ||
595 | |||
596 | struct iscsi_session { | ||
597 | u8 initiator_vendor; | ||
598 | u8 isid[6]; | ||
599 | enum iscsi_timer_flags_table time2retain_timer_flags; | ||
600 | u8 version_active; | ||
601 | u16 cid_called; | ||
602 | u16 conn_recovery_count; | ||
603 | u16 tsih; | ||
604 | /* state session is currently in */ | ||
605 | u32 session_state; | ||
606 | /* session wide counter: initiator assigned task tag */ | ||
607 | u32 init_task_tag; | ||
608 | /* session wide counter: target assigned task tag */ | ||
609 | u32 targ_xfer_tag; | ||
610 | u32 cmdsn_window; | ||
611 | |||
612 | /* protects cmdsn values */ | ||
613 | struct mutex cmdsn_mutex; | ||
614 | /* session wide counter: expected command sequence number */ | ||
615 | u32 exp_cmd_sn; | ||
616 | /* session wide counter: maximum allowed command sequence number */ | ||
617 | u32 max_cmd_sn; | ||
618 | struct list_head sess_ooo_cmdsn_list; | ||
619 | |||
620 | /* LIO specific session ID */ | ||
621 | u32 sid; | ||
622 | char auth_type[8]; | ||
623 | /* unique within the target */ | ||
624 | int session_index; | ||
625 | /* Used for session reference counting */ | ||
626 | int session_usage_count; | ||
627 | int session_waiting_on_uc; | ||
628 | u32 cmd_pdus; | ||
629 | u32 rsp_pdus; | ||
630 | u64 tx_data_octets; | ||
631 | u64 rx_data_octets; | ||
632 | u32 conn_digest_errors; | ||
633 | u32 conn_timeout_errors; | ||
634 | u64 creation_time; | ||
635 | spinlock_t session_stats_lock; | ||
636 | /* Number of active connections */ | ||
637 | atomic_t nconn; | ||
638 | atomic_t session_continuation; | ||
639 | atomic_t session_fall_back_to_erl0; | ||
640 | atomic_t session_logout; | ||
641 | atomic_t session_reinstatement; | ||
642 | atomic_t session_stop_active; | ||
643 | atomic_t sleep_on_sess_wait_comp; | ||
644 | atomic_t transport_wait_cmds; | ||
645 | /* connection list */ | ||
646 | struct list_head sess_conn_list; | ||
647 | struct list_head cr_active_list; | ||
648 | struct list_head cr_inactive_list; | ||
649 | spinlock_t conn_lock; | ||
650 | spinlock_t cr_a_lock; | ||
651 | spinlock_t cr_i_lock; | ||
652 | spinlock_t session_usage_lock; | ||
653 | spinlock_t ttt_lock; | ||
654 | struct completion async_msg_comp; | ||
655 | struct completion reinstatement_comp; | ||
656 | struct completion session_wait_comp; | ||
657 | struct completion session_waiting_on_uc_comp; | ||
658 | struct timer_list time2retain_timer; | ||
659 | struct iscsi_sess_ops *sess_ops; | ||
660 | struct se_session *se_sess; | ||
661 | struct iscsi_portal_group *tpg; | ||
662 | } ____cacheline_aligned; | ||
663 | |||
664 | struct iscsi_login { | ||
665 | u8 auth_complete; | ||
666 | u8 checked_for_existing; | ||
667 | u8 current_stage; | ||
668 | u8 leading_connection; | ||
669 | u8 first_request; | ||
670 | u8 version_min; | ||
671 | u8 version_max; | ||
672 | char isid[6]; | ||
673 | u32 cmd_sn; | ||
674 | u32 init_task_tag; | ||
675 | u32 initial_exp_statsn; | ||
676 | u32 rsp_length; | ||
677 | u16 cid; | ||
678 | u16 tsih; | ||
679 | char *req; | ||
680 | char *rsp; | ||
681 | char *req_buf; | ||
682 | char *rsp_buf; | ||
683 | } ____cacheline_aligned; | ||
684 | |||
685 | struct iscsi_node_attrib { | ||
686 | u32 dataout_timeout; | ||
687 | u32 dataout_timeout_retries; | ||
688 | u32 default_erl; | ||
689 | u32 nopin_timeout; | ||
690 | u32 nopin_response_timeout; | ||
691 | u32 random_datain_pdu_offsets; | ||
692 | u32 random_datain_seq_offsets; | ||
693 | u32 random_r2t_offsets; | ||
694 | u32 tmr_cold_reset; | ||
695 | u32 tmr_warm_reset; | ||
696 | struct iscsi_node_acl *nacl; | ||
697 | }; | ||
698 | |||
699 | struct se_dev_entry_s; | ||
700 | |||
701 | struct iscsi_node_auth { | ||
702 | enum naf_flags_table naf_flags; | ||
703 | int authenticate_target; | ||
704 | /* Used for iscsit_global->discovery_auth, | ||
705 | * set to zero (auth disabled) by default */ | ||
706 | int enforce_discovery_auth; | ||
707 | #define MAX_USER_LEN 256 | ||
708 | #define MAX_PASS_LEN 256 | ||
709 | char userid[MAX_USER_LEN]; | ||
710 | char password[MAX_PASS_LEN]; | ||
711 | char userid_mutual[MAX_USER_LEN]; | ||
712 | char password_mutual[MAX_PASS_LEN]; | ||
713 | }; | ||
714 | |||
715 | #include "iscsi_target_stat.h" | ||
716 | |||
717 | struct iscsi_node_stat_grps { | ||
718 | struct config_group iscsi_sess_stats_group; | ||
719 | struct config_group iscsi_conn_stats_group; | ||
720 | }; | ||
721 | |||
722 | struct iscsi_node_acl { | ||
723 | struct iscsi_node_attrib node_attrib; | ||
724 | struct iscsi_node_auth node_auth; | ||
725 | struct iscsi_node_stat_grps node_stat_grps; | ||
726 | struct se_node_acl se_node_acl; | ||
727 | }; | ||
728 | |||
729 | #define NODE_STAT_GRPS(nacl) (&(nacl)->node_stat_grps) | ||
730 | |||
731 | #define ISCSI_NODE_ATTRIB(t) (&(t)->node_attrib) | ||
732 | #define ISCSI_NODE_AUTH(t) (&(t)->node_auth) | ||
733 | |||
734 | struct iscsi_tpg_attrib { | ||
735 | u32 authentication; | ||
736 | u32 login_timeout; | ||
737 | u32 netif_timeout; | ||
738 | u32 generate_node_acls; | ||
739 | u32 cache_dynamic_acls; | ||
740 | u32 default_cmdsn_depth; | ||
741 | u32 demo_mode_write_protect; | ||
742 | u32 prod_mode_write_protect; | ||
743 | struct iscsi_portal_group *tpg; | ||
744 | }; | ||
745 | |||
746 | struct iscsi_np { | ||
747 | int np_network_transport; | ||
748 | int np_ip_proto; | ||
749 | int np_sock_type; | ||
750 | enum np_thread_state_table np_thread_state; | ||
751 | enum iscsi_timer_flags_table np_login_timer_flags; | ||
752 | u32 np_exports; | ||
753 | enum np_flags_table np_flags; | ||
754 | unsigned char np_ip[IPV6_ADDRESS_SPACE]; | ||
755 | u16 np_port; | ||
756 | spinlock_t np_thread_lock; | ||
757 | struct completion np_restart_comp; | ||
758 | struct socket *np_socket; | ||
759 | struct __kernel_sockaddr_storage np_sockaddr; | ||
760 | struct task_struct *np_thread; | ||
761 | struct timer_list np_login_timer; | ||
762 | struct iscsi_portal_group *np_login_tpg; | ||
763 | struct list_head np_list; | ||
764 | } ____cacheline_aligned; | ||
765 | |||
766 | struct iscsi_tpg_np { | ||
767 | struct iscsi_np *tpg_np; | ||
768 | struct iscsi_portal_group *tpg; | ||
769 | struct iscsi_tpg_np *tpg_np_parent; | ||
770 | struct list_head tpg_np_list; | ||
771 | struct list_head tpg_np_child_list; | ||
772 | struct list_head tpg_np_parent_list; | ||
773 | struct se_tpg_np se_tpg_np; | ||
774 | spinlock_t tpg_np_parent_lock; | ||
775 | }; | ||
776 | |||
777 | struct iscsi_portal_group { | ||
778 | unsigned char tpg_chap_id; | ||
779 | /* TPG State */ | ||
780 | enum tpg_state_table tpg_state; | ||
781 | /* Target Portal Group Tag */ | ||
782 | u16 tpgt; | ||
783 | /* Id assigned to target sessions */ | ||
784 | u16 ntsih; | ||
785 | /* Number of active sessions */ | ||
786 | u32 nsessions; | ||
787 | /* Number of Network Portals available for this TPG */ | ||
788 | u32 num_tpg_nps; | ||
789 | /* Per TPG LIO specific session ID. */ | ||
790 | u32 sid; | ||
791 | /* Spinlock for adding/removing Network Portals */ | ||
792 | spinlock_t tpg_np_lock; | ||
793 | spinlock_t tpg_state_lock; | ||
794 | struct se_portal_group tpg_se_tpg; | ||
795 | struct mutex tpg_access_lock; | ||
796 | struct mutex np_login_lock; | ||
797 | struct iscsi_tpg_attrib tpg_attrib; | ||
798 | /* Pointer to default list of iSCSI parameters for TPG */ | ||
799 | struct iscsi_param_list *param_list; | ||
800 | struct iscsi_tiqn *tpg_tiqn; | ||
801 | struct list_head tpg_gnp_list; | ||
802 | struct list_head tpg_list; | ||
803 | } ____cacheline_aligned; | ||
804 | |||
805 | #define ISCSI_TPG_C(c) ((struct iscsi_portal_group *)(c)->tpg) | ||
806 | #define ISCSI_TPG_LUN(c, l) ((iscsi_tpg_list_t *)(c)->tpg->tpg_lun_list_t[l]) | ||
807 | #define ISCSI_TPG_S(s) ((struct iscsi_portal_group *)(s)->tpg) | ||
808 | #define ISCSI_TPG_ATTRIB(t) (&(t)->tpg_attrib) | ||
809 | #define SE_TPG(tpg) (&(tpg)->tpg_se_tpg) | ||
810 | |||
811 | struct iscsi_wwn_stat_grps { | ||
812 | struct config_group iscsi_stat_group; | ||
813 | struct config_group iscsi_instance_group; | ||
814 | struct config_group iscsi_sess_err_group; | ||
815 | struct config_group iscsi_tgt_attr_group; | ||
816 | struct config_group iscsi_login_stats_group; | ||
817 | struct config_group iscsi_logout_stats_group; | ||
818 | }; | ||
819 | |||
820 | struct iscsi_tiqn { | ||
821 | #define ISCSI_IQN_LEN 224 | ||
822 | unsigned char tiqn[ISCSI_IQN_LEN]; | ||
823 | enum tiqn_state_table tiqn_state; | ||
824 | int tiqn_access_count; | ||
825 | u32 tiqn_active_tpgs; | ||
826 | u32 tiqn_ntpgs; | ||
827 | u32 tiqn_num_tpg_nps; | ||
828 | u32 tiqn_nsessions; | ||
829 | struct list_head tiqn_list; | ||
830 | struct list_head tiqn_tpg_list; | ||
831 | spinlock_t tiqn_state_lock; | ||
832 | spinlock_t tiqn_tpg_lock; | ||
833 | struct se_wwn tiqn_wwn; | ||
834 | struct iscsi_wwn_stat_grps tiqn_stat_grps; | ||
835 | int tiqn_index; | ||
836 | struct iscsi_sess_err_stats sess_err_stats; | ||
837 | struct iscsi_login_stats login_stats; | ||
838 | struct iscsi_logout_stats logout_stats; | ||
839 | } ____cacheline_aligned; | ||
840 | |||
841 | #define WWN_STAT_GRPS(tiqn) (&(tiqn)->tiqn_stat_grps) | ||
842 | |||
843 | struct iscsit_global { | ||
844 | /* In core shutdown */ | ||
845 | u32 in_shutdown; | ||
846 | u32 active_ts; | ||
847 | /* Unique identifier used for the authentication daemon */ | ||
848 | u32 auth_id; | ||
849 | u32 inactive_ts; | ||
850 | /* Thread Set bitmap count */ | ||
851 | int ts_bitmap_count; | ||
852 | /* Thread Set bitmap pointer */ | ||
853 | unsigned long *ts_bitmap; | ||
854 | /* Used for iSCSI discovery session authentication */ | ||
855 | struct iscsi_node_acl discovery_acl; | ||
856 | struct iscsi_portal_group *discovery_tpg; | ||
857 | }; | ||
858 | |||
859 | #endif /* ISCSI_TARGET_CORE_H */ | ||
diff --git a/drivers/target/iscsi/iscsi_target_datain_values.c b/drivers/target/iscsi/iscsi_target_datain_values.c new file mode 100644 index 000000000000..8c0495129513 --- /dev/null +++ b/drivers/target/iscsi/iscsi_target_datain_values.c | |||
@@ -0,0 +1,531 @@ | |||
1 | /******************************************************************************* | ||
2 | * This file contains the iSCSI Target DataIN value generation functions. | ||
3 | * | ||
4 | * \u00a9 Copyright 2007-2011 RisingTide Systems LLC. | ||
5 | * | ||
6 | * Licensed to the Linux Foundation under the General Public License (GPL) version 2. | ||
7 | * | ||
8 | * Author: Nicholas A. Bellinger <nab@linux-iscsi.org> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | ******************************************************************************/ | ||
20 | |||
21 | #include <scsi/iscsi_proto.h> | ||
22 | |||
23 | #include "iscsi_target_core.h" | ||
24 | #include "iscsi_target_seq_pdu_list.h" | ||
25 | #include "iscsi_target_erl1.h" | ||
26 | #include "iscsi_target_util.h" | ||
27 | #include "iscsi_target.h" | ||
28 | #include "iscsi_target_datain_values.h" | ||
29 | |||
30 | struct iscsi_datain_req *iscsit_allocate_datain_req(void) | ||
31 | { | ||
32 | struct iscsi_datain_req *dr; | ||
33 | |||
34 | dr = kmem_cache_zalloc(lio_dr_cache, GFP_ATOMIC); | ||
35 | if (!dr) { | ||
36 | pr_err("Unable to allocate memory for" | ||
37 | " struct iscsi_datain_req\n"); | ||
38 | return NULL; | ||
39 | } | ||
40 | INIT_LIST_HEAD(&dr->dr_list); | ||
41 | |||
42 | return dr; | ||
43 | } | ||
44 | |||
45 | void iscsit_attach_datain_req(struct iscsi_cmd *cmd, struct iscsi_datain_req *dr) | ||
46 | { | ||
47 | spin_lock(&cmd->datain_lock); | ||
48 | list_add_tail(&dr->dr_list, &cmd->datain_list); | ||
49 | spin_unlock(&cmd->datain_lock); | ||
50 | } | ||
51 | |||
52 | void iscsit_free_datain_req(struct iscsi_cmd *cmd, struct iscsi_datain_req *dr) | ||
53 | { | ||
54 | spin_lock(&cmd->datain_lock); | ||
55 | list_del(&dr->dr_list); | ||
56 | spin_unlock(&cmd->datain_lock); | ||
57 | |||
58 | kmem_cache_free(lio_dr_cache, dr); | ||
59 | } | ||
60 | |||
61 | void iscsit_free_all_datain_reqs(struct iscsi_cmd *cmd) | ||
62 | { | ||
63 | struct iscsi_datain_req *dr, *dr_tmp; | ||
64 | |||
65 | spin_lock(&cmd->datain_lock); | ||
66 | list_for_each_entry_safe(dr, dr_tmp, &cmd->datain_list, dr_list) { | ||
67 | list_del(&dr->dr_list); | ||
68 | kmem_cache_free(lio_dr_cache, dr); | ||
69 | } | ||
70 | spin_unlock(&cmd->datain_lock); | ||
71 | } | ||
72 | |||
73 | struct iscsi_datain_req *iscsit_get_datain_req(struct iscsi_cmd *cmd) | ||
74 | { | ||
75 | struct iscsi_datain_req *dr; | ||
76 | |||
77 | if (list_empty(&cmd->datain_list)) { | ||
78 | pr_err("cmd->datain_list is empty for ITT:" | ||
79 | " 0x%08x\n", cmd->init_task_tag); | ||
80 | return NULL; | ||
81 | } | ||
82 | list_for_each_entry(dr, &cmd->datain_list, dr_list) | ||
83 | break; | ||
84 | |||
85 | return dr; | ||
86 | } | ||
87 | |||
88 | /* | ||
89 | * For Normal and Recovery DataSequenceInOrder=Yes and DataPDUInOrder=Yes. | ||
90 | */ | ||
91 | static struct iscsi_datain_req *iscsit_set_datain_values_yes_and_yes( | ||
92 | struct iscsi_cmd *cmd, | ||
93 | struct iscsi_datain *datain) | ||
94 | { | ||
95 | u32 next_burst_len, read_data_done, read_data_left; | ||
96 | struct iscsi_conn *conn = cmd->conn; | ||
97 | struct iscsi_datain_req *dr; | ||
98 | |||
99 | dr = iscsit_get_datain_req(cmd); | ||
100 | if (!dr) | ||
101 | return NULL; | ||
102 | |||
103 | if (dr->recovery && dr->generate_recovery_values) { | ||
104 | if (iscsit_create_recovery_datain_values_datasequenceinorder_yes( | ||
105 | cmd, dr) < 0) | ||
106 | return NULL; | ||
107 | |||
108 | dr->generate_recovery_values = 0; | ||
109 | } | ||
110 | |||
111 | next_burst_len = (!dr->recovery) ? | ||
112 | cmd->next_burst_len : dr->next_burst_len; | ||
113 | read_data_done = (!dr->recovery) ? | ||
114 | cmd->read_data_done : dr->read_data_done; | ||
115 | |||
116 | read_data_left = (cmd->data_length - read_data_done); | ||
117 | if (!read_data_left) { | ||
118 | pr_err("ITT: 0x%08x read_data_left is zero!\n", | ||
119 | cmd->init_task_tag); | ||
120 | return NULL; | ||
121 | } | ||
122 | |||
123 | if ((read_data_left <= conn->conn_ops->MaxRecvDataSegmentLength) && | ||
124 | (read_data_left <= (conn->sess->sess_ops->MaxBurstLength - | ||
125 | next_burst_len))) { | ||
126 | datain->length = read_data_left; | ||
127 | |||
128 | datain->flags |= (ISCSI_FLAG_CMD_FINAL | ISCSI_FLAG_DATA_STATUS); | ||
129 | if (conn->sess->sess_ops->ErrorRecoveryLevel > 0) | ||
130 | datain->flags |= ISCSI_FLAG_DATA_ACK; | ||
131 | } else { | ||
132 | if ((next_burst_len + | ||
133 | conn->conn_ops->MaxRecvDataSegmentLength) < | ||
134 | conn->sess->sess_ops->MaxBurstLength) { | ||
135 | datain->length = | ||
136 | conn->conn_ops->MaxRecvDataSegmentLength; | ||
137 | next_burst_len += datain->length; | ||
138 | } else { | ||
139 | datain->length = (conn->sess->sess_ops->MaxBurstLength - | ||
140 | next_burst_len); | ||
141 | next_burst_len = 0; | ||
142 | |||
143 | datain->flags |= ISCSI_FLAG_CMD_FINAL; | ||
144 | if (conn->sess->sess_ops->ErrorRecoveryLevel > 0) | ||
145 | datain->flags |= ISCSI_FLAG_DATA_ACK; | ||
146 | } | ||
147 | } | ||
148 | |||
149 | datain->data_sn = (!dr->recovery) ? cmd->data_sn++ : dr->data_sn++; | ||
150 | datain->offset = read_data_done; | ||
151 | |||
152 | if (!dr->recovery) { | ||
153 | cmd->next_burst_len = next_burst_len; | ||
154 | cmd->read_data_done += datain->length; | ||
155 | } else { | ||
156 | dr->next_burst_len = next_burst_len; | ||
157 | dr->read_data_done += datain->length; | ||
158 | } | ||
159 | |||
160 | if (!dr->recovery) { | ||
161 | if (datain->flags & ISCSI_FLAG_DATA_STATUS) | ||
162 | dr->dr_complete = DATAIN_COMPLETE_NORMAL; | ||
163 | |||
164 | return dr; | ||
165 | } | ||
166 | |||
167 | if (!dr->runlength) { | ||
168 | if (datain->flags & ISCSI_FLAG_DATA_STATUS) { | ||
169 | dr->dr_complete = | ||
170 | (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ? | ||
171 | DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY : | ||
172 | DATAIN_COMPLETE_CONNECTION_RECOVERY; | ||
173 | } | ||
174 | } else { | ||
175 | if ((dr->begrun + dr->runlength) == dr->data_sn) { | ||
176 | dr->dr_complete = | ||
177 | (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ? | ||
178 | DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY : | ||
179 | DATAIN_COMPLETE_CONNECTION_RECOVERY; | ||
180 | } | ||
181 | } | ||
182 | |||
183 | return dr; | ||
184 | } | ||
185 | |||
186 | /* | ||
187 | * For Normal and Recovery DataSequenceInOrder=No and DataPDUInOrder=Yes. | ||
188 | */ | ||
189 | static struct iscsi_datain_req *iscsit_set_datain_values_no_and_yes( | ||
190 | struct iscsi_cmd *cmd, | ||
191 | struct iscsi_datain *datain) | ||
192 | { | ||
193 | u32 offset, read_data_done, read_data_left, seq_send_order; | ||
194 | struct iscsi_conn *conn = cmd->conn; | ||
195 | struct iscsi_datain_req *dr; | ||
196 | struct iscsi_seq *seq; | ||
197 | |||
198 | dr = iscsit_get_datain_req(cmd); | ||
199 | if (!dr) | ||
200 | return NULL; | ||
201 | |||
202 | if (dr->recovery && dr->generate_recovery_values) { | ||
203 | if (iscsit_create_recovery_datain_values_datasequenceinorder_no( | ||
204 | cmd, dr) < 0) | ||
205 | return NULL; | ||
206 | |||
207 | dr->generate_recovery_values = 0; | ||
208 | } | ||
209 | |||
210 | read_data_done = (!dr->recovery) ? | ||
211 | cmd->read_data_done : dr->read_data_done; | ||
212 | seq_send_order = (!dr->recovery) ? | ||
213 | cmd->seq_send_order : dr->seq_send_order; | ||
214 | |||
215 | read_data_left = (cmd->data_length - read_data_done); | ||
216 | if (!read_data_left) { | ||
217 | pr_err("ITT: 0x%08x read_data_left is zero!\n", | ||
218 | cmd->init_task_tag); | ||
219 | return NULL; | ||
220 | } | ||
221 | |||
222 | seq = iscsit_get_seq_holder_for_datain(cmd, seq_send_order); | ||
223 | if (!seq) | ||
224 | return NULL; | ||
225 | |||
226 | seq->sent = 1; | ||
227 | |||
228 | if (!dr->recovery && !seq->next_burst_len) | ||
229 | seq->first_datasn = cmd->data_sn; | ||
230 | |||
231 | offset = (seq->offset + seq->next_burst_len); | ||
232 | |||
233 | if ((offset + conn->conn_ops->MaxRecvDataSegmentLength) >= | ||
234 | cmd->data_length) { | ||
235 | datain->length = (cmd->data_length - offset); | ||
236 | datain->offset = offset; | ||
237 | |||
238 | datain->flags |= ISCSI_FLAG_CMD_FINAL; | ||
239 | if (conn->sess->sess_ops->ErrorRecoveryLevel > 0) | ||
240 | datain->flags |= ISCSI_FLAG_DATA_ACK; | ||
241 | |||
242 | seq->next_burst_len = 0; | ||
243 | seq_send_order++; | ||
244 | } else { | ||
245 | if ((seq->next_burst_len + | ||
246 | conn->conn_ops->MaxRecvDataSegmentLength) < | ||
247 | conn->sess->sess_ops->MaxBurstLength) { | ||
248 | datain->length = | ||
249 | conn->conn_ops->MaxRecvDataSegmentLength; | ||
250 | datain->offset = (seq->offset + seq->next_burst_len); | ||
251 | |||
252 | seq->next_burst_len += datain->length; | ||
253 | } else { | ||
254 | datain->length = (conn->sess->sess_ops->MaxBurstLength - | ||
255 | seq->next_burst_len); | ||
256 | datain->offset = (seq->offset + seq->next_burst_len); | ||
257 | |||
258 | datain->flags |= ISCSI_FLAG_CMD_FINAL; | ||
259 | if (conn->sess->sess_ops->ErrorRecoveryLevel > 0) | ||
260 | datain->flags |= ISCSI_FLAG_DATA_ACK; | ||
261 | |||
262 | seq->next_burst_len = 0; | ||
263 | seq_send_order++; | ||
264 | } | ||
265 | } | ||
266 | |||
267 | if ((read_data_done + datain->length) == cmd->data_length) | ||
268 | datain->flags |= ISCSI_FLAG_DATA_STATUS; | ||
269 | |||
270 | datain->data_sn = (!dr->recovery) ? cmd->data_sn++ : dr->data_sn++; | ||
271 | if (!dr->recovery) { | ||
272 | cmd->seq_send_order = seq_send_order; | ||
273 | cmd->read_data_done += datain->length; | ||
274 | } else { | ||
275 | dr->seq_send_order = seq_send_order; | ||
276 | dr->read_data_done += datain->length; | ||
277 | } | ||
278 | |||
279 | if (!dr->recovery) { | ||
280 | if (datain->flags & ISCSI_FLAG_CMD_FINAL) | ||
281 | seq->last_datasn = datain->data_sn; | ||
282 | if (datain->flags & ISCSI_FLAG_DATA_STATUS) | ||
283 | dr->dr_complete = DATAIN_COMPLETE_NORMAL; | ||
284 | |||
285 | return dr; | ||
286 | } | ||
287 | |||
288 | if (!dr->runlength) { | ||
289 | if (datain->flags & ISCSI_FLAG_DATA_STATUS) { | ||
290 | dr->dr_complete = | ||
291 | (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ? | ||
292 | DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY : | ||
293 | DATAIN_COMPLETE_CONNECTION_RECOVERY; | ||
294 | } | ||
295 | } else { | ||
296 | if ((dr->begrun + dr->runlength) == dr->data_sn) { | ||
297 | dr->dr_complete = | ||
298 | (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ? | ||
299 | DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY : | ||
300 | DATAIN_COMPLETE_CONNECTION_RECOVERY; | ||
301 | } | ||
302 | } | ||
303 | |||
304 | return dr; | ||
305 | } | ||
306 | |||
307 | /* | ||
308 | * For Normal and Recovery DataSequenceInOrder=Yes and DataPDUInOrder=No. | ||
309 | */ | ||
310 | static struct iscsi_datain_req *iscsit_set_datain_values_yes_and_no( | ||
311 | struct iscsi_cmd *cmd, | ||
312 | struct iscsi_datain *datain) | ||
313 | { | ||
314 | u32 next_burst_len, read_data_done, read_data_left; | ||
315 | struct iscsi_conn *conn = cmd->conn; | ||
316 | struct iscsi_datain_req *dr; | ||
317 | struct iscsi_pdu *pdu; | ||
318 | |||
319 | dr = iscsit_get_datain_req(cmd); | ||
320 | if (!dr) | ||
321 | return NULL; | ||
322 | |||
323 | if (dr->recovery && dr->generate_recovery_values) { | ||
324 | if (iscsit_create_recovery_datain_values_datasequenceinorder_yes( | ||
325 | cmd, dr) < 0) | ||
326 | return NULL; | ||
327 | |||
328 | dr->generate_recovery_values = 0; | ||
329 | } | ||
330 | |||
331 | next_burst_len = (!dr->recovery) ? | ||
332 | cmd->next_burst_len : dr->next_burst_len; | ||
333 | read_data_done = (!dr->recovery) ? | ||
334 | cmd->read_data_done : dr->read_data_done; | ||
335 | |||
336 | read_data_left = (cmd->data_length - read_data_done); | ||
337 | if (!read_data_left) { | ||
338 | pr_err("ITT: 0x%08x read_data_left is zero!\n", | ||
339 | cmd->init_task_tag); | ||
340 | return dr; | ||
341 | } | ||
342 | |||
343 | pdu = iscsit_get_pdu_holder_for_seq(cmd, NULL); | ||
344 | if (!pdu) | ||
345 | return dr; | ||
346 | |||
347 | if ((read_data_done + pdu->length) == cmd->data_length) { | ||
348 | pdu->flags |= (ISCSI_FLAG_CMD_FINAL | ISCSI_FLAG_DATA_STATUS); | ||
349 | if (conn->sess->sess_ops->ErrorRecoveryLevel > 0) | ||
350 | pdu->flags |= ISCSI_FLAG_DATA_ACK; | ||
351 | |||
352 | next_burst_len = 0; | ||
353 | } else { | ||
354 | if ((next_burst_len + conn->conn_ops->MaxRecvDataSegmentLength) < | ||
355 | conn->sess->sess_ops->MaxBurstLength) | ||
356 | next_burst_len += pdu->length; | ||
357 | else { | ||
358 | pdu->flags |= ISCSI_FLAG_CMD_FINAL; | ||
359 | if (conn->sess->sess_ops->ErrorRecoveryLevel > 0) | ||
360 | pdu->flags |= ISCSI_FLAG_DATA_ACK; | ||
361 | |||
362 | next_burst_len = 0; | ||
363 | } | ||
364 | } | ||
365 | |||
366 | pdu->data_sn = (!dr->recovery) ? cmd->data_sn++ : dr->data_sn++; | ||
367 | if (!dr->recovery) { | ||
368 | cmd->next_burst_len = next_burst_len; | ||
369 | cmd->read_data_done += pdu->length; | ||
370 | } else { | ||
371 | dr->next_burst_len = next_burst_len; | ||
372 | dr->read_data_done += pdu->length; | ||
373 | } | ||
374 | |||
375 | datain->flags = pdu->flags; | ||
376 | datain->length = pdu->length; | ||
377 | datain->offset = pdu->offset; | ||
378 | datain->data_sn = pdu->data_sn; | ||
379 | |||
380 | if (!dr->recovery) { | ||
381 | if (datain->flags & ISCSI_FLAG_DATA_STATUS) | ||
382 | dr->dr_complete = DATAIN_COMPLETE_NORMAL; | ||
383 | |||
384 | return dr; | ||
385 | } | ||
386 | |||
387 | if (!dr->runlength) { | ||
388 | if (datain->flags & ISCSI_FLAG_DATA_STATUS) { | ||
389 | dr->dr_complete = | ||
390 | (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ? | ||
391 | DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY : | ||
392 | DATAIN_COMPLETE_CONNECTION_RECOVERY; | ||
393 | } | ||
394 | } else { | ||
395 | if ((dr->begrun + dr->runlength) == dr->data_sn) { | ||
396 | dr->dr_complete = | ||
397 | (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ? | ||
398 | DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY : | ||
399 | DATAIN_COMPLETE_CONNECTION_RECOVERY; | ||
400 | } | ||
401 | } | ||
402 | |||
403 | return dr; | ||
404 | } | ||
405 | |||
406 | /* | ||
407 | * For Normal and Recovery DataSequenceInOrder=No and DataPDUInOrder=No. | ||
408 | */ | ||
409 | static struct iscsi_datain_req *iscsit_set_datain_values_no_and_no( | ||
410 | struct iscsi_cmd *cmd, | ||
411 | struct iscsi_datain *datain) | ||
412 | { | ||
413 | u32 read_data_done, read_data_left, seq_send_order; | ||
414 | struct iscsi_conn *conn = cmd->conn; | ||
415 | struct iscsi_datain_req *dr; | ||
416 | struct iscsi_pdu *pdu; | ||
417 | struct iscsi_seq *seq = NULL; | ||
418 | |||
419 | dr = iscsit_get_datain_req(cmd); | ||
420 | if (!dr) | ||
421 | return NULL; | ||
422 | |||
423 | if (dr->recovery && dr->generate_recovery_values) { | ||
424 | if (iscsit_create_recovery_datain_values_datasequenceinorder_no( | ||
425 | cmd, dr) < 0) | ||
426 | return NULL; | ||
427 | |||
428 | dr->generate_recovery_values = 0; | ||
429 | } | ||
430 | |||
431 | read_data_done = (!dr->recovery) ? | ||
432 | cmd->read_data_done : dr->read_data_done; | ||
433 | seq_send_order = (!dr->recovery) ? | ||
434 | cmd->seq_send_order : dr->seq_send_order; | ||
435 | |||
436 | read_data_left = (cmd->data_length - read_data_done); | ||
437 | if (!read_data_left) { | ||
438 | pr_err("ITT: 0x%08x read_data_left is zero!\n", | ||
439 | cmd->init_task_tag); | ||
440 | return NULL; | ||
441 | } | ||
442 | |||
443 | seq = iscsit_get_seq_holder_for_datain(cmd, seq_send_order); | ||
444 | if (!seq) | ||
445 | return NULL; | ||
446 | |||
447 | seq->sent = 1; | ||
448 | |||
449 | if (!dr->recovery && !seq->next_burst_len) | ||
450 | seq->first_datasn = cmd->data_sn; | ||
451 | |||
452 | pdu = iscsit_get_pdu_holder_for_seq(cmd, seq); | ||
453 | if (!pdu) | ||
454 | return NULL; | ||
455 | |||
456 | if (seq->pdu_send_order == seq->pdu_count) { | ||
457 | pdu->flags |= ISCSI_FLAG_CMD_FINAL; | ||
458 | if (conn->sess->sess_ops->ErrorRecoveryLevel > 0) | ||
459 | pdu->flags |= ISCSI_FLAG_DATA_ACK; | ||
460 | |||
461 | seq->next_burst_len = 0; | ||
462 | seq_send_order++; | ||
463 | } else | ||
464 | seq->next_burst_len += pdu->length; | ||
465 | |||
466 | if ((read_data_done + pdu->length) == cmd->data_length) | ||
467 | pdu->flags |= ISCSI_FLAG_DATA_STATUS; | ||
468 | |||
469 | pdu->data_sn = (!dr->recovery) ? cmd->data_sn++ : dr->data_sn++; | ||
470 | if (!dr->recovery) { | ||
471 | cmd->seq_send_order = seq_send_order; | ||
472 | cmd->read_data_done += pdu->length; | ||
473 | } else { | ||
474 | dr->seq_send_order = seq_send_order; | ||
475 | dr->read_data_done += pdu->length; | ||
476 | } | ||
477 | |||
478 | datain->flags = pdu->flags; | ||
479 | datain->length = pdu->length; | ||
480 | datain->offset = pdu->offset; | ||
481 | datain->data_sn = pdu->data_sn; | ||
482 | |||
483 | if (!dr->recovery) { | ||
484 | if (datain->flags & ISCSI_FLAG_CMD_FINAL) | ||
485 | seq->last_datasn = datain->data_sn; | ||
486 | if (datain->flags & ISCSI_FLAG_DATA_STATUS) | ||
487 | dr->dr_complete = DATAIN_COMPLETE_NORMAL; | ||
488 | |||
489 | return dr; | ||
490 | } | ||
491 | |||
492 | if (!dr->runlength) { | ||
493 | if (datain->flags & ISCSI_FLAG_DATA_STATUS) { | ||
494 | dr->dr_complete = | ||
495 | (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ? | ||
496 | DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY : | ||
497 | DATAIN_COMPLETE_CONNECTION_RECOVERY; | ||
498 | } | ||
499 | } else { | ||
500 | if ((dr->begrun + dr->runlength) == dr->data_sn) { | ||
501 | dr->dr_complete = | ||
502 | (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ? | ||
503 | DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY : | ||
504 | DATAIN_COMPLETE_CONNECTION_RECOVERY; | ||
505 | } | ||
506 | } | ||
507 | |||
508 | return dr; | ||
509 | } | ||
510 | |||
511 | struct iscsi_datain_req *iscsit_get_datain_values( | ||
512 | struct iscsi_cmd *cmd, | ||
513 | struct iscsi_datain *datain) | ||
514 | { | ||
515 | struct iscsi_conn *conn = cmd->conn; | ||
516 | |||
517 | if (conn->sess->sess_ops->DataSequenceInOrder && | ||
518 | conn->sess->sess_ops->DataPDUInOrder) | ||
519 | return iscsit_set_datain_values_yes_and_yes(cmd, datain); | ||
520 | else if (!conn->sess->sess_ops->DataSequenceInOrder && | ||
521 | conn->sess->sess_ops->DataPDUInOrder) | ||
522 | return iscsit_set_datain_values_no_and_yes(cmd, datain); | ||
523 | else if (conn->sess->sess_ops->DataSequenceInOrder && | ||
524 | !conn->sess->sess_ops->DataPDUInOrder) | ||
525 | return iscsit_set_datain_values_yes_and_no(cmd, datain); | ||
526 | else if (!conn->sess->sess_ops->DataSequenceInOrder && | ||
527 | !conn->sess->sess_ops->DataPDUInOrder) | ||
528 | return iscsit_set_datain_values_no_and_no(cmd, datain); | ||
529 | |||
530 | return NULL; | ||
531 | } | ||
diff --git a/drivers/target/iscsi/iscsi_target_datain_values.h b/drivers/target/iscsi/iscsi_target_datain_values.h new file mode 100644 index 000000000000..646429ac5a02 --- /dev/null +++ b/drivers/target/iscsi/iscsi_target_datain_values.h | |||
@@ -0,0 +1,12 @@ | |||
1 | #ifndef ISCSI_TARGET_DATAIN_VALUES_H | ||
2 | #define ISCSI_TARGET_DATAIN_VALUES_H | ||
3 | |||
4 | extern struct iscsi_datain_req *iscsit_allocate_datain_req(void); | ||
5 | extern void iscsit_attach_datain_req(struct iscsi_cmd *, struct iscsi_datain_req *); | ||
6 | extern void iscsit_free_datain_req(struct iscsi_cmd *, struct iscsi_datain_req *); | ||
7 | extern void iscsit_free_all_datain_reqs(struct iscsi_cmd *); | ||
8 | extern struct iscsi_datain_req *iscsit_get_datain_req(struct iscsi_cmd *); | ||
9 | extern struct iscsi_datain_req *iscsit_get_datain_values(struct iscsi_cmd *, | ||
10 | struct iscsi_datain *); | ||
11 | |||
12 | #endif /*** ISCSI_TARGET_DATAIN_VALUES_H ***/ | ||
diff --git a/drivers/target/iscsi/iscsi_target_device.c b/drivers/target/iscsi/iscsi_target_device.c new file mode 100644 index 000000000000..a19fa5eea88e --- /dev/null +++ b/drivers/target/iscsi/iscsi_target_device.c | |||
@@ -0,0 +1,87 @@ | |||
1 | /******************************************************************************* | ||
2 | * This file contains the iSCSI Virtual Device and Disk Transport | ||
3 | * agnostic related functions. | ||
4 | * | ||
5 | \u00a9 Copyright 2007-2011 RisingTide Systems LLC. | ||
6 | * | ||
7 | * Licensed to the Linux Foundation under the General Public License (GPL) version 2. | ||
8 | * | ||
9 | * Author: Nicholas A. Bellinger <nab@linux-iscsi.org> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or | ||
14 | * (at your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | * GNU General Public License for more details. | ||
20 | ******************************************************************************/ | ||
21 | |||
22 | #include <scsi/scsi_device.h> | ||
23 | #include <target/target_core_base.h> | ||
24 | #include <target/target_core_device.h> | ||
25 | #include <target/target_core_transport.h> | ||
26 | |||
27 | #include "iscsi_target_core.h" | ||
28 | #include "iscsi_target_device.h" | ||
29 | #include "iscsi_target_tpg.h" | ||
30 | #include "iscsi_target_util.h" | ||
31 | |||
32 | int iscsit_get_lun_for_tmr( | ||
33 | struct iscsi_cmd *cmd, | ||
34 | u64 lun) | ||
35 | { | ||
36 | u32 unpacked_lun = scsilun_to_int((struct scsi_lun *)&lun); | ||
37 | |||
38 | return transport_lookup_tmr_lun(&cmd->se_cmd, unpacked_lun); | ||
39 | } | ||
40 | |||
41 | int iscsit_get_lun_for_cmd( | ||
42 | struct iscsi_cmd *cmd, | ||
43 | unsigned char *cdb, | ||
44 | u64 lun) | ||
45 | { | ||
46 | u32 unpacked_lun = scsilun_to_int((struct scsi_lun *)&lun); | ||
47 | |||
48 | return transport_lookup_cmd_lun(&cmd->se_cmd, unpacked_lun); | ||
49 | } | ||
50 | |||
51 | void iscsit_determine_maxcmdsn(struct iscsi_session *sess) | ||
52 | { | ||
53 | struct se_node_acl *se_nacl; | ||
54 | |||
55 | /* | ||
56 | * This is a discovery session, the single queue slot was already | ||
57 | * assigned in iscsi_login_zero_tsih(). Since only Logout and | ||
58 | * Text Opcodes are allowed during discovery we do not have to worry | ||
59 | * about the HBA's queue depth here. | ||
60 | */ | ||
61 | if (sess->sess_ops->SessionType) | ||
62 | return; | ||
63 | |||
64 | se_nacl = sess->se_sess->se_node_acl; | ||
65 | |||
66 | /* | ||
67 | * This is a normal session, set the Session's CmdSN window to the | ||
68 | * struct se_node_acl->queue_depth. The value in struct se_node_acl->queue_depth | ||
69 | * has already been validated as a legal value in | ||
70 | * core_set_queue_depth_for_node(). | ||
71 | */ | ||
72 | sess->cmdsn_window = se_nacl->queue_depth; | ||
73 | sess->max_cmd_sn = (sess->max_cmd_sn + se_nacl->queue_depth) - 1; | ||
74 | } | ||
75 | |||
76 | void iscsit_increment_maxcmdsn(struct iscsi_cmd *cmd, struct iscsi_session *sess) | ||
77 | { | ||
78 | if (cmd->immediate_cmd || cmd->maxcmdsn_inc) | ||
79 | return; | ||
80 | |||
81 | cmd->maxcmdsn_inc = 1; | ||
82 | |||
83 | mutex_lock(&sess->cmdsn_mutex); | ||
84 | sess->max_cmd_sn += 1; | ||
85 | pr_debug("Updated MaxCmdSN to 0x%08x\n", sess->max_cmd_sn); | ||
86 | mutex_unlock(&sess->cmdsn_mutex); | ||
87 | } | ||
diff --git a/drivers/target/iscsi/iscsi_target_device.h b/drivers/target/iscsi/iscsi_target_device.h new file mode 100644 index 000000000000..bef1cada15f8 --- /dev/null +++ b/drivers/target/iscsi/iscsi_target_device.h | |||
@@ -0,0 +1,9 @@ | |||
1 | #ifndef ISCSI_TARGET_DEVICE_H | ||
2 | #define ISCSI_TARGET_DEVICE_H | ||
3 | |||
4 | extern int iscsit_get_lun_for_tmr(struct iscsi_cmd *, u64); | ||
5 | extern int iscsit_get_lun_for_cmd(struct iscsi_cmd *, unsigned char *, u64); | ||
6 | extern void iscsit_determine_maxcmdsn(struct iscsi_session *); | ||
7 | extern void iscsit_increment_maxcmdsn(struct iscsi_cmd *, struct iscsi_session *); | ||
8 | |||
9 | #endif /* ISCSI_TARGET_DEVICE_H */ | ||
diff --git a/drivers/target/iscsi/iscsi_target_erl0.c b/drivers/target/iscsi/iscsi_target_erl0.c new file mode 100644 index 000000000000..b7ffc3cd40cc --- /dev/null +++ b/drivers/target/iscsi/iscsi_target_erl0.c | |||
@@ -0,0 +1,1004 @@ | |||
1 | /****************************************************************************** | ||
2 | * This file contains error recovery level zero functions used by | ||
3 | * the iSCSI Target driver. | ||
4 | * | ||
5 | * \u00a9 Copyright 2007-2011 RisingTide Systems LLC. | ||
6 | * | ||
7 | * Licensed to the Linux Foundation under the General Public License (GPL) version 2. | ||
8 | * | ||
9 | * Author: Nicholas A. Bellinger <nab@linux-iscsi.org> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or | ||
14 | * (at your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | * GNU General Public License for more details. | ||
20 | ******************************************************************************/ | ||
21 | |||
22 | #include <scsi/iscsi_proto.h> | ||
23 | #include <target/target_core_base.h> | ||
24 | #include <target/target_core_transport.h> | ||
25 | |||
26 | #include "iscsi_target_core.h" | ||
27 | #include "iscsi_target_seq_pdu_list.h" | ||
28 | #include "iscsi_target_tq.h" | ||
29 | #include "iscsi_target_erl0.h" | ||
30 | #include "iscsi_target_erl1.h" | ||
31 | #include "iscsi_target_erl2.h" | ||
32 | #include "iscsi_target_util.h" | ||
33 | #include "iscsi_target.h" | ||
34 | |||
35 | /* | ||
36 | * Used to set values in struct iscsi_cmd that iscsit_dataout_check_sequence() | ||
37 | * checks against to determine a PDU's Offset+Length is within the current | ||
38 | * DataOUT Sequence. Used for DataSequenceInOrder=Yes only. | ||
39 | */ | ||
40 | void iscsit_set_dataout_sequence_values( | ||
41 | struct iscsi_cmd *cmd) | ||
42 | { | ||
43 | struct iscsi_conn *conn = cmd->conn; | ||
44 | /* | ||
45 | * Still set seq_start_offset and seq_end_offset for Unsolicited | ||
46 | * DataOUT, even if DataSequenceInOrder=No. | ||
47 | */ | ||
48 | if (cmd->unsolicited_data) { | ||
49 | cmd->seq_start_offset = cmd->write_data_done; | ||
50 | cmd->seq_end_offset = (cmd->write_data_done + | ||
51 | (cmd->data_length > | ||
52 | conn->sess->sess_ops->FirstBurstLength) ? | ||
53 | conn->sess->sess_ops->FirstBurstLength : cmd->data_length); | ||
54 | return; | ||
55 | } | ||
56 | |||
57 | if (!conn->sess->sess_ops->DataSequenceInOrder) | ||
58 | return; | ||
59 | |||
60 | if (!cmd->seq_start_offset && !cmd->seq_end_offset) { | ||
61 | cmd->seq_start_offset = cmd->write_data_done; | ||
62 | cmd->seq_end_offset = (cmd->data_length > | ||
63 | conn->sess->sess_ops->MaxBurstLength) ? | ||
64 | (cmd->write_data_done + | ||
65 | conn->sess->sess_ops->MaxBurstLength) : cmd->data_length; | ||
66 | } else { | ||
67 | cmd->seq_start_offset = cmd->seq_end_offset; | ||
68 | cmd->seq_end_offset = ((cmd->seq_end_offset + | ||
69 | conn->sess->sess_ops->MaxBurstLength) >= | ||
70 | cmd->data_length) ? cmd->data_length : | ||
71 | (cmd->seq_end_offset + | ||
72 | conn->sess->sess_ops->MaxBurstLength); | ||
73 | } | ||
74 | } | ||
75 | |||
76 | static int iscsit_dataout_within_command_recovery_check( | ||
77 | struct iscsi_cmd *cmd, | ||
78 | unsigned char *buf) | ||
79 | { | ||
80 | struct iscsi_conn *conn = cmd->conn; | ||
81 | struct iscsi_data *hdr = (struct iscsi_data *) buf; | ||
82 | u32 payload_length = ntoh24(hdr->dlength); | ||
83 | |||
84 | /* | ||
85 | * We do the within-command recovery checks here as it is | ||
86 | * the first function called in iscsi_check_pre_dataout(). | ||
87 | * Basically, if we are in within-command recovery and | ||
88 | * the PDU does not contain the offset the sequence needs, | ||
89 | * dump the payload. | ||
90 | * | ||
91 | * This only applies to DataPDUInOrder=Yes, for | ||
92 | * DataPDUInOrder=No we only re-request the failed PDU | ||
93 | * and check that all PDUs in a sequence are received | ||
94 | * upon end of sequence. | ||
95 | */ | ||
96 | if (conn->sess->sess_ops->DataSequenceInOrder) { | ||
97 | if ((cmd->cmd_flags & ICF_WITHIN_COMMAND_RECOVERY) && | ||
98 | (cmd->write_data_done != hdr->offset)) | ||
99 | goto dump; | ||
100 | |||
101 | cmd->cmd_flags &= ~ICF_WITHIN_COMMAND_RECOVERY; | ||
102 | } else { | ||
103 | struct iscsi_seq *seq; | ||
104 | |||
105 | seq = iscsit_get_seq_holder(cmd, hdr->offset, payload_length); | ||
106 | if (!seq) | ||
107 | return DATAOUT_CANNOT_RECOVER; | ||
108 | /* | ||
109 | * Set the struct iscsi_seq pointer to reuse later. | ||
110 | */ | ||
111 | cmd->seq_ptr = seq; | ||
112 | |||
113 | if (conn->sess->sess_ops->DataPDUInOrder) { | ||
114 | if ((seq->status == | ||
115 | DATAOUT_SEQUENCE_WITHIN_COMMAND_RECOVERY) && | ||
116 | ((seq->offset != hdr->offset) || | ||
117 | (seq->data_sn != hdr->datasn))) | ||
118 | goto dump; | ||
119 | } else { | ||
120 | if ((seq->status == | ||
121 | DATAOUT_SEQUENCE_WITHIN_COMMAND_RECOVERY) && | ||
122 | (seq->data_sn != hdr->datasn)) | ||
123 | goto dump; | ||
124 | } | ||
125 | |||
126 | if (seq->status == DATAOUT_SEQUENCE_COMPLETE) | ||
127 | goto dump; | ||
128 | |||
129 | if (seq->status != DATAOUT_SEQUENCE_COMPLETE) | ||
130 | seq->status = 0; | ||
131 | } | ||
132 | |||
133 | return DATAOUT_NORMAL; | ||
134 | |||
135 | dump: | ||
136 | pr_err("Dumping DataOUT PDU Offset: %u Length: %d DataSN:" | ||
137 | " 0x%08x\n", hdr->offset, payload_length, hdr->datasn); | ||
138 | return iscsit_dump_data_payload(conn, payload_length, 1); | ||
139 | } | ||
140 | |||
141 | static int iscsit_dataout_check_unsolicited_sequence( | ||
142 | struct iscsi_cmd *cmd, | ||
143 | unsigned char *buf) | ||
144 | { | ||
145 | u32 first_burst_len; | ||
146 | struct iscsi_conn *conn = cmd->conn; | ||
147 | struct iscsi_data *hdr = (struct iscsi_data *) buf; | ||
148 | u32 payload_length = ntoh24(hdr->dlength); | ||
149 | |||
150 | |||
151 | if ((hdr->offset < cmd->seq_start_offset) || | ||
152 | ((hdr->offset + payload_length) > cmd->seq_end_offset)) { | ||
153 | pr_err("Command ITT: 0x%08x with Offset: %u," | ||
154 | " Length: %u outside of Unsolicited Sequence %u:%u while" | ||
155 | " DataSequenceInOrder=Yes.\n", cmd->init_task_tag, | ||
156 | hdr->offset, payload_length, cmd->seq_start_offset, | ||
157 | cmd->seq_end_offset); | ||
158 | return DATAOUT_CANNOT_RECOVER; | ||
159 | } | ||
160 | |||
161 | first_burst_len = (cmd->first_burst_len + payload_length); | ||
162 | |||
163 | if (first_burst_len > conn->sess->sess_ops->FirstBurstLength) { | ||
164 | pr_err("Total %u bytes exceeds FirstBurstLength: %u" | ||
165 | " for this Unsolicited DataOut Burst.\n", | ||
166 | first_burst_len, conn->sess->sess_ops->FirstBurstLength); | ||
167 | transport_send_check_condition_and_sense(&cmd->se_cmd, | ||
168 | TCM_INCORRECT_AMOUNT_OF_DATA, 0); | ||
169 | return DATAOUT_CANNOT_RECOVER; | ||
170 | } | ||
171 | |||
172 | /* | ||
173 | * Perform various MaxBurstLength and ISCSI_FLAG_CMD_FINAL sanity | ||
174 | * checks for the current Unsolicited DataOUT Sequence. | ||
175 | */ | ||
176 | if (hdr->flags & ISCSI_FLAG_CMD_FINAL) { | ||
177 | /* | ||
178 | * Ignore ISCSI_FLAG_CMD_FINAL checks while DataPDUInOrder=No, end of | ||
179 | * sequence checks are handled in | ||
180 | * iscsit_dataout_datapduinorder_no_fbit(). | ||
181 | */ | ||
182 | if (!conn->sess->sess_ops->DataPDUInOrder) | ||
183 | goto out; | ||
184 | |||
185 | if ((first_burst_len != cmd->data_length) && | ||
186 | (first_burst_len != conn->sess->sess_ops->FirstBurstLength)) { | ||
187 | pr_err("Unsolicited non-immediate data" | ||
188 | " received %u does not equal FirstBurstLength: %u, and" | ||
189 | " does not equal ExpXferLen %u.\n", first_burst_len, | ||
190 | conn->sess->sess_ops->FirstBurstLength, | ||
191 | cmd->data_length); | ||
192 | transport_send_check_condition_and_sense(&cmd->se_cmd, | ||
193 | TCM_INCORRECT_AMOUNT_OF_DATA, 0); | ||
194 | return DATAOUT_CANNOT_RECOVER; | ||
195 | } | ||
196 | } else { | ||
197 | if (first_burst_len == conn->sess->sess_ops->FirstBurstLength) { | ||
198 | pr_err("Command ITT: 0x%08x reached" | ||
199 | " FirstBurstLength: %u, but ISCSI_FLAG_CMD_FINAL is not set. protocol" | ||
200 | " error.\n", cmd->init_task_tag, | ||
201 | conn->sess->sess_ops->FirstBurstLength); | ||
202 | return DATAOUT_CANNOT_RECOVER; | ||
203 | } | ||
204 | if (first_burst_len == cmd->data_length) { | ||
205 | pr_err("Command ITT: 0x%08x reached" | ||
206 | " ExpXferLen: %u, but ISCSI_FLAG_CMD_FINAL is not set. protocol" | ||
207 | " error.\n", cmd->init_task_tag, cmd->data_length); | ||
208 | return DATAOUT_CANNOT_RECOVER; | ||
209 | } | ||
210 | } | ||
211 | |||
212 | out: | ||
213 | return DATAOUT_NORMAL; | ||
214 | } | ||
215 | |||
216 | static int iscsit_dataout_check_sequence( | ||
217 | struct iscsi_cmd *cmd, | ||
218 | unsigned char *buf) | ||
219 | { | ||
220 | u32 next_burst_len; | ||
221 | struct iscsi_conn *conn = cmd->conn; | ||
222 | struct iscsi_seq *seq = NULL; | ||
223 | struct iscsi_data *hdr = (struct iscsi_data *) buf; | ||
224 | u32 payload_length = ntoh24(hdr->dlength); | ||
225 | |||
226 | /* | ||
227 | * For DataSequenceInOrder=Yes: Check that the offset and offset+length | ||
228 | * is within range as defined by iscsi_set_dataout_sequence_values(). | ||
229 | * | ||
230 | * For DataSequenceInOrder=No: Check that an struct iscsi_seq exists for | ||
231 | * offset+length tuple. | ||
232 | */ | ||
233 | if (conn->sess->sess_ops->DataSequenceInOrder) { | ||
234 | /* | ||
235 | * Due to possibility of recovery DataOUT sent by the initiator | ||
236 | * fullfilling an Recovery R2T, it's best to just dump the | ||
237 | * payload here, instead of erroring out. | ||
238 | */ | ||
239 | if ((hdr->offset < cmd->seq_start_offset) || | ||
240 | ((hdr->offset + payload_length) > cmd->seq_end_offset)) { | ||
241 | pr_err("Command ITT: 0x%08x with Offset: %u," | ||
242 | " Length: %u outside of Sequence %u:%u while" | ||
243 | " DataSequenceInOrder=Yes.\n", cmd->init_task_tag, | ||
244 | hdr->offset, payload_length, cmd->seq_start_offset, | ||
245 | cmd->seq_end_offset); | ||
246 | |||
247 | if (iscsit_dump_data_payload(conn, payload_length, 1) < 0) | ||
248 | return DATAOUT_CANNOT_RECOVER; | ||
249 | return DATAOUT_WITHIN_COMMAND_RECOVERY; | ||
250 | } | ||
251 | |||
252 | next_burst_len = (cmd->next_burst_len + payload_length); | ||
253 | } else { | ||
254 | seq = iscsit_get_seq_holder(cmd, hdr->offset, payload_length); | ||
255 | if (!seq) | ||
256 | return DATAOUT_CANNOT_RECOVER; | ||
257 | /* | ||
258 | * Set the struct iscsi_seq pointer to reuse later. | ||
259 | */ | ||
260 | cmd->seq_ptr = seq; | ||
261 | |||
262 | if (seq->status == DATAOUT_SEQUENCE_COMPLETE) { | ||
263 | if (iscsit_dump_data_payload(conn, payload_length, 1) < 0) | ||
264 | return DATAOUT_CANNOT_RECOVER; | ||
265 | return DATAOUT_WITHIN_COMMAND_RECOVERY; | ||
266 | } | ||
267 | |||
268 | next_burst_len = (seq->next_burst_len + payload_length); | ||
269 | } | ||
270 | |||
271 | if (next_burst_len > conn->sess->sess_ops->MaxBurstLength) { | ||
272 | pr_err("Command ITT: 0x%08x, NextBurstLength: %u and" | ||
273 | " Length: %u exceeds MaxBurstLength: %u. protocol" | ||
274 | " error.\n", cmd->init_task_tag, | ||
275 | (next_burst_len - payload_length), | ||
276 | payload_length, conn->sess->sess_ops->MaxBurstLength); | ||
277 | return DATAOUT_CANNOT_RECOVER; | ||
278 | } | ||
279 | |||
280 | /* | ||
281 | * Perform various MaxBurstLength and ISCSI_FLAG_CMD_FINAL sanity | ||
282 | * checks for the current DataOUT Sequence. | ||
283 | */ | ||
284 | if (hdr->flags & ISCSI_FLAG_CMD_FINAL) { | ||
285 | /* | ||
286 | * Ignore ISCSI_FLAG_CMD_FINAL checks while DataPDUInOrder=No, end of | ||
287 | * sequence checks are handled in | ||
288 | * iscsit_dataout_datapduinorder_no_fbit(). | ||
289 | */ | ||
290 | if (!conn->sess->sess_ops->DataPDUInOrder) | ||
291 | goto out; | ||
292 | |||
293 | if (conn->sess->sess_ops->DataSequenceInOrder) { | ||
294 | if ((next_burst_len < | ||
295 | conn->sess->sess_ops->MaxBurstLength) && | ||
296 | ((cmd->write_data_done + payload_length) < | ||
297 | cmd->data_length)) { | ||
298 | pr_err("Command ITT: 0x%08x set ISCSI_FLAG_CMD_FINAL" | ||
299 | " before end of DataOUT sequence, protocol" | ||
300 | " error.\n", cmd->init_task_tag); | ||
301 | return DATAOUT_CANNOT_RECOVER; | ||
302 | } | ||
303 | } else { | ||
304 | if (next_burst_len < seq->xfer_len) { | ||
305 | pr_err("Command ITT: 0x%08x set ISCSI_FLAG_CMD_FINAL" | ||
306 | " before end of DataOUT sequence, protocol" | ||
307 | " error.\n", cmd->init_task_tag); | ||
308 | return DATAOUT_CANNOT_RECOVER; | ||
309 | } | ||
310 | } | ||
311 | } else { | ||
312 | if (conn->sess->sess_ops->DataSequenceInOrder) { | ||
313 | if (next_burst_len == | ||
314 | conn->sess->sess_ops->MaxBurstLength) { | ||
315 | pr_err("Command ITT: 0x%08x reached" | ||
316 | " MaxBurstLength: %u, but ISCSI_FLAG_CMD_FINAL is" | ||
317 | " not set, protocol error.", cmd->init_task_tag, | ||
318 | conn->sess->sess_ops->MaxBurstLength); | ||
319 | return DATAOUT_CANNOT_RECOVER; | ||
320 | } | ||
321 | if ((cmd->write_data_done + payload_length) == | ||
322 | cmd->data_length) { | ||
323 | pr_err("Command ITT: 0x%08x reached" | ||
324 | " last DataOUT PDU in sequence but ISCSI_FLAG_" | ||
325 | "CMD_FINAL is not set, protocol error.\n", | ||
326 | cmd->init_task_tag); | ||
327 | return DATAOUT_CANNOT_RECOVER; | ||
328 | } | ||
329 | } else { | ||
330 | if (next_burst_len == seq->xfer_len) { | ||
331 | pr_err("Command ITT: 0x%08x reached" | ||
332 | " last DataOUT PDU in sequence but ISCSI_FLAG_" | ||
333 | "CMD_FINAL is not set, protocol error.\n", | ||
334 | cmd->init_task_tag); | ||
335 | return DATAOUT_CANNOT_RECOVER; | ||
336 | } | ||
337 | } | ||
338 | } | ||
339 | |||
340 | out: | ||
341 | return DATAOUT_NORMAL; | ||
342 | } | ||
343 | |||
344 | static int iscsit_dataout_check_datasn( | ||
345 | struct iscsi_cmd *cmd, | ||
346 | unsigned char *buf) | ||
347 | { | ||
348 | int dump = 0, recovery = 0; | ||
349 | u32 data_sn = 0; | ||
350 | struct iscsi_conn *conn = cmd->conn; | ||
351 | struct iscsi_data *hdr = (struct iscsi_data *) buf; | ||
352 | u32 payload_length = ntoh24(hdr->dlength); | ||
353 | |||
354 | /* | ||
355 | * Considering the target has no method of re-requesting DataOUT | ||
356 | * by DataSN, if we receieve a greater DataSN than expected we | ||
357 | * assume the functions for DataPDUInOrder=[Yes,No] below will | ||
358 | * handle it. | ||
359 | * | ||
360 | * If the DataSN is less than expected, dump the payload. | ||
361 | */ | ||
362 | if (conn->sess->sess_ops->DataSequenceInOrder) | ||
363 | data_sn = cmd->data_sn; | ||
364 | else { | ||
365 | struct iscsi_seq *seq = cmd->seq_ptr; | ||
366 | data_sn = seq->data_sn; | ||
367 | } | ||
368 | |||
369 | if (hdr->datasn > data_sn) { | ||
370 | pr_err("Command ITT: 0x%08x, received DataSN: 0x%08x" | ||
371 | " higher than expected 0x%08x.\n", cmd->init_task_tag, | ||
372 | hdr->datasn, data_sn); | ||
373 | recovery = 1; | ||
374 | goto recover; | ||
375 | } else if (hdr->datasn < data_sn) { | ||
376 | pr_err("Command ITT: 0x%08x, received DataSN: 0x%08x" | ||
377 | " lower than expected 0x%08x, discarding payload.\n", | ||
378 | cmd->init_task_tag, hdr->datasn, data_sn); | ||
379 | dump = 1; | ||
380 | goto dump; | ||
381 | } | ||
382 | |||
383 | return DATAOUT_NORMAL; | ||
384 | |||
385 | recover: | ||
386 | if (!conn->sess->sess_ops->ErrorRecoveryLevel) { | ||
387 | pr_err("Unable to perform within-command recovery" | ||
388 | " while ERL=0.\n"); | ||
389 | return DATAOUT_CANNOT_RECOVER; | ||
390 | } | ||
391 | dump: | ||
392 | if (iscsit_dump_data_payload(conn, payload_length, 1) < 0) | ||
393 | return DATAOUT_CANNOT_RECOVER; | ||
394 | |||
395 | return (recovery || dump) ? DATAOUT_WITHIN_COMMAND_RECOVERY : | ||
396 | DATAOUT_NORMAL; | ||
397 | } | ||
398 | |||
399 | static int iscsit_dataout_pre_datapduinorder_yes( | ||
400 | struct iscsi_cmd *cmd, | ||
401 | unsigned char *buf) | ||
402 | { | ||
403 | int dump = 0, recovery = 0; | ||
404 | struct iscsi_conn *conn = cmd->conn; | ||
405 | struct iscsi_data *hdr = (struct iscsi_data *) buf; | ||
406 | u32 payload_length = ntoh24(hdr->dlength); | ||
407 | |||
408 | /* | ||
409 | * For DataSequenceInOrder=Yes: If the offset is greater than the global | ||
410 | * DataPDUInOrder=Yes offset counter in struct iscsi_cmd a protcol error has | ||
411 | * occured and fail the connection. | ||
412 | * | ||
413 | * For DataSequenceInOrder=No: If the offset is greater than the per | ||
414 | * sequence DataPDUInOrder=Yes offset counter in struct iscsi_seq a protocol | ||
415 | * error has occured and fail the connection. | ||
416 | */ | ||
417 | if (conn->sess->sess_ops->DataSequenceInOrder) { | ||
418 | if (hdr->offset != cmd->write_data_done) { | ||
419 | pr_err("Command ITT: 0x%08x, received offset" | ||
420 | " %u different than expected %u.\n", cmd->init_task_tag, | ||
421 | hdr->offset, cmd->write_data_done); | ||
422 | recovery = 1; | ||
423 | goto recover; | ||
424 | } | ||
425 | } else { | ||
426 | struct iscsi_seq *seq = cmd->seq_ptr; | ||
427 | |||
428 | if (hdr->offset > seq->offset) { | ||
429 | pr_err("Command ITT: 0x%08x, received offset" | ||
430 | " %u greater than expected %u.\n", cmd->init_task_tag, | ||
431 | hdr->offset, seq->offset); | ||
432 | recovery = 1; | ||
433 | goto recover; | ||
434 | } else if (hdr->offset < seq->offset) { | ||
435 | pr_err("Command ITT: 0x%08x, received offset" | ||
436 | " %u less than expected %u, discarding payload.\n", | ||
437 | cmd->init_task_tag, hdr->offset, seq->offset); | ||
438 | dump = 1; | ||
439 | goto dump; | ||
440 | } | ||
441 | } | ||
442 | |||
443 | return DATAOUT_NORMAL; | ||
444 | |||
445 | recover: | ||
446 | if (!conn->sess->sess_ops->ErrorRecoveryLevel) { | ||
447 | pr_err("Unable to perform within-command recovery" | ||
448 | " while ERL=0.\n"); | ||
449 | return DATAOUT_CANNOT_RECOVER; | ||
450 | } | ||
451 | dump: | ||
452 | if (iscsit_dump_data_payload(conn, payload_length, 1) < 0) | ||
453 | return DATAOUT_CANNOT_RECOVER; | ||
454 | |||
455 | return (recovery) ? iscsit_recover_dataout_sequence(cmd, | ||
456 | hdr->offset, payload_length) : | ||
457 | (dump) ? DATAOUT_WITHIN_COMMAND_RECOVERY : DATAOUT_NORMAL; | ||
458 | } | ||
459 | |||
460 | static int iscsit_dataout_pre_datapduinorder_no( | ||
461 | struct iscsi_cmd *cmd, | ||
462 | unsigned char *buf) | ||
463 | { | ||
464 | struct iscsi_pdu *pdu; | ||
465 | struct iscsi_data *hdr = (struct iscsi_data *) buf; | ||
466 | u32 payload_length = ntoh24(hdr->dlength); | ||
467 | |||
468 | pdu = iscsit_get_pdu_holder(cmd, hdr->offset, payload_length); | ||
469 | if (!pdu) | ||
470 | return DATAOUT_CANNOT_RECOVER; | ||
471 | |||
472 | cmd->pdu_ptr = pdu; | ||
473 | |||
474 | switch (pdu->status) { | ||
475 | case ISCSI_PDU_NOT_RECEIVED: | ||
476 | case ISCSI_PDU_CRC_FAILED: | ||
477 | case ISCSI_PDU_TIMED_OUT: | ||
478 | break; | ||
479 | case ISCSI_PDU_RECEIVED_OK: | ||
480 | pr_err("Command ITT: 0x%08x received already gotten" | ||
481 | " Offset: %u, Length: %u\n", cmd->init_task_tag, | ||
482 | hdr->offset, payload_length); | ||
483 | return iscsit_dump_data_payload(cmd->conn, payload_length, 1); | ||
484 | default: | ||
485 | return DATAOUT_CANNOT_RECOVER; | ||
486 | } | ||
487 | |||
488 | return DATAOUT_NORMAL; | ||
489 | } | ||
490 | |||
491 | static int iscsit_dataout_update_r2t(struct iscsi_cmd *cmd, u32 offset, u32 length) | ||
492 | { | ||
493 | struct iscsi_r2t *r2t; | ||
494 | |||
495 | if (cmd->unsolicited_data) | ||
496 | return 0; | ||
497 | |||
498 | r2t = iscsit_get_r2t_for_eos(cmd, offset, length); | ||
499 | if (!r2t) | ||
500 | return -1; | ||
501 | |||
502 | spin_lock_bh(&cmd->r2t_lock); | ||
503 | r2t->seq_complete = 1; | ||
504 | cmd->outstanding_r2ts--; | ||
505 | spin_unlock_bh(&cmd->r2t_lock); | ||
506 | |||
507 | return 0; | ||
508 | } | ||
509 | |||
510 | static int iscsit_dataout_update_datapduinorder_no( | ||
511 | struct iscsi_cmd *cmd, | ||
512 | u32 data_sn, | ||
513 | int f_bit) | ||
514 | { | ||
515 | int ret = 0; | ||
516 | struct iscsi_pdu *pdu = cmd->pdu_ptr; | ||
517 | |||
518 | pdu->data_sn = data_sn; | ||
519 | |||
520 | switch (pdu->status) { | ||
521 | case ISCSI_PDU_NOT_RECEIVED: | ||
522 | pdu->status = ISCSI_PDU_RECEIVED_OK; | ||
523 | break; | ||
524 | case ISCSI_PDU_CRC_FAILED: | ||
525 | pdu->status = ISCSI_PDU_RECEIVED_OK; | ||
526 | break; | ||
527 | case ISCSI_PDU_TIMED_OUT: | ||
528 | pdu->status = ISCSI_PDU_RECEIVED_OK; | ||
529 | break; | ||
530 | default: | ||
531 | return DATAOUT_CANNOT_RECOVER; | ||
532 | } | ||
533 | |||
534 | if (f_bit) { | ||
535 | ret = iscsit_dataout_datapduinorder_no_fbit(cmd, pdu); | ||
536 | if (ret == DATAOUT_CANNOT_RECOVER) | ||
537 | return ret; | ||
538 | } | ||
539 | |||
540 | return DATAOUT_NORMAL; | ||
541 | } | ||
542 | |||
543 | static int iscsit_dataout_post_crc_passed( | ||
544 | struct iscsi_cmd *cmd, | ||
545 | unsigned char *buf) | ||
546 | { | ||
547 | int ret, send_r2t = 0; | ||
548 | struct iscsi_conn *conn = cmd->conn; | ||
549 | struct iscsi_seq *seq = NULL; | ||
550 | struct iscsi_data *hdr = (struct iscsi_data *) buf; | ||
551 | u32 payload_length = ntoh24(hdr->dlength); | ||
552 | |||
553 | if (cmd->unsolicited_data) { | ||
554 | if ((cmd->first_burst_len + payload_length) == | ||
555 | conn->sess->sess_ops->FirstBurstLength) { | ||
556 | if (iscsit_dataout_update_r2t(cmd, hdr->offset, | ||
557 | payload_length) < 0) | ||
558 | return DATAOUT_CANNOT_RECOVER; | ||
559 | send_r2t = 1; | ||
560 | } | ||
561 | |||
562 | if (!conn->sess->sess_ops->DataPDUInOrder) { | ||
563 | ret = iscsit_dataout_update_datapduinorder_no(cmd, | ||
564 | hdr->datasn, (hdr->flags & ISCSI_FLAG_CMD_FINAL)); | ||
565 | if (ret == DATAOUT_CANNOT_RECOVER) | ||
566 | return ret; | ||
567 | } | ||
568 | |||
569 | cmd->first_burst_len += payload_length; | ||
570 | |||
571 | if (conn->sess->sess_ops->DataSequenceInOrder) | ||
572 | cmd->data_sn++; | ||
573 | else { | ||
574 | seq = cmd->seq_ptr; | ||
575 | seq->data_sn++; | ||
576 | seq->offset += payload_length; | ||
577 | } | ||
578 | |||
579 | if (send_r2t) { | ||
580 | if (seq) | ||
581 | seq->status = DATAOUT_SEQUENCE_COMPLETE; | ||
582 | cmd->first_burst_len = 0; | ||
583 | cmd->unsolicited_data = 0; | ||
584 | } | ||
585 | } else { | ||
586 | if (conn->sess->sess_ops->DataSequenceInOrder) { | ||
587 | if ((cmd->next_burst_len + payload_length) == | ||
588 | conn->sess->sess_ops->MaxBurstLength) { | ||
589 | if (iscsit_dataout_update_r2t(cmd, hdr->offset, | ||
590 | payload_length) < 0) | ||
591 | return DATAOUT_CANNOT_RECOVER; | ||
592 | send_r2t = 1; | ||
593 | } | ||
594 | |||
595 | if (!conn->sess->sess_ops->DataPDUInOrder) { | ||
596 | ret = iscsit_dataout_update_datapduinorder_no( | ||
597 | cmd, hdr->datasn, | ||
598 | (hdr->flags & ISCSI_FLAG_CMD_FINAL)); | ||
599 | if (ret == DATAOUT_CANNOT_RECOVER) | ||
600 | return ret; | ||
601 | } | ||
602 | |||
603 | cmd->next_burst_len += payload_length; | ||
604 | cmd->data_sn++; | ||
605 | |||
606 | if (send_r2t) | ||
607 | cmd->next_burst_len = 0; | ||
608 | } else { | ||
609 | seq = cmd->seq_ptr; | ||
610 | |||
611 | if ((seq->next_burst_len + payload_length) == | ||
612 | seq->xfer_len) { | ||
613 | if (iscsit_dataout_update_r2t(cmd, hdr->offset, | ||
614 | payload_length) < 0) | ||
615 | return DATAOUT_CANNOT_RECOVER; | ||
616 | send_r2t = 1; | ||
617 | } | ||
618 | |||
619 | if (!conn->sess->sess_ops->DataPDUInOrder) { | ||
620 | ret = iscsit_dataout_update_datapduinorder_no( | ||
621 | cmd, hdr->datasn, | ||
622 | (hdr->flags & ISCSI_FLAG_CMD_FINAL)); | ||
623 | if (ret == DATAOUT_CANNOT_RECOVER) | ||
624 | return ret; | ||
625 | } | ||
626 | |||
627 | seq->data_sn++; | ||
628 | seq->offset += payload_length; | ||
629 | seq->next_burst_len += payload_length; | ||
630 | |||
631 | if (send_r2t) { | ||
632 | seq->next_burst_len = 0; | ||
633 | seq->status = DATAOUT_SEQUENCE_COMPLETE; | ||
634 | } | ||
635 | } | ||
636 | } | ||
637 | |||
638 | if (send_r2t && conn->sess->sess_ops->DataSequenceInOrder) | ||
639 | cmd->data_sn = 0; | ||
640 | |||
641 | cmd->write_data_done += payload_length; | ||
642 | |||
643 | return (cmd->write_data_done == cmd->data_length) ? | ||
644 | DATAOUT_SEND_TO_TRANSPORT : (send_r2t) ? | ||
645 | DATAOUT_SEND_R2T : DATAOUT_NORMAL; | ||
646 | } | ||
647 | |||
648 | static int iscsit_dataout_post_crc_failed( | ||
649 | struct iscsi_cmd *cmd, | ||
650 | unsigned char *buf) | ||
651 | { | ||
652 | struct iscsi_conn *conn = cmd->conn; | ||
653 | struct iscsi_pdu *pdu; | ||
654 | struct iscsi_data *hdr = (struct iscsi_data *) buf; | ||
655 | u32 payload_length = ntoh24(hdr->dlength); | ||
656 | |||
657 | if (conn->sess->sess_ops->DataPDUInOrder) | ||
658 | goto recover; | ||
659 | /* | ||
660 | * The rest of this function is only called when DataPDUInOrder=No. | ||
661 | */ | ||
662 | pdu = cmd->pdu_ptr; | ||
663 | |||
664 | switch (pdu->status) { | ||
665 | case ISCSI_PDU_NOT_RECEIVED: | ||
666 | pdu->status = ISCSI_PDU_CRC_FAILED; | ||
667 | break; | ||
668 | case ISCSI_PDU_CRC_FAILED: | ||
669 | break; | ||
670 | case ISCSI_PDU_TIMED_OUT: | ||
671 | pdu->status = ISCSI_PDU_CRC_FAILED; | ||
672 | break; | ||
673 | default: | ||
674 | return DATAOUT_CANNOT_RECOVER; | ||
675 | } | ||
676 | |||
677 | recover: | ||
678 | return iscsit_recover_dataout_sequence(cmd, hdr->offset, payload_length); | ||
679 | } | ||
680 | |||
681 | /* | ||
682 | * Called from iscsit_handle_data_out() before DataOUT Payload is received | ||
683 | * and CRC computed. | ||
684 | */ | ||
685 | extern int iscsit_check_pre_dataout( | ||
686 | struct iscsi_cmd *cmd, | ||
687 | unsigned char *buf) | ||
688 | { | ||
689 | int ret; | ||
690 | struct iscsi_conn *conn = cmd->conn; | ||
691 | |||
692 | ret = iscsit_dataout_within_command_recovery_check(cmd, buf); | ||
693 | if ((ret == DATAOUT_WITHIN_COMMAND_RECOVERY) || | ||
694 | (ret == DATAOUT_CANNOT_RECOVER)) | ||
695 | return ret; | ||
696 | |||
697 | ret = iscsit_dataout_check_datasn(cmd, buf); | ||
698 | if ((ret == DATAOUT_WITHIN_COMMAND_RECOVERY) || | ||
699 | (ret == DATAOUT_CANNOT_RECOVER)) | ||
700 | return ret; | ||
701 | |||
702 | if (cmd->unsolicited_data) { | ||
703 | ret = iscsit_dataout_check_unsolicited_sequence(cmd, buf); | ||
704 | if ((ret == DATAOUT_WITHIN_COMMAND_RECOVERY) || | ||
705 | (ret == DATAOUT_CANNOT_RECOVER)) | ||
706 | return ret; | ||
707 | } else { | ||
708 | ret = iscsit_dataout_check_sequence(cmd, buf); | ||
709 | if ((ret == DATAOUT_WITHIN_COMMAND_RECOVERY) || | ||
710 | (ret == DATAOUT_CANNOT_RECOVER)) | ||
711 | return ret; | ||
712 | } | ||
713 | |||
714 | return (conn->sess->sess_ops->DataPDUInOrder) ? | ||
715 | iscsit_dataout_pre_datapduinorder_yes(cmd, buf) : | ||
716 | iscsit_dataout_pre_datapduinorder_no(cmd, buf); | ||
717 | } | ||
718 | |||
719 | /* | ||
720 | * Called from iscsit_handle_data_out() after DataOUT Payload is received | ||
721 | * and CRC computed. | ||
722 | */ | ||
723 | int iscsit_check_post_dataout( | ||
724 | struct iscsi_cmd *cmd, | ||
725 | unsigned char *buf, | ||
726 | u8 data_crc_failed) | ||
727 | { | ||
728 | struct iscsi_conn *conn = cmd->conn; | ||
729 | |||
730 | cmd->dataout_timeout_retries = 0; | ||
731 | |||
732 | if (!data_crc_failed) | ||
733 | return iscsit_dataout_post_crc_passed(cmd, buf); | ||
734 | else { | ||
735 | if (!conn->sess->sess_ops->ErrorRecoveryLevel) { | ||
736 | pr_err("Unable to recover from DataOUT CRC" | ||
737 | " failure while ERL=0, closing session.\n"); | ||
738 | iscsit_add_reject_from_cmd(ISCSI_REASON_DATA_DIGEST_ERROR, | ||
739 | 1, 0, buf, cmd); | ||
740 | return DATAOUT_CANNOT_RECOVER; | ||
741 | } | ||
742 | |||
743 | iscsit_add_reject_from_cmd(ISCSI_REASON_DATA_DIGEST_ERROR, | ||
744 | 0, 0, buf, cmd); | ||
745 | return iscsit_dataout_post_crc_failed(cmd, buf); | ||
746 | } | ||
747 | } | ||
748 | |||
749 | static void iscsit_handle_time2retain_timeout(unsigned long data) | ||
750 | { | ||
751 | struct iscsi_session *sess = (struct iscsi_session *) data; | ||
752 | struct iscsi_portal_group *tpg = ISCSI_TPG_S(sess); | ||
753 | struct se_portal_group *se_tpg = &tpg->tpg_se_tpg; | ||
754 | |||
755 | spin_lock_bh(&se_tpg->session_lock); | ||
756 | if (sess->time2retain_timer_flags & ISCSI_TF_STOP) { | ||
757 | spin_unlock_bh(&se_tpg->session_lock); | ||
758 | return; | ||
759 | } | ||
760 | if (atomic_read(&sess->session_reinstatement)) { | ||
761 | pr_err("Exiting Time2Retain handler because" | ||
762 | " session_reinstatement=1\n"); | ||
763 | spin_unlock_bh(&se_tpg->session_lock); | ||
764 | return; | ||
765 | } | ||
766 | sess->time2retain_timer_flags |= ISCSI_TF_EXPIRED; | ||
767 | |||
768 | pr_err("Time2Retain timer expired for SID: %u, cleaning up" | ||
769 | " iSCSI session.\n", sess->sid); | ||
770 | { | ||
771 | struct iscsi_tiqn *tiqn = tpg->tpg_tiqn; | ||
772 | |||
773 | if (tiqn) { | ||
774 | spin_lock(&tiqn->sess_err_stats.lock); | ||
775 | strcpy(tiqn->sess_err_stats.last_sess_fail_rem_name, | ||
776 | (void *)sess->sess_ops->InitiatorName); | ||
777 | tiqn->sess_err_stats.last_sess_failure_type = | ||
778 | ISCSI_SESS_ERR_CXN_TIMEOUT; | ||
779 | tiqn->sess_err_stats.cxn_timeout_errors++; | ||
780 | sess->conn_timeout_errors++; | ||
781 | spin_unlock(&tiqn->sess_err_stats.lock); | ||
782 | } | ||
783 | } | ||
784 | |||
785 | spin_unlock_bh(&se_tpg->session_lock); | ||
786 | iscsit_close_session(sess); | ||
787 | } | ||
788 | |||
789 | extern void iscsit_start_time2retain_handler(struct iscsi_session *sess) | ||
790 | { | ||
791 | int tpg_active; | ||
792 | /* | ||
793 | * Only start Time2Retain timer when the assoicated TPG is still in | ||
794 | * an ACTIVE (eg: not disabled or shutdown) state. | ||
795 | */ | ||
796 | spin_lock(&ISCSI_TPG_S(sess)->tpg_state_lock); | ||
797 | tpg_active = (ISCSI_TPG_S(sess)->tpg_state == TPG_STATE_ACTIVE); | ||
798 | spin_unlock(&ISCSI_TPG_S(sess)->tpg_state_lock); | ||
799 | |||
800 | if (!tpg_active) | ||
801 | return; | ||
802 | |||
803 | if (sess->time2retain_timer_flags & ISCSI_TF_RUNNING) | ||
804 | return; | ||
805 | |||
806 | pr_debug("Starting Time2Retain timer for %u seconds on" | ||
807 | " SID: %u\n", sess->sess_ops->DefaultTime2Retain, sess->sid); | ||
808 | |||
809 | init_timer(&sess->time2retain_timer); | ||
810 | sess->time2retain_timer.expires = | ||
811 | (get_jiffies_64() + sess->sess_ops->DefaultTime2Retain * HZ); | ||
812 | sess->time2retain_timer.data = (unsigned long)sess; | ||
813 | sess->time2retain_timer.function = iscsit_handle_time2retain_timeout; | ||
814 | sess->time2retain_timer_flags &= ~ISCSI_TF_STOP; | ||
815 | sess->time2retain_timer_flags |= ISCSI_TF_RUNNING; | ||
816 | add_timer(&sess->time2retain_timer); | ||
817 | } | ||
818 | |||
819 | /* | ||
820 | * Called with spin_lock_bh(&struct se_portal_group->session_lock) held | ||
821 | */ | ||
822 | extern int iscsit_stop_time2retain_timer(struct iscsi_session *sess) | ||
823 | { | ||
824 | struct iscsi_portal_group *tpg = ISCSI_TPG_S(sess); | ||
825 | struct se_portal_group *se_tpg = &tpg->tpg_se_tpg; | ||
826 | |||
827 | if (sess->time2retain_timer_flags & ISCSI_TF_EXPIRED) | ||
828 | return -1; | ||
829 | |||
830 | if (!(sess->time2retain_timer_flags & ISCSI_TF_RUNNING)) | ||
831 | return 0; | ||
832 | |||
833 | sess->time2retain_timer_flags |= ISCSI_TF_STOP; | ||
834 | spin_unlock_bh(&se_tpg->session_lock); | ||
835 | |||
836 | del_timer_sync(&sess->time2retain_timer); | ||
837 | |||
838 | spin_lock_bh(&se_tpg->session_lock); | ||
839 | sess->time2retain_timer_flags &= ~ISCSI_TF_RUNNING; | ||
840 | pr_debug("Stopped Time2Retain Timer for SID: %u\n", | ||
841 | sess->sid); | ||
842 | return 0; | ||
843 | } | ||
844 | |||
845 | void iscsit_connection_reinstatement_rcfr(struct iscsi_conn *conn) | ||
846 | { | ||
847 | spin_lock_bh(&conn->state_lock); | ||
848 | if (atomic_read(&conn->connection_exit)) { | ||
849 | spin_unlock_bh(&conn->state_lock); | ||
850 | goto sleep; | ||
851 | } | ||
852 | |||
853 | if (atomic_read(&conn->transport_failed)) { | ||
854 | spin_unlock_bh(&conn->state_lock); | ||
855 | goto sleep; | ||
856 | } | ||
857 | spin_unlock_bh(&conn->state_lock); | ||
858 | |||
859 | iscsi_thread_set_force_reinstatement(conn); | ||
860 | |||
861 | sleep: | ||
862 | wait_for_completion(&conn->conn_wait_rcfr_comp); | ||
863 | complete(&conn->conn_post_wait_comp); | ||
864 | } | ||
865 | |||
866 | void iscsit_cause_connection_reinstatement(struct iscsi_conn *conn, int sleep) | ||
867 | { | ||
868 | spin_lock_bh(&conn->state_lock); | ||
869 | if (atomic_read(&conn->connection_exit)) { | ||
870 | spin_unlock_bh(&conn->state_lock); | ||
871 | return; | ||
872 | } | ||
873 | |||
874 | if (atomic_read(&conn->transport_failed)) { | ||
875 | spin_unlock_bh(&conn->state_lock); | ||
876 | return; | ||
877 | } | ||
878 | |||
879 | if (atomic_read(&conn->connection_reinstatement)) { | ||
880 | spin_unlock_bh(&conn->state_lock); | ||
881 | return; | ||
882 | } | ||
883 | |||
884 | if (iscsi_thread_set_force_reinstatement(conn) < 0) { | ||
885 | spin_unlock_bh(&conn->state_lock); | ||
886 | return; | ||
887 | } | ||
888 | |||
889 | atomic_set(&conn->connection_reinstatement, 1); | ||
890 | if (!sleep) { | ||
891 | spin_unlock_bh(&conn->state_lock); | ||
892 | return; | ||
893 | } | ||
894 | |||
895 | atomic_set(&conn->sleep_on_conn_wait_comp, 1); | ||
896 | spin_unlock_bh(&conn->state_lock); | ||
897 | |||
898 | wait_for_completion(&conn->conn_wait_comp); | ||
899 | complete(&conn->conn_post_wait_comp); | ||
900 | } | ||
901 | |||
902 | void iscsit_fall_back_to_erl0(struct iscsi_session *sess) | ||
903 | { | ||
904 | pr_debug("Falling back to ErrorRecoveryLevel=0 for SID:" | ||
905 | " %u\n", sess->sid); | ||
906 | |||
907 | atomic_set(&sess->session_fall_back_to_erl0, 1); | ||
908 | } | ||
909 | |||
910 | static void iscsit_handle_connection_cleanup(struct iscsi_conn *conn) | ||
911 | { | ||
912 | struct iscsi_session *sess = conn->sess; | ||
913 | |||
914 | if ((sess->sess_ops->ErrorRecoveryLevel == 2) && | ||
915 | !atomic_read(&sess->session_reinstatement) && | ||
916 | !atomic_read(&sess->session_fall_back_to_erl0)) | ||
917 | iscsit_connection_recovery_transport_reset(conn); | ||
918 | else { | ||
919 | pr_debug("Performing cleanup for failed iSCSI" | ||
920 | " Connection ID: %hu from %s\n", conn->cid, | ||
921 | sess->sess_ops->InitiatorName); | ||
922 | iscsit_close_connection(conn); | ||
923 | } | ||
924 | } | ||
925 | |||
926 | extern void iscsit_take_action_for_connection_exit(struct iscsi_conn *conn) | ||
927 | { | ||
928 | spin_lock_bh(&conn->state_lock); | ||
929 | if (atomic_read(&conn->connection_exit)) { | ||
930 | spin_unlock_bh(&conn->state_lock); | ||
931 | return; | ||
932 | } | ||
933 | atomic_set(&conn->connection_exit, 1); | ||
934 | |||
935 | if (conn->conn_state == TARG_CONN_STATE_IN_LOGOUT) { | ||
936 | spin_unlock_bh(&conn->state_lock); | ||
937 | iscsit_close_connection(conn); | ||
938 | return; | ||
939 | } | ||
940 | |||
941 | if (conn->conn_state == TARG_CONN_STATE_CLEANUP_WAIT) { | ||
942 | spin_unlock_bh(&conn->state_lock); | ||
943 | return; | ||
944 | } | ||
945 | |||
946 | pr_debug("Moving to TARG_CONN_STATE_CLEANUP_WAIT.\n"); | ||
947 | conn->conn_state = TARG_CONN_STATE_CLEANUP_WAIT; | ||
948 | spin_unlock_bh(&conn->state_lock); | ||
949 | |||
950 | iscsit_handle_connection_cleanup(conn); | ||
951 | } | ||
952 | |||
953 | /* | ||
954 | * This is the simple function that makes the magic of | ||
955 | * sync and steering happen in the follow paradoxical order: | ||
956 | * | ||
957 | * 0) Receive conn->of_marker (bytes left until next OFMarker) | ||
958 | * bytes into an offload buffer. When we pass the exact number | ||
959 | * of bytes in conn->of_marker, iscsit_dump_data_payload() and hence | ||
960 | * rx_data() will automatically receive the identical u32 marker | ||
961 | * values and store it in conn->of_marker_offset; | ||
962 | * 1) Now conn->of_marker_offset will contain the offset to the start | ||
963 | * of the next iSCSI PDU. Dump these remaining bytes into another | ||
964 | * offload buffer. | ||
965 | * 2) We are done! | ||
966 | * Next byte in the TCP stream will contain the next iSCSI PDU! | ||
967 | * Cool Huh?! | ||
968 | */ | ||
969 | int iscsit_recover_from_unknown_opcode(struct iscsi_conn *conn) | ||
970 | { | ||
971 | /* | ||
972 | * Make sure the remaining bytes to next maker is a sane value. | ||
973 | */ | ||
974 | if (conn->of_marker > (conn->conn_ops->OFMarkInt * 4)) { | ||
975 | pr_err("Remaining bytes to OFMarker: %u exceeds" | ||
976 | " OFMarkInt bytes: %u.\n", conn->of_marker, | ||
977 | conn->conn_ops->OFMarkInt * 4); | ||
978 | return -1; | ||
979 | } | ||
980 | |||
981 | pr_debug("Advancing %u bytes in TCP stream to get to the" | ||
982 | " next OFMarker.\n", conn->of_marker); | ||
983 | |||
984 | if (iscsit_dump_data_payload(conn, conn->of_marker, 0) < 0) | ||
985 | return -1; | ||
986 | |||
987 | /* | ||
988 | * Make sure the offset marker we retrived is a valid value. | ||
989 | */ | ||
990 | if (conn->of_marker_offset > (ISCSI_HDR_LEN + (ISCSI_CRC_LEN * 2) + | ||
991 | conn->conn_ops->MaxRecvDataSegmentLength)) { | ||
992 | pr_err("OfMarker offset value: %u exceeds limit.\n", | ||
993 | conn->of_marker_offset); | ||
994 | return -1; | ||
995 | } | ||
996 | |||
997 | pr_debug("Discarding %u bytes of TCP stream to get to the" | ||
998 | " next iSCSI Opcode.\n", conn->of_marker_offset); | ||
999 | |||
1000 | if (iscsit_dump_data_payload(conn, conn->of_marker_offset, 0) < 0) | ||
1001 | return -1; | ||
1002 | |||
1003 | return 0; | ||
1004 | } | ||
diff --git a/drivers/target/iscsi/iscsi_target_erl0.h b/drivers/target/iscsi/iscsi_target_erl0.h new file mode 100644 index 000000000000..21acc9a06376 --- /dev/null +++ b/drivers/target/iscsi/iscsi_target_erl0.h | |||
@@ -0,0 +1,15 @@ | |||
1 | #ifndef ISCSI_TARGET_ERL0_H | ||
2 | #define ISCSI_TARGET_ERL0_H | ||
3 | |||
4 | extern void iscsit_set_dataout_sequence_values(struct iscsi_cmd *); | ||
5 | extern int iscsit_check_pre_dataout(struct iscsi_cmd *, unsigned char *); | ||
6 | extern int iscsit_check_post_dataout(struct iscsi_cmd *, unsigned char *, u8); | ||
7 | extern void iscsit_start_time2retain_handler(struct iscsi_session *); | ||
8 | extern int iscsit_stop_time2retain_timer(struct iscsi_session *); | ||
9 | extern void iscsit_connection_reinstatement_rcfr(struct iscsi_conn *); | ||
10 | extern void iscsit_cause_connection_reinstatement(struct iscsi_conn *, int); | ||
11 | extern void iscsit_fall_back_to_erl0(struct iscsi_session *); | ||
12 | extern void iscsit_take_action_for_connection_exit(struct iscsi_conn *); | ||
13 | extern int iscsit_recover_from_unknown_opcode(struct iscsi_conn *); | ||
14 | |||
15 | #endif /*** ISCSI_TARGET_ERL0_H ***/ | ||
diff --git a/drivers/target/iscsi/iscsi_target_erl1.c b/drivers/target/iscsi/iscsi_target_erl1.c new file mode 100644 index 000000000000..980650792cf6 --- /dev/null +++ b/drivers/target/iscsi/iscsi_target_erl1.c | |||
@@ -0,0 +1,1299 @@ | |||
1 | /******************************************************************************* | ||
2 | * This file contains error recovery level one used by the iSCSI Target driver. | ||
3 | * | ||
4 | * \u00a9 Copyright 2007-2011 RisingTide Systems LLC. | ||
5 | * | ||
6 | * Licensed to the Linux Foundation under the General Public License (GPL) version 2. | ||
7 | * | ||
8 | * Author: Nicholas A. Bellinger <nab@linux-iscsi.org> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | ******************************************************************************/ | ||
20 | |||
21 | #include <linux/list.h> | ||
22 | #include <scsi/iscsi_proto.h> | ||
23 | #include <target/target_core_base.h> | ||
24 | #include <target/target_core_transport.h> | ||
25 | |||
26 | #include "iscsi_target_core.h" | ||
27 | #include "iscsi_target_seq_pdu_list.h" | ||
28 | #include "iscsi_target_datain_values.h" | ||
29 | #include "iscsi_target_device.h" | ||
30 | #include "iscsi_target_tpg.h" | ||
31 | #include "iscsi_target_util.h" | ||
32 | #include "iscsi_target_erl0.h" | ||
33 | #include "iscsi_target_erl1.h" | ||
34 | #include "iscsi_target_erl2.h" | ||
35 | #include "iscsi_target.h" | ||
36 | |||
37 | #define OFFLOAD_BUF_SIZE 32768 | ||
38 | |||
39 | /* | ||
40 | * Used to dump excess datain payload for certain error recovery | ||
41 | * situations. Receive in OFFLOAD_BUF_SIZE max of datain per rx_data(). | ||
42 | * | ||
43 | * dump_padding_digest denotes if padding and data digests need | ||
44 | * to be dumped. | ||
45 | */ | ||
46 | int iscsit_dump_data_payload( | ||
47 | struct iscsi_conn *conn, | ||
48 | u32 buf_len, | ||
49 | int dump_padding_digest) | ||
50 | { | ||
51 | char *buf, pad_bytes[4]; | ||
52 | int ret = DATAOUT_WITHIN_COMMAND_RECOVERY, rx_got; | ||
53 | u32 length, padding, offset = 0, size; | ||
54 | struct kvec iov; | ||
55 | |||
56 | length = (buf_len > OFFLOAD_BUF_SIZE) ? OFFLOAD_BUF_SIZE : buf_len; | ||
57 | |||
58 | buf = kzalloc(length, GFP_ATOMIC); | ||
59 | if (!buf) { | ||
60 | pr_err("Unable to allocate %u bytes for offload" | ||
61 | " buffer.\n", length); | ||
62 | return -1; | ||
63 | } | ||
64 | memset(&iov, 0, sizeof(struct kvec)); | ||
65 | |||
66 | while (offset < buf_len) { | ||
67 | size = ((offset + length) > buf_len) ? | ||
68 | (buf_len - offset) : length; | ||
69 | |||
70 | iov.iov_len = size; | ||
71 | iov.iov_base = buf; | ||
72 | |||
73 | rx_got = rx_data(conn, &iov, 1, size); | ||
74 | if (rx_got != size) { | ||
75 | ret = DATAOUT_CANNOT_RECOVER; | ||
76 | goto out; | ||
77 | } | ||
78 | |||
79 | offset += size; | ||
80 | } | ||
81 | |||
82 | if (!dump_padding_digest) | ||
83 | goto out; | ||
84 | |||
85 | padding = ((-buf_len) & 3); | ||
86 | if (padding != 0) { | ||
87 | iov.iov_len = padding; | ||
88 | iov.iov_base = pad_bytes; | ||
89 | |||
90 | rx_got = rx_data(conn, &iov, 1, padding); | ||
91 | if (rx_got != padding) { | ||
92 | ret = DATAOUT_CANNOT_RECOVER; | ||
93 | goto out; | ||
94 | } | ||
95 | } | ||
96 | |||
97 | if (conn->conn_ops->DataDigest) { | ||
98 | u32 data_crc; | ||
99 | |||
100 | iov.iov_len = ISCSI_CRC_LEN; | ||
101 | iov.iov_base = &data_crc; | ||
102 | |||
103 | rx_got = rx_data(conn, &iov, 1, ISCSI_CRC_LEN); | ||
104 | if (rx_got != ISCSI_CRC_LEN) { | ||
105 | ret = DATAOUT_CANNOT_RECOVER; | ||
106 | goto out; | ||
107 | } | ||
108 | } | ||
109 | |||
110 | out: | ||
111 | kfree(buf); | ||
112 | return ret; | ||
113 | } | ||
114 | |||
115 | /* | ||
116 | * Used for retransmitting R2Ts from a R2T SNACK request. | ||
117 | */ | ||
118 | static int iscsit_send_recovery_r2t_for_snack( | ||
119 | struct iscsi_cmd *cmd, | ||
120 | struct iscsi_r2t *r2t) | ||
121 | { | ||
122 | /* | ||
123 | * If the struct iscsi_r2t has not been sent yet, we can safely | ||
124 | * ignore retransmission | ||
125 | * of the R2TSN in question. | ||
126 | */ | ||
127 | spin_lock_bh(&cmd->r2t_lock); | ||
128 | if (!r2t->sent_r2t) { | ||
129 | spin_unlock_bh(&cmd->r2t_lock); | ||
130 | return 0; | ||
131 | } | ||
132 | r2t->sent_r2t = 0; | ||
133 | spin_unlock_bh(&cmd->r2t_lock); | ||
134 | |||
135 | iscsit_add_cmd_to_immediate_queue(cmd, cmd->conn, ISTATE_SEND_R2T); | ||
136 | |||
137 | return 0; | ||
138 | } | ||
139 | |||
140 | static int iscsit_handle_r2t_snack( | ||
141 | struct iscsi_cmd *cmd, | ||
142 | unsigned char *buf, | ||
143 | u32 begrun, | ||
144 | u32 runlength) | ||
145 | { | ||
146 | u32 last_r2tsn; | ||
147 | struct iscsi_r2t *r2t; | ||
148 | |||
149 | /* | ||
150 | * Make sure the initiator is not requesting retransmission | ||
151 | * of R2TSNs already acknowledged by a TMR TASK_REASSIGN. | ||
152 | */ | ||
153 | if ((cmd->cmd_flags & ICF_GOT_DATACK_SNACK) && | ||
154 | (begrun <= cmd->acked_data_sn)) { | ||
155 | pr_err("ITT: 0x%08x, R2T SNACK requesting" | ||
156 | " retransmission of R2TSN: 0x%08x to 0x%08x but already" | ||
157 | " acked to R2TSN: 0x%08x by TMR TASK_REASSIGN," | ||
158 | " protocol error.\n", cmd->init_task_tag, begrun, | ||
159 | (begrun + runlength), cmd->acked_data_sn); | ||
160 | |||
161 | return iscsit_add_reject_from_cmd( | ||
162 | ISCSI_REASON_PROTOCOL_ERROR, | ||
163 | 1, 0, buf, cmd); | ||
164 | } | ||
165 | |||
166 | if (runlength) { | ||
167 | if ((begrun + runlength) > cmd->r2t_sn) { | ||
168 | pr_err("Command ITT: 0x%08x received R2T SNACK" | ||
169 | " with BegRun: 0x%08x, RunLength: 0x%08x, exceeds" | ||
170 | " current R2TSN: 0x%08x, protocol error.\n", | ||
171 | cmd->init_task_tag, begrun, runlength, cmd->r2t_sn); | ||
172 | return iscsit_add_reject_from_cmd( | ||
173 | ISCSI_REASON_BOOKMARK_INVALID, 1, 0, buf, cmd); | ||
174 | } | ||
175 | last_r2tsn = (begrun + runlength); | ||
176 | } else | ||
177 | last_r2tsn = cmd->r2t_sn; | ||
178 | |||
179 | while (begrun < last_r2tsn) { | ||
180 | r2t = iscsit_get_holder_for_r2tsn(cmd, begrun); | ||
181 | if (!r2t) | ||
182 | return -1; | ||
183 | if (iscsit_send_recovery_r2t_for_snack(cmd, r2t) < 0) | ||
184 | return -1; | ||
185 | |||
186 | begrun++; | ||
187 | } | ||
188 | |||
189 | return 0; | ||
190 | } | ||
191 | |||
192 | /* | ||
193 | * Generates Offsets and NextBurstLength based on Begrun and Runlength | ||
194 | * carried in a Data SNACK or ExpDataSN in TMR TASK_REASSIGN. | ||
195 | * | ||
196 | * For DataSequenceInOrder=Yes and DataPDUInOrder=[Yes,No] only. | ||
197 | * | ||
198 | * FIXME: How is this handled for a RData SNACK? | ||
199 | */ | ||
200 | int iscsit_create_recovery_datain_values_datasequenceinorder_yes( | ||
201 | struct iscsi_cmd *cmd, | ||
202 | struct iscsi_datain_req *dr) | ||
203 | { | ||
204 | u32 data_sn = 0, data_sn_count = 0; | ||
205 | u32 pdu_start = 0, seq_no = 0; | ||
206 | u32 begrun = dr->begrun; | ||
207 | struct iscsi_conn *conn = cmd->conn; | ||
208 | |||
209 | while (begrun > data_sn++) { | ||
210 | data_sn_count++; | ||
211 | if ((dr->next_burst_len + | ||
212 | conn->conn_ops->MaxRecvDataSegmentLength) < | ||
213 | conn->sess->sess_ops->MaxBurstLength) { | ||
214 | dr->read_data_done += | ||
215 | conn->conn_ops->MaxRecvDataSegmentLength; | ||
216 | dr->next_burst_len += | ||
217 | conn->conn_ops->MaxRecvDataSegmentLength; | ||
218 | } else { | ||
219 | dr->read_data_done += | ||
220 | (conn->sess->sess_ops->MaxBurstLength - | ||
221 | dr->next_burst_len); | ||
222 | dr->next_burst_len = 0; | ||
223 | pdu_start += data_sn_count; | ||
224 | data_sn_count = 0; | ||
225 | seq_no++; | ||
226 | } | ||
227 | } | ||
228 | |||
229 | if (!conn->sess->sess_ops->DataPDUInOrder) { | ||
230 | cmd->seq_no = seq_no; | ||
231 | cmd->pdu_start = pdu_start; | ||
232 | cmd->pdu_send_order = data_sn_count; | ||
233 | } | ||
234 | |||
235 | return 0; | ||
236 | } | ||
237 | |||
238 | /* | ||
239 | * Generates Offsets and NextBurstLength based on Begrun and Runlength | ||
240 | * carried in a Data SNACK or ExpDataSN in TMR TASK_REASSIGN. | ||
241 | * | ||
242 | * For DataSequenceInOrder=No and DataPDUInOrder=[Yes,No] only. | ||
243 | * | ||
244 | * FIXME: How is this handled for a RData SNACK? | ||
245 | */ | ||
246 | int iscsit_create_recovery_datain_values_datasequenceinorder_no( | ||
247 | struct iscsi_cmd *cmd, | ||
248 | struct iscsi_datain_req *dr) | ||
249 | { | ||
250 | int found_seq = 0, i; | ||
251 | u32 data_sn, read_data_done = 0, seq_send_order = 0; | ||
252 | u32 begrun = dr->begrun; | ||
253 | u32 runlength = dr->runlength; | ||
254 | struct iscsi_conn *conn = cmd->conn; | ||
255 | struct iscsi_seq *first_seq = NULL, *seq = NULL; | ||
256 | |||
257 | if (!cmd->seq_list) { | ||
258 | pr_err("struct iscsi_cmd->seq_list is NULL!\n"); | ||
259 | return -1; | ||
260 | } | ||
261 | |||
262 | /* | ||
263 | * Calculate read_data_done for all sequences containing a | ||
264 | * first_datasn and last_datasn less than the BegRun. | ||
265 | * | ||
266 | * Locate the struct iscsi_seq the BegRun lies within and calculate | ||
267 | * NextBurstLenghth up to the DataSN based on MaxRecvDataSegmentLength. | ||
268 | * | ||
269 | * Also use struct iscsi_seq->seq_send_order to determine where to start. | ||
270 | */ | ||
271 | for (i = 0; i < cmd->seq_count; i++) { | ||
272 | seq = &cmd->seq_list[i]; | ||
273 | |||
274 | if (!seq->seq_send_order) | ||
275 | first_seq = seq; | ||
276 | |||
277 | /* | ||
278 | * No data has been transferred for this DataIN sequence, so the | ||
279 | * seq->first_datasn and seq->last_datasn have not been set. | ||
280 | */ | ||
281 | if (!seq->sent) { | ||
282 | #if 0 | ||
283 | pr_err("Ignoring non-sent sequence 0x%08x ->" | ||
284 | " 0x%08x\n\n", seq->first_datasn, | ||
285 | seq->last_datasn); | ||
286 | #endif | ||
287 | continue; | ||
288 | } | ||
289 | |||
290 | /* | ||
291 | * This DataIN sequence is precedes the received BegRun, add the | ||
292 | * total xfer_len of the sequence to read_data_done and reset | ||
293 | * seq->pdu_send_order. | ||
294 | */ | ||
295 | if ((seq->first_datasn < begrun) && | ||
296 | (seq->last_datasn < begrun)) { | ||
297 | #if 0 | ||
298 | pr_err("Pre BegRun sequence 0x%08x ->" | ||
299 | " 0x%08x\n", seq->first_datasn, | ||
300 | seq->last_datasn); | ||
301 | #endif | ||
302 | read_data_done += cmd->seq_list[i].xfer_len; | ||
303 | seq->next_burst_len = seq->pdu_send_order = 0; | ||
304 | continue; | ||
305 | } | ||
306 | |||
307 | /* | ||
308 | * The BegRun lies within this DataIN sequence. | ||
309 | */ | ||
310 | if ((seq->first_datasn <= begrun) && | ||
311 | (seq->last_datasn >= begrun)) { | ||
312 | #if 0 | ||
313 | pr_err("Found sequence begrun: 0x%08x in" | ||
314 | " 0x%08x -> 0x%08x\n", begrun, | ||
315 | seq->first_datasn, seq->last_datasn); | ||
316 | #endif | ||
317 | seq_send_order = seq->seq_send_order; | ||
318 | data_sn = seq->first_datasn; | ||
319 | seq->next_burst_len = seq->pdu_send_order = 0; | ||
320 | found_seq = 1; | ||
321 | |||
322 | /* | ||
323 | * For DataPDUInOrder=Yes, while the first DataSN of | ||
324 | * the sequence is less than the received BegRun, add | ||
325 | * the MaxRecvDataSegmentLength to read_data_done and | ||
326 | * to the sequence's next_burst_len; | ||
327 | * | ||
328 | * For DataPDUInOrder=No, while the first DataSN of the | ||
329 | * sequence is less than the received BegRun, find the | ||
330 | * struct iscsi_pdu of the DataSN in question and add the | ||
331 | * MaxRecvDataSegmentLength to read_data_done and to the | ||
332 | * sequence's next_burst_len; | ||
333 | */ | ||
334 | if (conn->sess->sess_ops->DataPDUInOrder) { | ||
335 | while (data_sn < begrun) { | ||
336 | seq->pdu_send_order++; | ||
337 | read_data_done += | ||
338 | conn->conn_ops->MaxRecvDataSegmentLength; | ||
339 | seq->next_burst_len += | ||
340 | conn->conn_ops->MaxRecvDataSegmentLength; | ||
341 | data_sn++; | ||
342 | } | ||
343 | } else { | ||
344 | int j; | ||
345 | struct iscsi_pdu *pdu; | ||
346 | |||
347 | while (data_sn < begrun) { | ||
348 | seq->pdu_send_order++; | ||
349 | |||
350 | for (j = 0; j < seq->pdu_count; j++) { | ||
351 | pdu = &cmd->pdu_list[ | ||
352 | seq->pdu_start + j]; | ||
353 | if (pdu->data_sn == data_sn) { | ||
354 | read_data_done += | ||
355 | pdu->length; | ||
356 | seq->next_burst_len += | ||
357 | pdu->length; | ||
358 | } | ||
359 | } | ||
360 | data_sn++; | ||
361 | } | ||
362 | } | ||
363 | continue; | ||
364 | } | ||
365 | |||
366 | /* | ||
367 | * This DataIN sequence is larger than the received BegRun, | ||
368 | * reset seq->pdu_send_order and continue. | ||
369 | */ | ||
370 | if ((seq->first_datasn > begrun) || | ||
371 | (seq->last_datasn > begrun)) { | ||
372 | #if 0 | ||
373 | pr_err("Post BegRun sequence 0x%08x -> 0x%08x\n", | ||
374 | seq->first_datasn, seq->last_datasn); | ||
375 | #endif | ||
376 | seq->next_burst_len = seq->pdu_send_order = 0; | ||
377 | continue; | ||
378 | } | ||
379 | } | ||
380 | |||
381 | if (!found_seq) { | ||
382 | if (!begrun) { | ||
383 | if (!first_seq) { | ||
384 | pr_err("ITT: 0x%08x, Begrun: 0x%08x" | ||
385 | " but first_seq is NULL\n", | ||
386 | cmd->init_task_tag, begrun); | ||
387 | return -1; | ||
388 | } | ||
389 | seq_send_order = first_seq->seq_send_order; | ||
390 | seq->next_burst_len = seq->pdu_send_order = 0; | ||
391 | goto done; | ||
392 | } | ||
393 | |||
394 | pr_err("Unable to locate struct iscsi_seq for ITT: 0x%08x," | ||
395 | " BegRun: 0x%08x, RunLength: 0x%08x while" | ||
396 | " DataSequenceInOrder=No and DataPDUInOrder=%s.\n", | ||
397 | cmd->init_task_tag, begrun, runlength, | ||
398 | (conn->sess->sess_ops->DataPDUInOrder) ? "Yes" : "No"); | ||
399 | return -1; | ||
400 | } | ||
401 | |||
402 | done: | ||
403 | dr->read_data_done = read_data_done; | ||
404 | dr->seq_send_order = seq_send_order; | ||
405 | |||
406 | return 0; | ||
407 | } | ||
408 | |||
409 | static int iscsit_handle_recovery_datain( | ||
410 | struct iscsi_cmd *cmd, | ||
411 | unsigned char *buf, | ||
412 | u32 begrun, | ||
413 | u32 runlength) | ||
414 | { | ||
415 | struct iscsi_conn *conn = cmd->conn; | ||
416 | struct iscsi_datain_req *dr; | ||
417 | struct se_cmd *se_cmd = &cmd->se_cmd; | ||
418 | |||
419 | if (!atomic_read(&se_cmd->t_transport_complete)) { | ||
420 | pr_err("Ignoring ITT: 0x%08x Data SNACK\n", | ||
421 | cmd->init_task_tag); | ||
422 | return 0; | ||
423 | } | ||
424 | |||
425 | /* | ||
426 | * Make sure the initiator is not requesting retransmission | ||
427 | * of DataSNs already acknowledged by a Data ACK SNACK. | ||
428 | */ | ||
429 | if ((cmd->cmd_flags & ICF_GOT_DATACK_SNACK) && | ||
430 | (begrun <= cmd->acked_data_sn)) { | ||
431 | pr_err("ITT: 0x%08x, Data SNACK requesting" | ||
432 | " retransmission of DataSN: 0x%08x to 0x%08x but" | ||
433 | " already acked to DataSN: 0x%08x by Data ACK SNACK," | ||
434 | " protocol error.\n", cmd->init_task_tag, begrun, | ||
435 | (begrun + runlength), cmd->acked_data_sn); | ||
436 | |||
437 | return iscsit_add_reject_from_cmd(ISCSI_REASON_PROTOCOL_ERROR, | ||
438 | 1, 0, buf, cmd); | ||
439 | } | ||
440 | |||
441 | /* | ||
442 | * Make sure BegRun and RunLength in the Data SNACK are sane. | ||
443 | * Note: (cmd->data_sn - 1) will carry the maximum DataSN sent. | ||
444 | */ | ||
445 | if ((begrun + runlength) > (cmd->data_sn - 1)) { | ||
446 | pr_err("Initiator requesting BegRun: 0x%08x, RunLength" | ||
447 | ": 0x%08x greater than maximum DataSN: 0x%08x.\n", | ||
448 | begrun, runlength, (cmd->data_sn - 1)); | ||
449 | return iscsit_add_reject_from_cmd(ISCSI_REASON_BOOKMARK_INVALID, | ||
450 | 1, 0, buf, cmd); | ||
451 | } | ||
452 | |||
453 | dr = iscsit_allocate_datain_req(); | ||
454 | if (!dr) | ||
455 | return iscsit_add_reject_from_cmd(ISCSI_REASON_BOOKMARK_NO_RESOURCES, | ||
456 | 1, 0, buf, cmd); | ||
457 | |||
458 | dr->data_sn = dr->begrun = begrun; | ||
459 | dr->runlength = runlength; | ||
460 | dr->generate_recovery_values = 1; | ||
461 | dr->recovery = DATAIN_WITHIN_COMMAND_RECOVERY; | ||
462 | |||
463 | iscsit_attach_datain_req(cmd, dr); | ||
464 | |||
465 | cmd->i_state = ISTATE_SEND_DATAIN; | ||
466 | iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state); | ||
467 | |||
468 | return 0; | ||
469 | } | ||
470 | |||
471 | int iscsit_handle_recovery_datain_or_r2t( | ||
472 | struct iscsi_conn *conn, | ||
473 | unsigned char *buf, | ||
474 | u32 init_task_tag, | ||
475 | u32 targ_xfer_tag, | ||
476 | u32 begrun, | ||
477 | u32 runlength) | ||
478 | { | ||
479 | struct iscsi_cmd *cmd; | ||
480 | |||
481 | cmd = iscsit_find_cmd_from_itt(conn, init_task_tag); | ||
482 | if (!cmd) | ||
483 | return 0; | ||
484 | |||
485 | /* | ||
486 | * FIXME: This will not work for bidi commands. | ||
487 | */ | ||
488 | switch (cmd->data_direction) { | ||
489 | case DMA_TO_DEVICE: | ||
490 | return iscsit_handle_r2t_snack(cmd, buf, begrun, runlength); | ||
491 | case DMA_FROM_DEVICE: | ||
492 | return iscsit_handle_recovery_datain(cmd, buf, begrun, | ||
493 | runlength); | ||
494 | default: | ||
495 | pr_err("Unknown cmd->data_direction: 0x%02x\n", | ||
496 | cmd->data_direction); | ||
497 | return -1; | ||
498 | } | ||
499 | |||
500 | return 0; | ||
501 | } | ||
502 | |||
503 | /* #warning FIXME: Status SNACK needs to be dependent on OPCODE!!! */ | ||
504 | int iscsit_handle_status_snack( | ||
505 | struct iscsi_conn *conn, | ||
506 | u32 init_task_tag, | ||
507 | u32 targ_xfer_tag, | ||
508 | u32 begrun, | ||
509 | u32 runlength) | ||
510 | { | ||
511 | struct iscsi_cmd *cmd = NULL; | ||
512 | u32 last_statsn; | ||
513 | int found_cmd; | ||
514 | |||
515 | if (conn->exp_statsn > begrun) { | ||
516 | pr_err("Got Status SNACK Begrun: 0x%08x, RunLength:" | ||
517 | " 0x%08x but already got ExpStatSN: 0x%08x on CID:" | ||
518 | " %hu.\n", begrun, runlength, conn->exp_statsn, | ||
519 | conn->cid); | ||
520 | return 0; | ||
521 | } | ||
522 | |||
523 | last_statsn = (!runlength) ? conn->stat_sn : (begrun + runlength); | ||
524 | |||
525 | while (begrun < last_statsn) { | ||
526 | found_cmd = 0; | ||
527 | |||
528 | spin_lock_bh(&conn->cmd_lock); | ||
529 | list_for_each_entry(cmd, &conn->conn_cmd_list, i_list) { | ||
530 | if (cmd->stat_sn == begrun) { | ||
531 | found_cmd = 1; | ||
532 | break; | ||
533 | } | ||
534 | } | ||
535 | spin_unlock_bh(&conn->cmd_lock); | ||
536 | |||
537 | if (!found_cmd) { | ||
538 | pr_err("Unable to find StatSN: 0x%08x for" | ||
539 | " a Status SNACK, assuming this was a" | ||
540 | " protactic SNACK for an untransmitted" | ||
541 | " StatSN, ignoring.\n", begrun); | ||
542 | begrun++; | ||
543 | continue; | ||
544 | } | ||
545 | |||
546 | spin_lock_bh(&cmd->istate_lock); | ||
547 | if (cmd->i_state == ISTATE_SEND_DATAIN) { | ||
548 | spin_unlock_bh(&cmd->istate_lock); | ||
549 | pr_err("Ignoring Status SNACK for BegRun:" | ||
550 | " 0x%08x, RunLength: 0x%08x, assuming this was" | ||
551 | " a protactic SNACK for an untransmitted" | ||
552 | " StatSN\n", begrun, runlength); | ||
553 | begrun++; | ||
554 | continue; | ||
555 | } | ||
556 | spin_unlock_bh(&cmd->istate_lock); | ||
557 | |||
558 | cmd->i_state = ISTATE_SEND_STATUS_RECOVERY; | ||
559 | iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state); | ||
560 | begrun++; | ||
561 | } | ||
562 | |||
563 | return 0; | ||
564 | } | ||
565 | |||
566 | int iscsit_handle_data_ack( | ||
567 | struct iscsi_conn *conn, | ||
568 | u32 targ_xfer_tag, | ||
569 | u32 begrun, | ||
570 | u32 runlength) | ||
571 | { | ||
572 | struct iscsi_cmd *cmd = NULL; | ||
573 | |||
574 | cmd = iscsit_find_cmd_from_ttt(conn, targ_xfer_tag); | ||
575 | if (!cmd) { | ||
576 | pr_err("Data ACK SNACK for TTT: 0x%08x is" | ||
577 | " invalid.\n", targ_xfer_tag); | ||
578 | return -1; | ||
579 | } | ||
580 | |||
581 | if (begrun <= cmd->acked_data_sn) { | ||
582 | pr_err("ITT: 0x%08x Data ACK SNACK BegRUN: 0x%08x is" | ||
583 | " less than the already acked DataSN: 0x%08x.\n", | ||
584 | cmd->init_task_tag, begrun, cmd->acked_data_sn); | ||
585 | return -1; | ||
586 | } | ||
587 | |||
588 | /* | ||
589 | * For Data ACK SNACK, BegRun is the next expected DataSN. | ||
590 | * (see iSCSI v19: 10.16.6) | ||
591 | */ | ||
592 | cmd->cmd_flags |= ICF_GOT_DATACK_SNACK; | ||
593 | cmd->acked_data_sn = (begrun - 1); | ||
594 | |||
595 | pr_debug("Received Data ACK SNACK for ITT: 0x%08x," | ||
596 | " updated acked DataSN to 0x%08x.\n", | ||
597 | cmd->init_task_tag, cmd->acked_data_sn); | ||
598 | |||
599 | return 0; | ||
600 | } | ||
601 | |||
602 | static int iscsit_send_recovery_r2t( | ||
603 | struct iscsi_cmd *cmd, | ||
604 | u32 offset, | ||
605 | u32 xfer_len) | ||
606 | { | ||
607 | int ret; | ||
608 | |||
609 | spin_lock_bh(&cmd->r2t_lock); | ||
610 | ret = iscsit_add_r2t_to_list(cmd, offset, xfer_len, 1, 0); | ||
611 | spin_unlock_bh(&cmd->r2t_lock); | ||
612 | |||
613 | return ret; | ||
614 | } | ||
615 | |||
616 | int iscsit_dataout_datapduinorder_no_fbit( | ||
617 | struct iscsi_cmd *cmd, | ||
618 | struct iscsi_pdu *pdu) | ||
619 | { | ||
620 | int i, send_recovery_r2t = 0, recovery = 0; | ||
621 | u32 length = 0, offset = 0, pdu_count = 0, xfer_len = 0; | ||
622 | struct iscsi_conn *conn = cmd->conn; | ||
623 | struct iscsi_pdu *first_pdu = NULL; | ||
624 | |||
625 | /* | ||
626 | * Get an struct iscsi_pdu pointer to the first PDU, and total PDU count | ||
627 | * of the DataOUT sequence. | ||
628 | */ | ||
629 | if (conn->sess->sess_ops->DataSequenceInOrder) { | ||
630 | for (i = 0; i < cmd->pdu_count; i++) { | ||
631 | if (cmd->pdu_list[i].seq_no == pdu->seq_no) { | ||
632 | if (!first_pdu) | ||
633 | first_pdu = &cmd->pdu_list[i]; | ||
634 | xfer_len += cmd->pdu_list[i].length; | ||
635 | pdu_count++; | ||
636 | } else if (pdu_count) | ||
637 | break; | ||
638 | } | ||
639 | } else { | ||
640 | struct iscsi_seq *seq = cmd->seq_ptr; | ||
641 | |||
642 | first_pdu = &cmd->pdu_list[seq->pdu_start]; | ||
643 | pdu_count = seq->pdu_count; | ||
644 | } | ||
645 | |||
646 | if (!first_pdu || !pdu_count) | ||
647 | return DATAOUT_CANNOT_RECOVER; | ||
648 | |||
649 | /* | ||
650 | * Loop through the ending DataOUT Sequence checking each struct iscsi_pdu. | ||
651 | * The following ugly logic does batching of not received PDUs. | ||
652 | */ | ||
653 | for (i = 0; i < pdu_count; i++) { | ||
654 | if (first_pdu[i].status == ISCSI_PDU_RECEIVED_OK) { | ||
655 | if (!send_recovery_r2t) | ||
656 | continue; | ||
657 | |||
658 | if (iscsit_send_recovery_r2t(cmd, offset, length) < 0) | ||
659 | return DATAOUT_CANNOT_RECOVER; | ||
660 | |||
661 | send_recovery_r2t = length = offset = 0; | ||
662 | continue; | ||
663 | } | ||
664 | /* | ||
665 | * Set recovery = 1 for any missing, CRC failed, or timed | ||
666 | * out PDUs to let the DataOUT logic know that this sequence | ||
667 | * has not been completed yet. | ||
668 | * | ||
669 | * Also, only send a Recovery R2T for ISCSI_PDU_NOT_RECEIVED. | ||
670 | * We assume if the PDU either failed CRC or timed out | ||
671 | * that a Recovery R2T has already been sent. | ||
672 | */ | ||
673 | recovery = 1; | ||
674 | |||
675 | if (first_pdu[i].status != ISCSI_PDU_NOT_RECEIVED) | ||
676 | continue; | ||
677 | |||
678 | if (!offset) | ||
679 | offset = first_pdu[i].offset; | ||
680 | length += first_pdu[i].length; | ||
681 | |||
682 | send_recovery_r2t = 1; | ||
683 | } | ||
684 | |||
685 | if (send_recovery_r2t) | ||
686 | if (iscsit_send_recovery_r2t(cmd, offset, length) < 0) | ||
687 | return DATAOUT_CANNOT_RECOVER; | ||
688 | |||
689 | return (!recovery) ? DATAOUT_NORMAL : DATAOUT_WITHIN_COMMAND_RECOVERY; | ||
690 | } | ||
691 | |||
692 | static int iscsit_recalculate_dataout_values( | ||
693 | struct iscsi_cmd *cmd, | ||
694 | u32 pdu_offset, | ||
695 | u32 pdu_length, | ||
696 | u32 *r2t_offset, | ||
697 | u32 *r2t_length) | ||
698 | { | ||
699 | int i; | ||
700 | struct iscsi_conn *conn = cmd->conn; | ||
701 | struct iscsi_pdu *pdu = NULL; | ||
702 | |||
703 | if (conn->sess->sess_ops->DataSequenceInOrder) { | ||
704 | cmd->data_sn = 0; | ||
705 | |||
706 | if (conn->sess->sess_ops->DataPDUInOrder) { | ||
707 | *r2t_offset = cmd->write_data_done; | ||
708 | *r2t_length = (cmd->seq_end_offset - | ||
709 | cmd->write_data_done); | ||
710 | return 0; | ||
711 | } | ||
712 | |||
713 | *r2t_offset = cmd->seq_start_offset; | ||
714 | *r2t_length = (cmd->seq_end_offset - cmd->seq_start_offset); | ||
715 | |||
716 | for (i = 0; i < cmd->pdu_count; i++) { | ||
717 | pdu = &cmd->pdu_list[i]; | ||
718 | |||
719 | if (pdu->status != ISCSI_PDU_RECEIVED_OK) | ||
720 | continue; | ||
721 | |||
722 | if ((pdu->offset >= cmd->seq_start_offset) && | ||
723 | ((pdu->offset + pdu->length) <= | ||
724 | cmd->seq_end_offset)) { | ||
725 | if (!cmd->unsolicited_data) | ||
726 | cmd->next_burst_len -= pdu->length; | ||
727 | else | ||
728 | cmd->first_burst_len -= pdu->length; | ||
729 | |||
730 | cmd->write_data_done -= pdu->length; | ||
731 | pdu->status = ISCSI_PDU_NOT_RECEIVED; | ||
732 | } | ||
733 | } | ||
734 | } else { | ||
735 | struct iscsi_seq *seq = NULL; | ||
736 | |||
737 | seq = iscsit_get_seq_holder(cmd, pdu_offset, pdu_length); | ||
738 | if (!seq) | ||
739 | return -1; | ||
740 | |||
741 | *r2t_offset = seq->orig_offset; | ||
742 | *r2t_length = seq->xfer_len; | ||
743 | |||
744 | cmd->write_data_done -= (seq->offset - seq->orig_offset); | ||
745 | if (cmd->immediate_data) | ||
746 | cmd->first_burst_len = cmd->write_data_done; | ||
747 | |||
748 | seq->data_sn = 0; | ||
749 | seq->offset = seq->orig_offset; | ||
750 | seq->next_burst_len = 0; | ||
751 | seq->status = DATAOUT_SEQUENCE_WITHIN_COMMAND_RECOVERY; | ||
752 | |||
753 | if (conn->sess->sess_ops->DataPDUInOrder) | ||
754 | return 0; | ||
755 | |||
756 | for (i = 0; i < seq->pdu_count; i++) { | ||
757 | pdu = &cmd->pdu_list[i+seq->pdu_start]; | ||
758 | |||
759 | if (pdu->status != ISCSI_PDU_RECEIVED_OK) | ||
760 | continue; | ||
761 | |||
762 | pdu->status = ISCSI_PDU_NOT_RECEIVED; | ||
763 | } | ||
764 | } | ||
765 | |||
766 | return 0; | ||
767 | } | ||
768 | |||
769 | int iscsit_recover_dataout_sequence( | ||
770 | struct iscsi_cmd *cmd, | ||
771 | u32 pdu_offset, | ||
772 | u32 pdu_length) | ||
773 | { | ||
774 | u32 r2t_length = 0, r2t_offset = 0; | ||
775 | |||
776 | spin_lock_bh(&cmd->istate_lock); | ||
777 | cmd->cmd_flags |= ICF_WITHIN_COMMAND_RECOVERY; | ||
778 | spin_unlock_bh(&cmd->istate_lock); | ||
779 | |||
780 | if (iscsit_recalculate_dataout_values(cmd, pdu_offset, pdu_length, | ||
781 | &r2t_offset, &r2t_length) < 0) | ||
782 | return DATAOUT_CANNOT_RECOVER; | ||
783 | |||
784 | iscsit_send_recovery_r2t(cmd, r2t_offset, r2t_length); | ||
785 | |||
786 | return DATAOUT_WITHIN_COMMAND_RECOVERY; | ||
787 | } | ||
788 | |||
789 | static struct iscsi_ooo_cmdsn *iscsit_allocate_ooo_cmdsn(void) | ||
790 | { | ||
791 | struct iscsi_ooo_cmdsn *ooo_cmdsn = NULL; | ||
792 | |||
793 | ooo_cmdsn = kmem_cache_zalloc(lio_ooo_cache, GFP_ATOMIC); | ||
794 | if (!ooo_cmdsn) { | ||
795 | pr_err("Unable to allocate memory for" | ||
796 | " struct iscsi_ooo_cmdsn.\n"); | ||
797 | return NULL; | ||
798 | } | ||
799 | INIT_LIST_HEAD(&ooo_cmdsn->ooo_list); | ||
800 | |||
801 | return ooo_cmdsn; | ||
802 | } | ||
803 | |||
804 | /* | ||
805 | * Called with sess->cmdsn_mutex held. | ||
806 | */ | ||
807 | static int iscsit_attach_ooo_cmdsn( | ||
808 | struct iscsi_session *sess, | ||
809 | struct iscsi_ooo_cmdsn *ooo_cmdsn) | ||
810 | { | ||
811 | struct iscsi_ooo_cmdsn *ooo_tail, *ooo_tmp; | ||
812 | /* | ||
813 | * We attach the struct iscsi_ooo_cmdsn entry to the out of order | ||
814 | * list in increasing CmdSN order. | ||
815 | * This allows iscsi_execute_ooo_cmdsns() to detect any | ||
816 | * additional CmdSN holes while performing delayed execution. | ||
817 | */ | ||
818 | if (list_empty(&sess->sess_ooo_cmdsn_list)) | ||
819 | list_add_tail(&ooo_cmdsn->ooo_list, | ||
820 | &sess->sess_ooo_cmdsn_list); | ||
821 | else { | ||
822 | ooo_tail = list_entry(sess->sess_ooo_cmdsn_list.prev, | ||
823 | typeof(*ooo_tail), ooo_list); | ||
824 | /* | ||
825 | * CmdSN is greater than the tail of the list. | ||
826 | */ | ||
827 | if (ooo_tail->cmdsn < ooo_cmdsn->cmdsn) | ||
828 | list_add_tail(&ooo_cmdsn->ooo_list, | ||
829 | &sess->sess_ooo_cmdsn_list); | ||
830 | else { | ||
831 | /* | ||
832 | * CmdSN is either lower than the head, or somewhere | ||
833 | * in the middle. | ||
834 | */ | ||
835 | list_for_each_entry(ooo_tmp, &sess->sess_ooo_cmdsn_list, | ||
836 | ooo_list) { | ||
837 | while (ooo_tmp->cmdsn < ooo_cmdsn->cmdsn) | ||
838 | continue; | ||
839 | |||
840 | list_add(&ooo_cmdsn->ooo_list, | ||
841 | &ooo_tmp->ooo_list); | ||
842 | break; | ||
843 | } | ||
844 | } | ||
845 | } | ||
846 | |||
847 | return 0; | ||
848 | } | ||
849 | |||
850 | /* | ||
851 | * Removes an struct iscsi_ooo_cmdsn from a session's list, | ||
852 | * called with struct iscsi_session->cmdsn_mutex held. | ||
853 | */ | ||
854 | void iscsit_remove_ooo_cmdsn( | ||
855 | struct iscsi_session *sess, | ||
856 | struct iscsi_ooo_cmdsn *ooo_cmdsn) | ||
857 | { | ||
858 | list_del(&ooo_cmdsn->ooo_list); | ||
859 | kmem_cache_free(lio_ooo_cache, ooo_cmdsn); | ||
860 | } | ||
861 | |||
862 | void iscsit_clear_ooo_cmdsns_for_conn(struct iscsi_conn *conn) | ||
863 | { | ||
864 | struct iscsi_ooo_cmdsn *ooo_cmdsn; | ||
865 | struct iscsi_session *sess = conn->sess; | ||
866 | |||
867 | mutex_lock(&sess->cmdsn_mutex); | ||
868 | list_for_each_entry(ooo_cmdsn, &sess->sess_ooo_cmdsn_list, ooo_list) { | ||
869 | if (ooo_cmdsn->cid != conn->cid) | ||
870 | continue; | ||
871 | |||
872 | ooo_cmdsn->cmd = NULL; | ||
873 | } | ||
874 | mutex_unlock(&sess->cmdsn_mutex); | ||
875 | } | ||
876 | |||
877 | /* | ||
878 | * Called with sess->cmdsn_mutex held. | ||
879 | */ | ||
880 | int iscsit_execute_ooo_cmdsns(struct iscsi_session *sess) | ||
881 | { | ||
882 | int ooo_count = 0; | ||
883 | struct iscsi_cmd *cmd = NULL; | ||
884 | struct iscsi_ooo_cmdsn *ooo_cmdsn, *ooo_cmdsn_tmp; | ||
885 | |||
886 | list_for_each_entry_safe(ooo_cmdsn, ooo_cmdsn_tmp, | ||
887 | &sess->sess_ooo_cmdsn_list, ooo_list) { | ||
888 | if (ooo_cmdsn->cmdsn != sess->exp_cmd_sn) | ||
889 | continue; | ||
890 | |||
891 | if (!ooo_cmdsn->cmd) { | ||
892 | sess->exp_cmd_sn++; | ||
893 | iscsit_remove_ooo_cmdsn(sess, ooo_cmdsn); | ||
894 | continue; | ||
895 | } | ||
896 | |||
897 | cmd = ooo_cmdsn->cmd; | ||
898 | cmd->i_state = cmd->deferred_i_state; | ||
899 | ooo_count++; | ||
900 | sess->exp_cmd_sn++; | ||
901 | pr_debug("Executing out of order CmdSN: 0x%08x," | ||
902 | " incremented ExpCmdSN to 0x%08x.\n", | ||
903 | cmd->cmd_sn, sess->exp_cmd_sn); | ||
904 | |||
905 | iscsit_remove_ooo_cmdsn(sess, ooo_cmdsn); | ||
906 | |||
907 | if (iscsit_execute_cmd(cmd, 1) < 0) | ||
908 | return -1; | ||
909 | |||
910 | continue; | ||
911 | } | ||
912 | |||
913 | return ooo_count; | ||
914 | } | ||
915 | |||
916 | /* | ||
917 | * Called either: | ||
918 | * | ||
919 | * 1. With sess->cmdsn_mutex held from iscsi_execute_ooo_cmdsns() | ||
920 | * or iscsi_check_received_cmdsn(). | ||
921 | * 2. With no locks held directly from iscsi_handle_XXX_pdu() functions | ||
922 | * for immediate commands. | ||
923 | */ | ||
924 | int iscsit_execute_cmd(struct iscsi_cmd *cmd, int ooo) | ||
925 | { | ||
926 | struct se_cmd *se_cmd = &cmd->se_cmd; | ||
927 | int lr = 0; | ||
928 | |||
929 | spin_lock_bh(&cmd->istate_lock); | ||
930 | if (ooo) | ||
931 | cmd->cmd_flags &= ~ICF_OOO_CMDSN; | ||
932 | |||
933 | switch (cmd->iscsi_opcode) { | ||
934 | case ISCSI_OP_SCSI_CMD: | ||
935 | /* | ||
936 | * Go ahead and send the CHECK_CONDITION status for | ||
937 | * any SCSI CDB exceptions that may have occurred, also | ||
938 | * handle the SCF_SCSI_RESERVATION_CONFLICT case here as well. | ||
939 | */ | ||
940 | if (se_cmd->se_cmd_flags & SCF_SCSI_CDB_EXCEPTION) { | ||
941 | if (se_cmd->se_cmd_flags & | ||
942 | SCF_SCSI_RESERVATION_CONFLICT) { | ||
943 | cmd->i_state = ISTATE_SEND_STATUS; | ||
944 | spin_unlock_bh(&cmd->istate_lock); | ||
945 | iscsit_add_cmd_to_response_queue(cmd, cmd->conn, | ||
946 | cmd->i_state); | ||
947 | return 0; | ||
948 | } | ||
949 | spin_unlock_bh(&cmd->istate_lock); | ||
950 | /* | ||
951 | * Determine if delayed TASK_ABORTED status for WRITEs | ||
952 | * should be sent now if no unsolicited data out | ||
953 | * payloads are expected, or if the delayed status | ||
954 | * should be sent after unsolicited data out with | ||
955 | * ISCSI_FLAG_CMD_FINAL set in iscsi_handle_data_out() | ||
956 | */ | ||
957 | if (transport_check_aborted_status(se_cmd, | ||
958 | (cmd->unsolicited_data == 0)) != 0) | ||
959 | return 0; | ||
960 | /* | ||
961 | * Otherwise send CHECK_CONDITION and sense for | ||
962 | * exception | ||
963 | */ | ||
964 | return transport_send_check_condition_and_sense(se_cmd, | ||
965 | se_cmd->scsi_sense_reason, 0); | ||
966 | } | ||
967 | /* | ||
968 | * Special case for delayed CmdSN with Immediate | ||
969 | * Data and/or Unsolicited Data Out attached. | ||
970 | */ | ||
971 | if (cmd->immediate_data) { | ||
972 | if (cmd->cmd_flags & ICF_GOT_LAST_DATAOUT) { | ||
973 | spin_unlock_bh(&cmd->istate_lock); | ||
974 | return transport_generic_handle_data( | ||
975 | &cmd->se_cmd); | ||
976 | } | ||
977 | spin_unlock_bh(&cmd->istate_lock); | ||
978 | |||
979 | if (!(cmd->cmd_flags & | ||
980 | ICF_NON_IMMEDIATE_UNSOLICITED_DATA)) { | ||
981 | /* | ||
982 | * Send the delayed TASK_ABORTED status for | ||
983 | * WRITEs if no more unsolicitied data is | ||
984 | * expected. | ||
985 | */ | ||
986 | if (transport_check_aborted_status(se_cmd, 1) | ||
987 | != 0) | ||
988 | return 0; | ||
989 | |||
990 | iscsit_set_dataout_sequence_values(cmd); | ||
991 | iscsit_build_r2ts_for_cmd(cmd, cmd->conn, 0); | ||
992 | } | ||
993 | return 0; | ||
994 | } | ||
995 | /* | ||
996 | * The default handler. | ||
997 | */ | ||
998 | spin_unlock_bh(&cmd->istate_lock); | ||
999 | |||
1000 | if ((cmd->data_direction == DMA_TO_DEVICE) && | ||
1001 | !(cmd->cmd_flags & ICF_NON_IMMEDIATE_UNSOLICITED_DATA)) { | ||
1002 | /* | ||
1003 | * Send the delayed TASK_ABORTED status for WRITEs if | ||
1004 | * no more nsolicitied data is expected. | ||
1005 | */ | ||
1006 | if (transport_check_aborted_status(se_cmd, 1) != 0) | ||
1007 | return 0; | ||
1008 | |||
1009 | iscsit_set_dataout_sequence_values(cmd); | ||
1010 | spin_lock_bh(&cmd->dataout_timeout_lock); | ||
1011 | iscsit_start_dataout_timer(cmd, cmd->conn); | ||
1012 | spin_unlock_bh(&cmd->dataout_timeout_lock); | ||
1013 | } | ||
1014 | return transport_handle_cdb_direct(&cmd->se_cmd); | ||
1015 | |||
1016 | case ISCSI_OP_NOOP_OUT: | ||
1017 | case ISCSI_OP_TEXT: | ||
1018 | spin_unlock_bh(&cmd->istate_lock); | ||
1019 | iscsit_add_cmd_to_response_queue(cmd, cmd->conn, cmd->i_state); | ||
1020 | break; | ||
1021 | case ISCSI_OP_SCSI_TMFUNC: | ||
1022 | if (se_cmd->se_cmd_flags & SCF_SCSI_CDB_EXCEPTION) { | ||
1023 | spin_unlock_bh(&cmd->istate_lock); | ||
1024 | iscsit_add_cmd_to_response_queue(cmd, cmd->conn, | ||
1025 | cmd->i_state); | ||
1026 | return 0; | ||
1027 | } | ||
1028 | spin_unlock_bh(&cmd->istate_lock); | ||
1029 | |||
1030 | return transport_generic_handle_tmr(&cmd->se_cmd); | ||
1031 | case ISCSI_OP_LOGOUT: | ||
1032 | spin_unlock_bh(&cmd->istate_lock); | ||
1033 | switch (cmd->logout_reason) { | ||
1034 | case ISCSI_LOGOUT_REASON_CLOSE_SESSION: | ||
1035 | lr = iscsit_logout_closesession(cmd, cmd->conn); | ||
1036 | break; | ||
1037 | case ISCSI_LOGOUT_REASON_CLOSE_CONNECTION: | ||
1038 | lr = iscsit_logout_closeconnection(cmd, cmd->conn); | ||
1039 | break; | ||
1040 | case ISCSI_LOGOUT_REASON_RECOVERY: | ||
1041 | lr = iscsit_logout_removeconnforrecovery(cmd, cmd->conn); | ||
1042 | break; | ||
1043 | default: | ||
1044 | pr_err("Unknown iSCSI Logout Request Code:" | ||
1045 | " 0x%02x\n", cmd->logout_reason); | ||
1046 | return -1; | ||
1047 | } | ||
1048 | |||
1049 | return lr; | ||
1050 | default: | ||
1051 | spin_unlock_bh(&cmd->istate_lock); | ||
1052 | pr_err("Cannot perform out of order execution for" | ||
1053 | " unknown iSCSI Opcode: 0x%02x\n", cmd->iscsi_opcode); | ||
1054 | return -1; | ||
1055 | } | ||
1056 | |||
1057 | return 0; | ||
1058 | } | ||
1059 | |||
1060 | void iscsit_free_all_ooo_cmdsns(struct iscsi_session *sess) | ||
1061 | { | ||
1062 | struct iscsi_ooo_cmdsn *ooo_cmdsn, *ooo_cmdsn_tmp; | ||
1063 | |||
1064 | mutex_lock(&sess->cmdsn_mutex); | ||
1065 | list_for_each_entry_safe(ooo_cmdsn, ooo_cmdsn_tmp, | ||
1066 | &sess->sess_ooo_cmdsn_list, ooo_list) { | ||
1067 | |||
1068 | list_del(&ooo_cmdsn->ooo_list); | ||
1069 | kmem_cache_free(lio_ooo_cache, ooo_cmdsn); | ||
1070 | } | ||
1071 | mutex_unlock(&sess->cmdsn_mutex); | ||
1072 | } | ||
1073 | |||
1074 | int iscsit_handle_ooo_cmdsn( | ||
1075 | struct iscsi_session *sess, | ||
1076 | struct iscsi_cmd *cmd, | ||
1077 | u32 cmdsn) | ||
1078 | { | ||
1079 | int batch = 0; | ||
1080 | struct iscsi_ooo_cmdsn *ooo_cmdsn = NULL, *ooo_tail = NULL; | ||
1081 | |||
1082 | cmd->deferred_i_state = cmd->i_state; | ||
1083 | cmd->i_state = ISTATE_DEFERRED_CMD; | ||
1084 | cmd->cmd_flags |= ICF_OOO_CMDSN; | ||
1085 | |||
1086 | if (list_empty(&sess->sess_ooo_cmdsn_list)) | ||
1087 | batch = 1; | ||
1088 | else { | ||
1089 | ooo_tail = list_entry(sess->sess_ooo_cmdsn_list.prev, | ||
1090 | typeof(*ooo_tail), ooo_list); | ||
1091 | if (ooo_tail->cmdsn != (cmdsn - 1)) | ||
1092 | batch = 1; | ||
1093 | } | ||
1094 | |||
1095 | ooo_cmdsn = iscsit_allocate_ooo_cmdsn(); | ||
1096 | if (!ooo_cmdsn) | ||
1097 | return CMDSN_ERROR_CANNOT_RECOVER; | ||
1098 | |||
1099 | ooo_cmdsn->cmd = cmd; | ||
1100 | ooo_cmdsn->batch_count = (batch) ? | ||
1101 | (cmdsn - sess->exp_cmd_sn) : 1; | ||
1102 | ooo_cmdsn->cid = cmd->conn->cid; | ||
1103 | ooo_cmdsn->exp_cmdsn = sess->exp_cmd_sn; | ||
1104 | ooo_cmdsn->cmdsn = cmdsn; | ||
1105 | |||
1106 | if (iscsit_attach_ooo_cmdsn(sess, ooo_cmdsn) < 0) { | ||
1107 | kmem_cache_free(lio_ooo_cache, ooo_cmdsn); | ||
1108 | return CMDSN_ERROR_CANNOT_RECOVER; | ||
1109 | } | ||
1110 | |||
1111 | return CMDSN_HIGHER_THAN_EXP; | ||
1112 | } | ||
1113 | |||
1114 | static int iscsit_set_dataout_timeout_values( | ||
1115 | struct iscsi_cmd *cmd, | ||
1116 | u32 *offset, | ||
1117 | u32 *length) | ||
1118 | { | ||
1119 | struct iscsi_conn *conn = cmd->conn; | ||
1120 | struct iscsi_r2t *r2t; | ||
1121 | |||
1122 | if (cmd->unsolicited_data) { | ||
1123 | *offset = 0; | ||
1124 | *length = (conn->sess->sess_ops->FirstBurstLength > | ||
1125 | cmd->data_length) ? | ||
1126 | cmd->data_length : | ||
1127 | conn->sess->sess_ops->FirstBurstLength; | ||
1128 | return 0; | ||
1129 | } | ||
1130 | |||
1131 | spin_lock_bh(&cmd->r2t_lock); | ||
1132 | if (list_empty(&cmd->cmd_r2t_list)) { | ||
1133 | pr_err("cmd->cmd_r2t_list is empty!\n"); | ||
1134 | spin_unlock_bh(&cmd->r2t_lock); | ||
1135 | return -1; | ||
1136 | } | ||
1137 | |||
1138 | list_for_each_entry(r2t, &cmd->cmd_r2t_list, r2t_list) { | ||
1139 | if (r2t->sent_r2t && !r2t->recovery_r2t && !r2t->seq_complete) { | ||
1140 | *offset = r2t->offset; | ||
1141 | *length = r2t->xfer_len; | ||
1142 | spin_unlock_bh(&cmd->r2t_lock); | ||
1143 | return 0; | ||
1144 | } | ||
1145 | } | ||
1146 | spin_unlock_bh(&cmd->r2t_lock); | ||
1147 | |||
1148 | pr_err("Unable to locate any incomplete DataOUT" | ||
1149 | " sequences for ITT: 0x%08x.\n", cmd->init_task_tag); | ||
1150 | |||
1151 | return -1; | ||
1152 | } | ||
1153 | |||
1154 | /* | ||
1155 | * NOTE: Called from interrupt (timer) context. | ||
1156 | */ | ||
1157 | static void iscsit_handle_dataout_timeout(unsigned long data) | ||
1158 | { | ||
1159 | u32 pdu_length = 0, pdu_offset = 0; | ||
1160 | u32 r2t_length = 0, r2t_offset = 0; | ||
1161 | struct iscsi_cmd *cmd = (struct iscsi_cmd *) data; | ||
1162 | struct iscsi_conn *conn = cmd->conn; | ||
1163 | struct iscsi_session *sess = NULL; | ||
1164 | struct iscsi_node_attrib *na; | ||
1165 | |||
1166 | iscsit_inc_conn_usage_count(conn); | ||
1167 | |||
1168 | spin_lock_bh(&cmd->dataout_timeout_lock); | ||
1169 | if (cmd->dataout_timer_flags & ISCSI_TF_STOP) { | ||
1170 | spin_unlock_bh(&cmd->dataout_timeout_lock); | ||
1171 | iscsit_dec_conn_usage_count(conn); | ||
1172 | return; | ||
1173 | } | ||
1174 | cmd->dataout_timer_flags &= ~ISCSI_TF_RUNNING; | ||
1175 | sess = conn->sess; | ||
1176 | na = iscsit_tpg_get_node_attrib(sess); | ||
1177 | |||
1178 | if (!sess->sess_ops->ErrorRecoveryLevel) { | ||
1179 | pr_debug("Unable to recover from DataOut timeout while" | ||
1180 | " in ERL=0.\n"); | ||
1181 | goto failure; | ||
1182 | } | ||
1183 | |||
1184 | if (++cmd->dataout_timeout_retries == na->dataout_timeout_retries) { | ||
1185 | pr_debug("Command ITT: 0x%08x exceeded max retries" | ||
1186 | " for DataOUT timeout %u, closing iSCSI connection.\n", | ||
1187 | cmd->init_task_tag, na->dataout_timeout_retries); | ||
1188 | goto failure; | ||
1189 | } | ||
1190 | |||
1191 | cmd->cmd_flags |= ICF_WITHIN_COMMAND_RECOVERY; | ||
1192 | |||
1193 | if (conn->sess->sess_ops->DataSequenceInOrder) { | ||
1194 | if (conn->sess->sess_ops->DataPDUInOrder) { | ||
1195 | pdu_offset = cmd->write_data_done; | ||
1196 | if ((pdu_offset + (conn->sess->sess_ops->MaxBurstLength - | ||
1197 | cmd->next_burst_len)) > cmd->data_length) | ||
1198 | pdu_length = (cmd->data_length - | ||
1199 | cmd->write_data_done); | ||
1200 | else | ||
1201 | pdu_length = (conn->sess->sess_ops->MaxBurstLength - | ||
1202 | cmd->next_burst_len); | ||
1203 | } else { | ||
1204 | pdu_offset = cmd->seq_start_offset; | ||
1205 | pdu_length = (cmd->seq_end_offset - | ||
1206 | cmd->seq_start_offset); | ||
1207 | } | ||
1208 | } else { | ||
1209 | if (iscsit_set_dataout_timeout_values(cmd, &pdu_offset, | ||
1210 | &pdu_length) < 0) | ||
1211 | goto failure; | ||
1212 | } | ||
1213 | |||
1214 | if (iscsit_recalculate_dataout_values(cmd, pdu_offset, pdu_length, | ||
1215 | &r2t_offset, &r2t_length) < 0) | ||
1216 | goto failure; | ||
1217 | |||
1218 | pr_debug("Command ITT: 0x%08x timed out waiting for" | ||
1219 | " completion of %sDataOUT Sequence Offset: %u, Length: %u\n", | ||
1220 | cmd->init_task_tag, (cmd->unsolicited_data) ? "Unsolicited " : | ||
1221 | "", r2t_offset, r2t_length); | ||
1222 | |||
1223 | if (iscsit_send_recovery_r2t(cmd, r2t_offset, r2t_length) < 0) | ||
1224 | goto failure; | ||
1225 | |||
1226 | iscsit_start_dataout_timer(cmd, conn); | ||
1227 | spin_unlock_bh(&cmd->dataout_timeout_lock); | ||
1228 | iscsit_dec_conn_usage_count(conn); | ||
1229 | |||
1230 | return; | ||
1231 | |||
1232 | failure: | ||
1233 | spin_unlock_bh(&cmd->dataout_timeout_lock); | ||
1234 | iscsit_cause_connection_reinstatement(conn, 0); | ||
1235 | iscsit_dec_conn_usage_count(conn); | ||
1236 | } | ||
1237 | |||
1238 | void iscsit_mod_dataout_timer(struct iscsi_cmd *cmd) | ||
1239 | { | ||
1240 | struct iscsi_conn *conn = cmd->conn; | ||
1241 | struct iscsi_session *sess = conn->sess; | ||
1242 | struct iscsi_node_attrib *na = na = iscsit_tpg_get_node_attrib(sess); | ||
1243 | |||
1244 | spin_lock_bh(&cmd->dataout_timeout_lock); | ||
1245 | if (!(cmd->dataout_timer_flags & ISCSI_TF_RUNNING)) { | ||
1246 | spin_unlock_bh(&cmd->dataout_timeout_lock); | ||
1247 | return; | ||
1248 | } | ||
1249 | |||
1250 | mod_timer(&cmd->dataout_timer, | ||
1251 | (get_jiffies_64() + na->dataout_timeout * HZ)); | ||
1252 | pr_debug("Updated DataOUT timer for ITT: 0x%08x", | ||
1253 | cmd->init_task_tag); | ||
1254 | spin_unlock_bh(&cmd->dataout_timeout_lock); | ||
1255 | } | ||
1256 | |||
1257 | /* | ||
1258 | * Called with cmd->dataout_timeout_lock held. | ||
1259 | */ | ||
1260 | void iscsit_start_dataout_timer( | ||
1261 | struct iscsi_cmd *cmd, | ||
1262 | struct iscsi_conn *conn) | ||
1263 | { | ||
1264 | struct iscsi_session *sess = conn->sess; | ||
1265 | struct iscsi_node_attrib *na = na = iscsit_tpg_get_node_attrib(sess); | ||
1266 | |||
1267 | if (cmd->dataout_timer_flags & ISCSI_TF_RUNNING) | ||
1268 | return; | ||
1269 | |||
1270 | pr_debug("Starting DataOUT timer for ITT: 0x%08x on" | ||
1271 | " CID: %hu.\n", cmd->init_task_tag, conn->cid); | ||
1272 | |||
1273 | init_timer(&cmd->dataout_timer); | ||
1274 | cmd->dataout_timer.expires = (get_jiffies_64() + na->dataout_timeout * HZ); | ||
1275 | cmd->dataout_timer.data = (unsigned long)cmd; | ||
1276 | cmd->dataout_timer.function = iscsit_handle_dataout_timeout; | ||
1277 | cmd->dataout_timer_flags &= ~ISCSI_TF_STOP; | ||
1278 | cmd->dataout_timer_flags |= ISCSI_TF_RUNNING; | ||
1279 | add_timer(&cmd->dataout_timer); | ||
1280 | } | ||
1281 | |||
1282 | void iscsit_stop_dataout_timer(struct iscsi_cmd *cmd) | ||
1283 | { | ||
1284 | spin_lock_bh(&cmd->dataout_timeout_lock); | ||
1285 | if (!(cmd->dataout_timer_flags & ISCSI_TF_RUNNING)) { | ||
1286 | spin_unlock_bh(&cmd->dataout_timeout_lock); | ||
1287 | return; | ||
1288 | } | ||
1289 | cmd->dataout_timer_flags |= ISCSI_TF_STOP; | ||
1290 | spin_unlock_bh(&cmd->dataout_timeout_lock); | ||
1291 | |||
1292 | del_timer_sync(&cmd->dataout_timer); | ||
1293 | |||
1294 | spin_lock_bh(&cmd->dataout_timeout_lock); | ||
1295 | cmd->dataout_timer_flags &= ~ISCSI_TF_RUNNING; | ||
1296 | pr_debug("Stopped DataOUT Timer for ITT: 0x%08x\n", | ||
1297 | cmd->init_task_tag); | ||
1298 | spin_unlock_bh(&cmd->dataout_timeout_lock); | ||
1299 | } | ||
diff --git a/drivers/target/iscsi/iscsi_target_erl1.h b/drivers/target/iscsi/iscsi_target_erl1.h new file mode 100644 index 000000000000..85e67e29de6b --- /dev/null +++ b/drivers/target/iscsi/iscsi_target_erl1.h | |||
@@ -0,0 +1,26 @@ | |||
1 | #ifndef ISCSI_TARGET_ERL1_H | ||
2 | #define ISCSI_TARGET_ERL1_H | ||
3 | |||
4 | extern int iscsit_dump_data_payload(struct iscsi_conn *, u32, int); | ||
5 | extern int iscsit_create_recovery_datain_values_datasequenceinorder_yes( | ||
6 | struct iscsi_cmd *, struct iscsi_datain_req *); | ||
7 | extern int iscsit_create_recovery_datain_values_datasequenceinorder_no( | ||
8 | struct iscsi_cmd *, struct iscsi_datain_req *); | ||
9 | extern int iscsit_handle_recovery_datain_or_r2t(struct iscsi_conn *, unsigned char *, | ||
10 | u32, u32, u32, u32); | ||
11 | extern int iscsit_handle_status_snack(struct iscsi_conn *, u32, u32, | ||
12 | u32, u32); | ||
13 | extern int iscsit_handle_data_ack(struct iscsi_conn *, u32, u32, u32); | ||
14 | extern int iscsit_dataout_datapduinorder_no_fbit(struct iscsi_cmd *, struct iscsi_pdu *); | ||
15 | extern int iscsit_recover_dataout_sequence(struct iscsi_cmd *, u32, u32); | ||
16 | extern void iscsit_clear_ooo_cmdsns_for_conn(struct iscsi_conn *); | ||
17 | extern void iscsit_free_all_ooo_cmdsns(struct iscsi_session *); | ||
18 | extern int iscsit_execute_ooo_cmdsns(struct iscsi_session *); | ||
19 | extern int iscsit_execute_cmd(struct iscsi_cmd *, int); | ||
20 | extern int iscsit_handle_ooo_cmdsn(struct iscsi_session *, struct iscsi_cmd *, u32); | ||
21 | extern void iscsit_remove_ooo_cmdsn(struct iscsi_session *, struct iscsi_ooo_cmdsn *); | ||
22 | extern void iscsit_mod_dataout_timer(struct iscsi_cmd *); | ||
23 | extern void iscsit_start_dataout_timer(struct iscsi_cmd *, struct iscsi_conn *); | ||
24 | extern void iscsit_stop_dataout_timer(struct iscsi_cmd *); | ||
25 | |||
26 | #endif /* ISCSI_TARGET_ERL1_H */ | ||
diff --git a/drivers/target/iscsi/iscsi_target_erl2.c b/drivers/target/iscsi/iscsi_target_erl2.c new file mode 100644 index 000000000000..91a4d170bda4 --- /dev/null +++ b/drivers/target/iscsi/iscsi_target_erl2.c | |||
@@ -0,0 +1,474 @@ | |||
1 | /******************************************************************************* | ||
2 | * This file contains error recovery level two functions used by | ||
3 | * the iSCSI Target driver. | ||
4 | * | ||
5 | * \u00a9 Copyright 2007-2011 RisingTide Systems LLC. | ||
6 | * | ||
7 | * Licensed to the Linux Foundation under the General Public License (GPL) version 2. | ||
8 | * | ||
9 | * Author: Nicholas A. Bellinger <nab@linux-iscsi.org> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or | ||
14 | * (at your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | * GNU General Public License for more details. | ||
20 | ******************************************************************************/ | ||
21 | |||
22 | #include <scsi/iscsi_proto.h> | ||
23 | #include <target/target_core_base.h> | ||
24 | #include <target/target_core_transport.h> | ||
25 | |||
26 | #include "iscsi_target_core.h" | ||
27 | #include "iscsi_target_datain_values.h" | ||
28 | #include "iscsi_target_util.h" | ||
29 | #include "iscsi_target_erl0.h" | ||
30 | #include "iscsi_target_erl1.h" | ||
31 | #include "iscsi_target_erl2.h" | ||
32 | #include "iscsi_target.h" | ||
33 | |||
34 | /* | ||
35 | * FIXME: Does RData SNACK apply here as well? | ||
36 | */ | ||
37 | void iscsit_create_conn_recovery_datain_values( | ||
38 | struct iscsi_cmd *cmd, | ||
39 | u32 exp_data_sn) | ||
40 | { | ||
41 | u32 data_sn = 0; | ||
42 | struct iscsi_conn *conn = cmd->conn; | ||
43 | |||
44 | cmd->next_burst_len = 0; | ||
45 | cmd->read_data_done = 0; | ||
46 | |||
47 | while (exp_data_sn > data_sn) { | ||
48 | if ((cmd->next_burst_len + | ||
49 | conn->conn_ops->MaxRecvDataSegmentLength) < | ||
50 | conn->sess->sess_ops->MaxBurstLength) { | ||
51 | cmd->read_data_done += | ||
52 | conn->conn_ops->MaxRecvDataSegmentLength; | ||
53 | cmd->next_burst_len += | ||
54 | conn->conn_ops->MaxRecvDataSegmentLength; | ||
55 | } else { | ||
56 | cmd->read_data_done += | ||
57 | (conn->sess->sess_ops->MaxBurstLength - | ||
58 | cmd->next_burst_len); | ||
59 | cmd->next_burst_len = 0; | ||
60 | } | ||
61 | data_sn++; | ||
62 | } | ||
63 | } | ||
64 | |||
65 | void iscsit_create_conn_recovery_dataout_values( | ||
66 | struct iscsi_cmd *cmd) | ||
67 | { | ||
68 | u32 write_data_done = 0; | ||
69 | struct iscsi_conn *conn = cmd->conn; | ||
70 | |||
71 | cmd->data_sn = 0; | ||
72 | cmd->next_burst_len = 0; | ||
73 | |||
74 | while (cmd->write_data_done > write_data_done) { | ||
75 | if ((write_data_done + conn->sess->sess_ops->MaxBurstLength) <= | ||
76 | cmd->write_data_done) | ||
77 | write_data_done += conn->sess->sess_ops->MaxBurstLength; | ||
78 | else | ||
79 | break; | ||
80 | } | ||
81 | |||
82 | cmd->write_data_done = write_data_done; | ||
83 | } | ||
84 | |||
85 | static int iscsit_attach_active_connection_recovery_entry( | ||
86 | struct iscsi_session *sess, | ||
87 | struct iscsi_conn_recovery *cr) | ||
88 | { | ||
89 | spin_lock(&sess->cr_a_lock); | ||
90 | list_add_tail(&cr->cr_list, &sess->cr_active_list); | ||
91 | spin_unlock(&sess->cr_a_lock); | ||
92 | |||
93 | return 0; | ||
94 | } | ||
95 | |||
96 | static int iscsit_attach_inactive_connection_recovery_entry( | ||
97 | struct iscsi_session *sess, | ||
98 | struct iscsi_conn_recovery *cr) | ||
99 | { | ||
100 | spin_lock(&sess->cr_i_lock); | ||
101 | list_add_tail(&cr->cr_list, &sess->cr_inactive_list); | ||
102 | |||
103 | sess->conn_recovery_count++; | ||
104 | pr_debug("Incremented connection recovery count to %u for" | ||
105 | " SID: %u\n", sess->conn_recovery_count, sess->sid); | ||
106 | spin_unlock(&sess->cr_i_lock); | ||
107 | |||
108 | return 0; | ||
109 | } | ||
110 | |||
111 | struct iscsi_conn_recovery *iscsit_get_inactive_connection_recovery_entry( | ||
112 | struct iscsi_session *sess, | ||
113 | u16 cid) | ||
114 | { | ||
115 | struct iscsi_conn_recovery *cr; | ||
116 | |||
117 | spin_lock(&sess->cr_i_lock); | ||
118 | list_for_each_entry(cr, &sess->cr_inactive_list, cr_list) { | ||
119 | if (cr->cid == cid) { | ||
120 | spin_unlock(&sess->cr_i_lock); | ||
121 | return cr; | ||
122 | } | ||
123 | } | ||
124 | spin_unlock(&sess->cr_i_lock); | ||
125 | |||
126 | return NULL; | ||
127 | } | ||
128 | |||
129 | void iscsit_free_connection_recovery_entires(struct iscsi_session *sess) | ||
130 | { | ||
131 | struct iscsi_cmd *cmd, *cmd_tmp; | ||
132 | struct iscsi_conn_recovery *cr, *cr_tmp; | ||
133 | |||
134 | spin_lock(&sess->cr_a_lock); | ||
135 | list_for_each_entry_safe(cr, cr_tmp, &sess->cr_active_list, cr_list) { | ||
136 | list_del(&cr->cr_list); | ||
137 | spin_unlock(&sess->cr_a_lock); | ||
138 | |||
139 | spin_lock(&cr->conn_recovery_cmd_lock); | ||
140 | list_for_each_entry_safe(cmd, cmd_tmp, | ||
141 | &cr->conn_recovery_cmd_list, i_list) { | ||
142 | |||
143 | list_del(&cmd->i_list); | ||
144 | cmd->conn = NULL; | ||
145 | spin_unlock(&cr->conn_recovery_cmd_lock); | ||
146 | if (!(cmd->se_cmd.se_cmd_flags & SCF_SE_LUN_CMD) || | ||
147 | !(cmd->se_cmd.transport_wait_for_tasks)) | ||
148 | iscsit_release_cmd(cmd); | ||
149 | else | ||
150 | cmd->se_cmd.transport_wait_for_tasks( | ||
151 | &cmd->se_cmd, 1, 1); | ||
152 | spin_lock(&cr->conn_recovery_cmd_lock); | ||
153 | } | ||
154 | spin_unlock(&cr->conn_recovery_cmd_lock); | ||
155 | spin_lock(&sess->cr_a_lock); | ||
156 | |||
157 | kfree(cr); | ||
158 | } | ||
159 | spin_unlock(&sess->cr_a_lock); | ||
160 | |||
161 | spin_lock(&sess->cr_i_lock); | ||
162 | list_for_each_entry_safe(cr, cr_tmp, &sess->cr_inactive_list, cr_list) { | ||
163 | list_del(&cr->cr_list); | ||
164 | spin_unlock(&sess->cr_i_lock); | ||
165 | |||
166 | spin_lock(&cr->conn_recovery_cmd_lock); | ||
167 | list_for_each_entry_safe(cmd, cmd_tmp, | ||
168 | &cr->conn_recovery_cmd_list, i_list) { | ||
169 | |||
170 | list_del(&cmd->i_list); | ||
171 | cmd->conn = NULL; | ||
172 | spin_unlock(&cr->conn_recovery_cmd_lock); | ||
173 | if (!(cmd->se_cmd.se_cmd_flags & SCF_SE_LUN_CMD) || | ||
174 | !(cmd->se_cmd.transport_wait_for_tasks)) | ||
175 | iscsit_release_cmd(cmd); | ||
176 | else | ||
177 | cmd->se_cmd.transport_wait_for_tasks( | ||
178 | &cmd->se_cmd, 1, 1); | ||
179 | spin_lock(&cr->conn_recovery_cmd_lock); | ||
180 | } | ||
181 | spin_unlock(&cr->conn_recovery_cmd_lock); | ||
182 | spin_lock(&sess->cr_i_lock); | ||
183 | |||
184 | kfree(cr); | ||
185 | } | ||
186 | spin_unlock(&sess->cr_i_lock); | ||
187 | } | ||
188 | |||
189 | int iscsit_remove_active_connection_recovery_entry( | ||
190 | struct iscsi_conn_recovery *cr, | ||
191 | struct iscsi_session *sess) | ||
192 | { | ||
193 | spin_lock(&sess->cr_a_lock); | ||
194 | list_del(&cr->cr_list); | ||
195 | |||
196 | sess->conn_recovery_count--; | ||
197 | pr_debug("Decremented connection recovery count to %u for" | ||
198 | " SID: %u\n", sess->conn_recovery_count, sess->sid); | ||
199 | spin_unlock(&sess->cr_a_lock); | ||
200 | |||
201 | kfree(cr); | ||
202 | |||
203 | return 0; | ||
204 | } | ||
205 | |||
206 | int iscsit_remove_inactive_connection_recovery_entry( | ||
207 | struct iscsi_conn_recovery *cr, | ||
208 | struct iscsi_session *sess) | ||
209 | { | ||
210 | spin_lock(&sess->cr_i_lock); | ||
211 | list_del(&cr->cr_list); | ||
212 | spin_unlock(&sess->cr_i_lock); | ||
213 | |||
214 | return 0; | ||
215 | } | ||
216 | |||
217 | /* | ||
218 | * Called with cr->conn_recovery_cmd_lock help. | ||
219 | */ | ||
220 | int iscsit_remove_cmd_from_connection_recovery( | ||
221 | struct iscsi_cmd *cmd, | ||
222 | struct iscsi_session *sess) | ||
223 | { | ||
224 | struct iscsi_conn_recovery *cr; | ||
225 | |||
226 | if (!cmd->cr) { | ||
227 | pr_err("struct iscsi_conn_recovery pointer for ITT: 0x%08x" | ||
228 | " is NULL!\n", cmd->init_task_tag); | ||
229 | BUG(); | ||
230 | } | ||
231 | cr = cmd->cr; | ||
232 | |||
233 | list_del(&cmd->i_list); | ||
234 | return --cr->cmd_count; | ||
235 | } | ||
236 | |||
237 | void iscsit_discard_cr_cmds_by_expstatsn( | ||
238 | struct iscsi_conn_recovery *cr, | ||
239 | u32 exp_statsn) | ||
240 | { | ||
241 | u32 dropped_count = 0; | ||
242 | struct iscsi_cmd *cmd, *cmd_tmp; | ||
243 | struct iscsi_session *sess = cr->sess; | ||
244 | |||
245 | spin_lock(&cr->conn_recovery_cmd_lock); | ||
246 | list_for_each_entry_safe(cmd, cmd_tmp, | ||
247 | &cr->conn_recovery_cmd_list, i_list) { | ||
248 | |||
249 | if (((cmd->deferred_i_state != ISTATE_SENT_STATUS) && | ||
250 | (cmd->deferred_i_state != ISTATE_REMOVE)) || | ||
251 | (cmd->stat_sn >= exp_statsn)) { | ||
252 | continue; | ||
253 | } | ||
254 | |||
255 | dropped_count++; | ||
256 | pr_debug("Dropping Acknowledged ITT: 0x%08x, StatSN:" | ||
257 | " 0x%08x, CID: %hu.\n", cmd->init_task_tag, | ||
258 | cmd->stat_sn, cr->cid); | ||
259 | |||
260 | iscsit_remove_cmd_from_connection_recovery(cmd, sess); | ||
261 | |||
262 | spin_unlock(&cr->conn_recovery_cmd_lock); | ||
263 | if (!(cmd->se_cmd.se_cmd_flags & SCF_SE_LUN_CMD) || | ||
264 | !(cmd->se_cmd.transport_wait_for_tasks)) | ||
265 | iscsit_release_cmd(cmd); | ||
266 | else | ||
267 | cmd->se_cmd.transport_wait_for_tasks( | ||
268 | &cmd->se_cmd, 1, 0); | ||
269 | spin_lock(&cr->conn_recovery_cmd_lock); | ||
270 | } | ||
271 | spin_unlock(&cr->conn_recovery_cmd_lock); | ||
272 | |||
273 | pr_debug("Dropped %u total acknowledged commands on" | ||
274 | " CID: %hu less than old ExpStatSN: 0x%08x\n", | ||
275 | dropped_count, cr->cid, exp_statsn); | ||
276 | |||
277 | if (!cr->cmd_count) { | ||
278 | pr_debug("No commands to be reassigned for failed" | ||
279 | " connection CID: %hu on SID: %u\n", | ||
280 | cr->cid, sess->sid); | ||
281 | iscsit_remove_inactive_connection_recovery_entry(cr, sess); | ||
282 | iscsit_attach_active_connection_recovery_entry(sess, cr); | ||
283 | pr_debug("iSCSI connection recovery successful for CID:" | ||
284 | " %hu on SID: %u\n", cr->cid, sess->sid); | ||
285 | iscsit_remove_active_connection_recovery_entry(cr, sess); | ||
286 | } else { | ||
287 | iscsit_remove_inactive_connection_recovery_entry(cr, sess); | ||
288 | iscsit_attach_active_connection_recovery_entry(sess, cr); | ||
289 | } | ||
290 | } | ||
291 | |||
292 | int iscsit_discard_unacknowledged_ooo_cmdsns_for_conn(struct iscsi_conn *conn) | ||
293 | { | ||
294 | u32 dropped_count = 0; | ||
295 | struct iscsi_cmd *cmd, *cmd_tmp; | ||
296 | struct iscsi_ooo_cmdsn *ooo_cmdsn, *ooo_cmdsn_tmp; | ||
297 | struct iscsi_session *sess = conn->sess; | ||
298 | |||
299 | mutex_lock(&sess->cmdsn_mutex); | ||
300 | list_for_each_entry_safe(ooo_cmdsn, ooo_cmdsn_tmp, | ||
301 | &sess->sess_ooo_cmdsn_list, ooo_list) { | ||
302 | |||
303 | if (ooo_cmdsn->cid != conn->cid) | ||
304 | continue; | ||
305 | |||
306 | dropped_count++; | ||
307 | pr_debug("Dropping unacknowledged CmdSN:" | ||
308 | " 0x%08x during connection recovery on CID: %hu\n", | ||
309 | ooo_cmdsn->cmdsn, conn->cid); | ||
310 | iscsit_remove_ooo_cmdsn(sess, ooo_cmdsn); | ||
311 | } | ||
312 | mutex_unlock(&sess->cmdsn_mutex); | ||
313 | |||
314 | spin_lock_bh(&conn->cmd_lock); | ||
315 | list_for_each_entry_safe(cmd, cmd_tmp, &conn->conn_cmd_list, i_list) { | ||
316 | if (!(cmd->cmd_flags & ICF_OOO_CMDSN)) | ||
317 | continue; | ||
318 | |||
319 | list_del(&cmd->i_list); | ||
320 | |||
321 | spin_unlock_bh(&conn->cmd_lock); | ||
322 | if (!(cmd->se_cmd.se_cmd_flags & SCF_SE_LUN_CMD) || | ||
323 | !(cmd->se_cmd.transport_wait_for_tasks)) | ||
324 | iscsit_release_cmd(cmd); | ||
325 | else | ||
326 | cmd->se_cmd.transport_wait_for_tasks( | ||
327 | &cmd->se_cmd, 1, 1); | ||
328 | spin_lock_bh(&conn->cmd_lock); | ||
329 | } | ||
330 | spin_unlock_bh(&conn->cmd_lock); | ||
331 | |||
332 | pr_debug("Dropped %u total unacknowledged commands on CID:" | ||
333 | " %hu for ExpCmdSN: 0x%08x.\n", dropped_count, conn->cid, | ||
334 | sess->exp_cmd_sn); | ||
335 | return 0; | ||
336 | } | ||
337 | |||
338 | int iscsit_prepare_cmds_for_realligance(struct iscsi_conn *conn) | ||
339 | { | ||
340 | u32 cmd_count = 0; | ||
341 | struct iscsi_cmd *cmd, *cmd_tmp; | ||
342 | struct iscsi_conn_recovery *cr; | ||
343 | |||
344 | /* | ||
345 | * Allocate an struct iscsi_conn_recovery for this connection. | ||
346 | * Each struct iscsi_cmd contains an struct iscsi_conn_recovery pointer | ||
347 | * (struct iscsi_cmd->cr) so we need to allocate this before preparing the | ||
348 | * connection's command list for connection recovery. | ||
349 | */ | ||
350 | cr = kzalloc(sizeof(struct iscsi_conn_recovery), GFP_KERNEL); | ||
351 | if (!cr) { | ||
352 | pr_err("Unable to allocate memory for" | ||
353 | " struct iscsi_conn_recovery.\n"); | ||
354 | return -1; | ||
355 | } | ||
356 | INIT_LIST_HEAD(&cr->cr_list); | ||
357 | INIT_LIST_HEAD(&cr->conn_recovery_cmd_list); | ||
358 | spin_lock_init(&cr->conn_recovery_cmd_lock); | ||
359 | /* | ||
360 | * Only perform connection recovery on ISCSI_OP_SCSI_CMD or | ||
361 | * ISCSI_OP_NOOP_OUT opcodes. For all other opcodes call | ||
362 | * list_del(&cmd->i_list); to release the command to the | ||
363 | * session pool and remove it from the connection's list. | ||
364 | * | ||
365 | * Also stop the DataOUT timer, which will be restarted after | ||
366 | * sending the TMR response. | ||
367 | */ | ||
368 | spin_lock_bh(&conn->cmd_lock); | ||
369 | list_for_each_entry_safe(cmd, cmd_tmp, &conn->conn_cmd_list, i_list) { | ||
370 | |||
371 | if ((cmd->iscsi_opcode != ISCSI_OP_SCSI_CMD) && | ||
372 | (cmd->iscsi_opcode != ISCSI_OP_NOOP_OUT)) { | ||
373 | pr_debug("Not performing realligence on" | ||
374 | " Opcode: 0x%02x, ITT: 0x%08x, CmdSN: 0x%08x," | ||
375 | " CID: %hu\n", cmd->iscsi_opcode, | ||
376 | cmd->init_task_tag, cmd->cmd_sn, conn->cid); | ||
377 | |||
378 | list_del(&cmd->i_list); | ||
379 | spin_unlock_bh(&conn->cmd_lock); | ||
380 | |||
381 | if (!(cmd->se_cmd.se_cmd_flags & SCF_SE_LUN_CMD) || | ||
382 | !(cmd->se_cmd.transport_wait_for_tasks)) | ||
383 | iscsit_release_cmd(cmd); | ||
384 | else | ||
385 | cmd->se_cmd.transport_wait_for_tasks( | ||
386 | &cmd->se_cmd, 1, 0); | ||
387 | spin_lock_bh(&conn->cmd_lock); | ||
388 | continue; | ||
389 | } | ||
390 | |||
391 | /* | ||
392 | * Special case where commands greater than or equal to | ||
393 | * the session's ExpCmdSN are attached to the connection | ||
394 | * list but not to the out of order CmdSN list. The one | ||
395 | * obvious case is when a command with immediate data | ||
396 | * attached must only check the CmdSN against ExpCmdSN | ||
397 | * after the data is received. The special case below | ||
398 | * is when the connection fails before data is received, | ||
399 | * but also may apply to other PDUs, so it has been | ||
400 | * made generic here. | ||
401 | */ | ||
402 | if (!(cmd->cmd_flags & ICF_OOO_CMDSN) && !cmd->immediate_cmd && | ||
403 | (cmd->cmd_sn >= conn->sess->exp_cmd_sn)) { | ||
404 | list_del(&cmd->i_list); | ||
405 | spin_unlock_bh(&conn->cmd_lock); | ||
406 | |||
407 | if (!(cmd->se_cmd.se_cmd_flags & SCF_SE_LUN_CMD) || | ||
408 | !(cmd->se_cmd.transport_wait_for_tasks)) | ||
409 | iscsit_release_cmd(cmd); | ||
410 | else | ||
411 | cmd->se_cmd.transport_wait_for_tasks( | ||
412 | &cmd->se_cmd, 1, 1); | ||
413 | spin_lock_bh(&conn->cmd_lock); | ||
414 | continue; | ||
415 | } | ||
416 | |||
417 | cmd_count++; | ||
418 | pr_debug("Preparing Opcode: 0x%02x, ITT: 0x%08x," | ||
419 | " CmdSN: 0x%08x, StatSN: 0x%08x, CID: %hu for" | ||
420 | " realligence.\n", cmd->iscsi_opcode, | ||
421 | cmd->init_task_tag, cmd->cmd_sn, cmd->stat_sn, | ||
422 | conn->cid); | ||
423 | |||
424 | cmd->deferred_i_state = cmd->i_state; | ||
425 | cmd->i_state = ISTATE_IN_CONNECTION_RECOVERY; | ||
426 | |||
427 | if (cmd->data_direction == DMA_TO_DEVICE) | ||
428 | iscsit_stop_dataout_timer(cmd); | ||
429 | |||
430 | cmd->sess = conn->sess; | ||
431 | |||
432 | list_del(&cmd->i_list); | ||
433 | spin_unlock_bh(&conn->cmd_lock); | ||
434 | |||
435 | iscsit_free_all_datain_reqs(cmd); | ||
436 | |||
437 | if ((cmd->se_cmd.se_cmd_flags & SCF_SE_LUN_CMD) && | ||
438 | cmd->se_cmd.transport_wait_for_tasks) | ||
439 | cmd->se_cmd.transport_wait_for_tasks(&cmd->se_cmd, | ||
440 | 0, 0); | ||
441 | /* | ||
442 | * Add the struct iscsi_cmd to the connection recovery cmd list | ||
443 | */ | ||
444 | spin_lock(&cr->conn_recovery_cmd_lock); | ||
445 | list_add_tail(&cmd->i_list, &cr->conn_recovery_cmd_list); | ||
446 | spin_unlock(&cr->conn_recovery_cmd_lock); | ||
447 | |||
448 | spin_lock_bh(&conn->cmd_lock); | ||
449 | cmd->cr = cr; | ||
450 | cmd->conn = NULL; | ||
451 | } | ||
452 | spin_unlock_bh(&conn->cmd_lock); | ||
453 | /* | ||
454 | * Fill in the various values in the preallocated struct iscsi_conn_recovery. | ||
455 | */ | ||
456 | cr->cid = conn->cid; | ||
457 | cr->cmd_count = cmd_count; | ||
458 | cr->maxrecvdatasegmentlength = conn->conn_ops->MaxRecvDataSegmentLength; | ||
459 | cr->sess = conn->sess; | ||
460 | |||
461 | iscsit_attach_inactive_connection_recovery_entry(conn->sess, cr); | ||
462 | |||
463 | return 0; | ||
464 | } | ||
465 | |||
466 | int iscsit_connection_recovery_transport_reset(struct iscsi_conn *conn) | ||
467 | { | ||
468 | atomic_set(&conn->connection_recovery, 1); | ||
469 | |||
470 | if (iscsit_close_connection(conn) < 0) | ||
471 | return -1; | ||
472 | |||
473 | return 0; | ||
474 | } | ||
diff --git a/drivers/target/iscsi/iscsi_target_erl2.h b/drivers/target/iscsi/iscsi_target_erl2.h new file mode 100644 index 000000000000..22f8d24780a6 --- /dev/null +++ b/drivers/target/iscsi/iscsi_target_erl2.h | |||
@@ -0,0 +1,18 @@ | |||
1 | #ifndef ISCSI_TARGET_ERL2_H | ||
2 | #define ISCSI_TARGET_ERL2_H | ||
3 | |||
4 | extern void iscsit_create_conn_recovery_datain_values(struct iscsi_cmd *, u32); | ||
5 | extern void iscsit_create_conn_recovery_dataout_values(struct iscsi_cmd *); | ||
6 | extern struct iscsi_conn_recovery *iscsit_get_inactive_connection_recovery_entry( | ||
7 | struct iscsi_session *, u16); | ||
8 | extern void iscsit_free_connection_recovery_entires(struct iscsi_session *); | ||
9 | extern int iscsit_remove_active_connection_recovery_entry( | ||
10 | struct iscsi_conn_recovery *, struct iscsi_session *); | ||
11 | extern int iscsit_remove_cmd_from_connection_recovery(struct iscsi_cmd *, | ||
12 | struct iscsi_session *); | ||
13 | extern void iscsit_discard_cr_cmds_by_expstatsn(struct iscsi_conn_recovery *, u32); | ||
14 | extern int iscsit_discard_unacknowledged_ooo_cmdsns_for_conn(struct iscsi_conn *); | ||
15 | extern int iscsit_prepare_cmds_for_realligance(struct iscsi_conn *); | ||
16 | extern int iscsit_connection_recovery_transport_reset(struct iscsi_conn *); | ||
17 | |||
18 | #endif /*** ISCSI_TARGET_ERL2_H ***/ | ||
diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c new file mode 100644 index 000000000000..bcaf82f47037 --- /dev/null +++ b/drivers/target/iscsi/iscsi_target_login.c | |||
@@ -0,0 +1,1232 @@ | |||
1 | /******************************************************************************* | ||
2 | * This file contains the login functions used by the iSCSI Target driver. | ||
3 | * | ||
4 | * \u00a9 Copyright 2007-2011 RisingTide Systems LLC. | ||
5 | * | ||
6 | * Licensed to the Linux Foundation under the General Public License (GPL) version 2. | ||
7 | * | ||
8 | * Author: Nicholas A. Bellinger <nab@linux-iscsi.org> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | ******************************************************************************/ | ||
20 | |||
21 | #include <linux/string.h> | ||
22 | #include <linux/kthread.h> | ||
23 | #include <linux/crypto.h> | ||
24 | #include <scsi/iscsi_proto.h> | ||
25 | #include <target/target_core_base.h> | ||
26 | #include <target/target_core_transport.h> | ||
27 | |||
28 | #include "iscsi_target_core.h" | ||
29 | #include "iscsi_target_tq.h" | ||
30 | #include "iscsi_target_device.h" | ||
31 | #include "iscsi_target_nego.h" | ||
32 | #include "iscsi_target_erl0.h" | ||
33 | #include "iscsi_target_erl2.h" | ||
34 | #include "iscsi_target_login.h" | ||
35 | #include "iscsi_target_stat.h" | ||
36 | #include "iscsi_target_tpg.h" | ||
37 | #include "iscsi_target_util.h" | ||
38 | #include "iscsi_target.h" | ||
39 | #include "iscsi_target_parameters.h" | ||
40 | |||
41 | extern struct idr sess_idr; | ||
42 | extern struct mutex auth_id_lock; | ||
43 | extern spinlock_t sess_idr_lock; | ||
44 | |||
45 | static int iscsi_login_init_conn(struct iscsi_conn *conn) | ||
46 | { | ||
47 | INIT_LIST_HEAD(&conn->conn_list); | ||
48 | INIT_LIST_HEAD(&conn->conn_cmd_list); | ||
49 | INIT_LIST_HEAD(&conn->immed_queue_list); | ||
50 | INIT_LIST_HEAD(&conn->response_queue_list); | ||
51 | init_completion(&conn->conn_post_wait_comp); | ||
52 | init_completion(&conn->conn_wait_comp); | ||
53 | init_completion(&conn->conn_wait_rcfr_comp); | ||
54 | init_completion(&conn->conn_waiting_on_uc_comp); | ||
55 | init_completion(&conn->conn_logout_comp); | ||
56 | init_completion(&conn->rx_half_close_comp); | ||
57 | init_completion(&conn->tx_half_close_comp); | ||
58 | spin_lock_init(&conn->cmd_lock); | ||
59 | spin_lock_init(&conn->conn_usage_lock); | ||
60 | spin_lock_init(&conn->immed_queue_lock); | ||
61 | spin_lock_init(&conn->nopin_timer_lock); | ||
62 | spin_lock_init(&conn->response_queue_lock); | ||
63 | spin_lock_init(&conn->state_lock); | ||
64 | |||
65 | if (!zalloc_cpumask_var(&conn->conn_cpumask, GFP_KERNEL)) { | ||
66 | pr_err("Unable to allocate conn->conn_cpumask\n"); | ||
67 | return -ENOMEM; | ||
68 | } | ||
69 | |||
70 | return 0; | ||
71 | } | ||
72 | |||
73 | /* | ||
74 | * Used by iscsi_target_nego.c:iscsi_target_locate_portal() to setup | ||
75 | * per struct iscsi_conn libcrypto contexts for crc32c and crc32-intel | ||
76 | */ | ||
77 | int iscsi_login_setup_crypto(struct iscsi_conn *conn) | ||
78 | { | ||
79 | /* | ||
80 | * Setup slicing by CRC32C algorithm for RX and TX libcrypto contexts | ||
81 | * which will default to crc32c_intel.ko for cpu_has_xmm4_2, or fallback | ||
82 | * to software 1x8 byte slicing from crc32c.ko | ||
83 | */ | ||
84 | conn->conn_rx_hash.flags = 0; | ||
85 | conn->conn_rx_hash.tfm = crypto_alloc_hash("crc32c", 0, | ||
86 | CRYPTO_ALG_ASYNC); | ||
87 | if (IS_ERR(conn->conn_rx_hash.tfm)) { | ||
88 | pr_err("crypto_alloc_hash() failed for conn_rx_tfm\n"); | ||
89 | return -ENOMEM; | ||
90 | } | ||
91 | |||
92 | conn->conn_tx_hash.flags = 0; | ||
93 | conn->conn_tx_hash.tfm = crypto_alloc_hash("crc32c", 0, | ||
94 | CRYPTO_ALG_ASYNC); | ||
95 | if (IS_ERR(conn->conn_tx_hash.tfm)) { | ||
96 | pr_err("crypto_alloc_hash() failed for conn_tx_tfm\n"); | ||
97 | crypto_free_hash(conn->conn_rx_hash.tfm); | ||
98 | return -ENOMEM; | ||
99 | } | ||
100 | |||
101 | return 0; | ||
102 | } | ||
103 | |||
104 | static int iscsi_login_check_initiator_version( | ||
105 | struct iscsi_conn *conn, | ||
106 | u8 version_max, | ||
107 | u8 version_min) | ||
108 | { | ||
109 | if ((version_max != 0x00) || (version_min != 0x00)) { | ||
110 | pr_err("Unsupported iSCSI IETF Pre-RFC Revision," | ||
111 | " version Min/Max 0x%02x/0x%02x, rejecting login.\n", | ||
112 | version_min, version_max); | ||
113 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, | ||
114 | ISCSI_LOGIN_STATUS_NO_VERSION); | ||
115 | return -1; | ||
116 | } | ||
117 | |||
118 | return 0; | ||
119 | } | ||
120 | |||
121 | int iscsi_check_for_session_reinstatement(struct iscsi_conn *conn) | ||
122 | { | ||
123 | int sessiontype; | ||
124 | struct iscsi_param *initiatorname_param = NULL, *sessiontype_param = NULL; | ||
125 | struct iscsi_portal_group *tpg = conn->tpg; | ||
126 | struct iscsi_session *sess = NULL, *sess_p = NULL; | ||
127 | struct se_portal_group *se_tpg = &tpg->tpg_se_tpg; | ||
128 | struct se_session *se_sess, *se_sess_tmp; | ||
129 | |||
130 | initiatorname_param = iscsi_find_param_from_key( | ||
131 | INITIATORNAME, conn->param_list); | ||
132 | if (!initiatorname_param) | ||
133 | return -1; | ||
134 | |||
135 | sessiontype_param = iscsi_find_param_from_key( | ||
136 | SESSIONTYPE, conn->param_list); | ||
137 | if (!sessiontype_param) | ||
138 | return -1; | ||
139 | |||
140 | sessiontype = (strncmp(sessiontype_param->value, NORMAL, 6)) ? 1 : 0; | ||
141 | |||
142 | spin_lock_bh(&se_tpg->session_lock); | ||
143 | list_for_each_entry_safe(se_sess, se_sess_tmp, &se_tpg->tpg_sess_list, | ||
144 | sess_list) { | ||
145 | |||
146 | sess_p = (struct iscsi_session *)se_sess->fabric_sess_ptr; | ||
147 | spin_lock(&sess_p->conn_lock); | ||
148 | if (atomic_read(&sess_p->session_fall_back_to_erl0) || | ||
149 | atomic_read(&sess_p->session_logout) || | ||
150 | (sess_p->time2retain_timer_flags & ISCSI_TF_EXPIRED)) { | ||
151 | spin_unlock(&sess_p->conn_lock); | ||
152 | continue; | ||
153 | } | ||
154 | if (!memcmp((void *)sess_p->isid, (void *)conn->sess->isid, 6) && | ||
155 | (!strcmp((void *)sess_p->sess_ops->InitiatorName, | ||
156 | (void *)initiatorname_param->value) && | ||
157 | (sess_p->sess_ops->SessionType == sessiontype))) { | ||
158 | atomic_set(&sess_p->session_reinstatement, 1); | ||
159 | spin_unlock(&sess_p->conn_lock); | ||
160 | iscsit_inc_session_usage_count(sess_p); | ||
161 | iscsit_stop_time2retain_timer(sess_p); | ||
162 | sess = sess_p; | ||
163 | break; | ||
164 | } | ||
165 | spin_unlock(&sess_p->conn_lock); | ||
166 | } | ||
167 | spin_unlock_bh(&se_tpg->session_lock); | ||
168 | /* | ||
169 | * If the Time2Retain handler has expired, the session is already gone. | ||
170 | */ | ||
171 | if (!sess) | ||
172 | return 0; | ||
173 | |||
174 | pr_debug("%s iSCSI Session SID %u is still active for %s," | ||
175 | " preforming session reinstatement.\n", (sessiontype) ? | ||
176 | "Discovery" : "Normal", sess->sid, | ||
177 | sess->sess_ops->InitiatorName); | ||
178 | |||
179 | spin_lock_bh(&sess->conn_lock); | ||
180 | if (sess->session_state == TARG_SESS_STATE_FAILED) { | ||
181 | spin_unlock_bh(&sess->conn_lock); | ||
182 | iscsit_dec_session_usage_count(sess); | ||
183 | return iscsit_close_session(sess); | ||
184 | } | ||
185 | spin_unlock_bh(&sess->conn_lock); | ||
186 | |||
187 | iscsit_stop_session(sess, 1, 1); | ||
188 | iscsit_dec_session_usage_count(sess); | ||
189 | |||
190 | return iscsit_close_session(sess); | ||
191 | } | ||
192 | |||
193 | static void iscsi_login_set_conn_values( | ||
194 | struct iscsi_session *sess, | ||
195 | struct iscsi_conn *conn, | ||
196 | u16 cid) | ||
197 | { | ||
198 | conn->sess = sess; | ||
199 | conn->cid = cid; | ||
200 | /* | ||
201 | * Generate a random Status sequence number (statsn) for the new | ||
202 | * iSCSI connection. | ||
203 | */ | ||
204 | get_random_bytes(&conn->stat_sn, sizeof(u32)); | ||
205 | |||
206 | mutex_lock(&auth_id_lock); | ||
207 | conn->auth_id = iscsit_global->auth_id++; | ||
208 | mutex_unlock(&auth_id_lock); | ||
209 | } | ||
210 | |||
211 | /* | ||
212 | * This is the leading connection of a new session, | ||
213 | * or session reinstatement. | ||
214 | */ | ||
215 | static int iscsi_login_zero_tsih_s1( | ||
216 | struct iscsi_conn *conn, | ||
217 | unsigned char *buf) | ||
218 | { | ||
219 | struct iscsi_session *sess = NULL; | ||
220 | struct iscsi_login_req *pdu = (struct iscsi_login_req *)buf; | ||
221 | |||
222 | sess = kzalloc(sizeof(struct iscsi_session), GFP_KERNEL); | ||
223 | if (!sess) { | ||
224 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, | ||
225 | ISCSI_LOGIN_STATUS_NO_RESOURCES); | ||
226 | pr_err("Could not allocate memory for session\n"); | ||
227 | return -1; | ||
228 | } | ||
229 | |||
230 | iscsi_login_set_conn_values(sess, conn, pdu->cid); | ||
231 | sess->init_task_tag = pdu->itt; | ||
232 | memcpy((void *)&sess->isid, (void *)pdu->isid, 6); | ||
233 | sess->exp_cmd_sn = pdu->cmdsn; | ||
234 | INIT_LIST_HEAD(&sess->sess_conn_list); | ||
235 | INIT_LIST_HEAD(&sess->sess_ooo_cmdsn_list); | ||
236 | INIT_LIST_HEAD(&sess->cr_active_list); | ||
237 | INIT_LIST_HEAD(&sess->cr_inactive_list); | ||
238 | init_completion(&sess->async_msg_comp); | ||
239 | init_completion(&sess->reinstatement_comp); | ||
240 | init_completion(&sess->session_wait_comp); | ||
241 | init_completion(&sess->session_waiting_on_uc_comp); | ||
242 | mutex_init(&sess->cmdsn_mutex); | ||
243 | spin_lock_init(&sess->conn_lock); | ||
244 | spin_lock_init(&sess->cr_a_lock); | ||
245 | spin_lock_init(&sess->cr_i_lock); | ||
246 | spin_lock_init(&sess->session_usage_lock); | ||
247 | spin_lock_init(&sess->ttt_lock); | ||
248 | |||
249 | if (!idr_pre_get(&sess_idr, GFP_KERNEL)) { | ||
250 | pr_err("idr_pre_get() for sess_idr failed\n"); | ||
251 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, | ||
252 | ISCSI_LOGIN_STATUS_NO_RESOURCES); | ||
253 | return -1; | ||
254 | } | ||
255 | spin_lock(&sess_idr_lock); | ||
256 | idr_get_new(&sess_idr, NULL, &sess->session_index); | ||
257 | spin_unlock(&sess_idr_lock); | ||
258 | |||
259 | sess->creation_time = get_jiffies_64(); | ||
260 | spin_lock_init(&sess->session_stats_lock); | ||
261 | /* | ||
262 | * The FFP CmdSN window values will be allocated from the TPG's | ||
263 | * Initiator Node's ACL once the login has been successfully completed. | ||
264 | */ | ||
265 | sess->max_cmd_sn = pdu->cmdsn; | ||
266 | |||
267 | sess->sess_ops = kzalloc(sizeof(struct iscsi_sess_ops), GFP_KERNEL); | ||
268 | if (!sess->sess_ops) { | ||
269 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, | ||
270 | ISCSI_LOGIN_STATUS_NO_RESOURCES); | ||
271 | pr_err("Unable to allocate memory for" | ||
272 | " struct iscsi_sess_ops.\n"); | ||
273 | return -1; | ||
274 | } | ||
275 | |||
276 | sess->se_sess = transport_init_session(); | ||
277 | if (!sess->se_sess) { | ||
278 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, | ||
279 | ISCSI_LOGIN_STATUS_NO_RESOURCES); | ||
280 | return -1; | ||
281 | } | ||
282 | |||
283 | return 0; | ||
284 | } | ||
285 | |||
286 | static int iscsi_login_zero_tsih_s2( | ||
287 | struct iscsi_conn *conn) | ||
288 | { | ||
289 | struct iscsi_node_attrib *na; | ||
290 | struct iscsi_session *sess = conn->sess; | ||
291 | unsigned char buf[32]; | ||
292 | |||
293 | sess->tpg = conn->tpg; | ||
294 | |||
295 | /* | ||
296 | * Assign a new TPG Session Handle. Note this is protected with | ||
297 | * struct iscsi_portal_group->np_login_sem from iscsit_access_np(). | ||
298 | */ | ||
299 | sess->tsih = ++ISCSI_TPG_S(sess)->ntsih; | ||
300 | if (!sess->tsih) | ||
301 | sess->tsih = ++ISCSI_TPG_S(sess)->ntsih; | ||
302 | |||
303 | /* | ||
304 | * Create the default params from user defined values.. | ||
305 | */ | ||
306 | if (iscsi_copy_param_list(&conn->param_list, | ||
307 | ISCSI_TPG_C(conn)->param_list, 1) < 0) { | ||
308 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, | ||
309 | ISCSI_LOGIN_STATUS_NO_RESOURCES); | ||
310 | return -1; | ||
311 | } | ||
312 | |||
313 | iscsi_set_keys_to_negotiate(0, conn->param_list); | ||
314 | |||
315 | if (sess->sess_ops->SessionType) | ||
316 | return iscsi_set_keys_irrelevant_for_discovery( | ||
317 | conn->param_list); | ||
318 | |||
319 | na = iscsit_tpg_get_node_attrib(sess); | ||
320 | |||
321 | /* | ||
322 | * Need to send TargetPortalGroupTag back in first login response | ||
323 | * on any iSCSI connection where the Initiator provides TargetName. | ||
324 | * See 5.3.1. Login Phase Start | ||
325 | * | ||
326 | * In our case, we have already located the struct iscsi_tiqn at this point. | ||
327 | */ | ||
328 | memset(buf, 0, 32); | ||
329 | sprintf(buf, "TargetPortalGroupTag=%hu", ISCSI_TPG_S(sess)->tpgt); | ||
330 | if (iscsi_change_param_value(buf, conn->param_list, 0) < 0) { | ||
331 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, | ||
332 | ISCSI_LOGIN_STATUS_NO_RESOURCES); | ||
333 | return -1; | ||
334 | } | ||
335 | |||
336 | /* | ||
337 | * Workaround for Initiators that have broken connection recovery logic. | ||
338 | * | ||
339 | * "We would really like to get rid of this." Linux-iSCSI.org team | ||
340 | */ | ||
341 | memset(buf, 0, 32); | ||
342 | sprintf(buf, "ErrorRecoveryLevel=%d", na->default_erl); | ||
343 | if (iscsi_change_param_value(buf, conn->param_list, 0) < 0) { | ||
344 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, | ||
345 | ISCSI_LOGIN_STATUS_NO_RESOURCES); | ||
346 | return -1; | ||
347 | } | ||
348 | |||
349 | if (iscsi_login_disable_FIM_keys(conn->param_list, conn) < 0) | ||
350 | return -1; | ||
351 | |||
352 | return 0; | ||
353 | } | ||
354 | |||
355 | /* | ||
356 | * Remove PSTATE_NEGOTIATE for the four FIM related keys. | ||
357 | * The Initiator node will be able to enable FIM by proposing them itself. | ||
358 | */ | ||
359 | int iscsi_login_disable_FIM_keys( | ||
360 | struct iscsi_param_list *param_list, | ||
361 | struct iscsi_conn *conn) | ||
362 | { | ||
363 | struct iscsi_param *param; | ||
364 | |||
365 | param = iscsi_find_param_from_key("OFMarker", param_list); | ||
366 | if (!param) { | ||
367 | pr_err("iscsi_find_param_from_key() for" | ||
368 | " OFMarker failed\n"); | ||
369 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, | ||
370 | ISCSI_LOGIN_STATUS_NO_RESOURCES); | ||
371 | return -1; | ||
372 | } | ||
373 | param->state &= ~PSTATE_NEGOTIATE; | ||
374 | |||
375 | param = iscsi_find_param_from_key("OFMarkInt", param_list); | ||
376 | if (!param) { | ||
377 | pr_err("iscsi_find_param_from_key() for" | ||
378 | " IFMarker failed\n"); | ||
379 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, | ||
380 | ISCSI_LOGIN_STATUS_NO_RESOURCES); | ||
381 | return -1; | ||
382 | } | ||
383 | param->state &= ~PSTATE_NEGOTIATE; | ||
384 | |||
385 | param = iscsi_find_param_from_key("IFMarker", param_list); | ||
386 | if (!param) { | ||
387 | pr_err("iscsi_find_param_from_key() for" | ||
388 | " IFMarker failed\n"); | ||
389 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, | ||
390 | ISCSI_LOGIN_STATUS_NO_RESOURCES); | ||
391 | return -1; | ||
392 | } | ||
393 | param->state &= ~PSTATE_NEGOTIATE; | ||
394 | |||
395 | param = iscsi_find_param_from_key("IFMarkInt", param_list); | ||
396 | if (!param) { | ||
397 | pr_err("iscsi_find_param_from_key() for" | ||
398 | " IFMarker failed\n"); | ||
399 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, | ||
400 | ISCSI_LOGIN_STATUS_NO_RESOURCES); | ||
401 | return -1; | ||
402 | } | ||
403 | param->state &= ~PSTATE_NEGOTIATE; | ||
404 | |||
405 | return 0; | ||
406 | } | ||
407 | |||
408 | static int iscsi_login_non_zero_tsih_s1( | ||
409 | struct iscsi_conn *conn, | ||
410 | unsigned char *buf) | ||
411 | { | ||
412 | struct iscsi_login_req *pdu = (struct iscsi_login_req *)buf; | ||
413 | |||
414 | iscsi_login_set_conn_values(NULL, conn, pdu->cid); | ||
415 | return 0; | ||
416 | } | ||
417 | |||
418 | /* | ||
419 | * Add a new connection to an existing session. | ||
420 | */ | ||
421 | static int iscsi_login_non_zero_tsih_s2( | ||
422 | struct iscsi_conn *conn, | ||
423 | unsigned char *buf) | ||
424 | { | ||
425 | struct iscsi_portal_group *tpg = conn->tpg; | ||
426 | struct iscsi_session *sess = NULL, *sess_p = NULL; | ||
427 | struct se_portal_group *se_tpg = &tpg->tpg_se_tpg; | ||
428 | struct se_session *se_sess, *se_sess_tmp; | ||
429 | struct iscsi_login_req *pdu = (struct iscsi_login_req *)buf; | ||
430 | |||
431 | spin_lock_bh(&se_tpg->session_lock); | ||
432 | list_for_each_entry_safe(se_sess, se_sess_tmp, &se_tpg->tpg_sess_list, | ||
433 | sess_list) { | ||
434 | |||
435 | sess_p = (struct iscsi_session *)se_sess->fabric_sess_ptr; | ||
436 | if (atomic_read(&sess_p->session_fall_back_to_erl0) || | ||
437 | atomic_read(&sess_p->session_logout) || | ||
438 | (sess_p->time2retain_timer_flags & ISCSI_TF_EXPIRED)) | ||
439 | continue; | ||
440 | if (!memcmp((const void *)sess_p->isid, | ||
441 | (const void *)pdu->isid, 6) && | ||
442 | (sess_p->tsih == pdu->tsih)) { | ||
443 | iscsit_inc_session_usage_count(sess_p); | ||
444 | iscsit_stop_time2retain_timer(sess_p); | ||
445 | sess = sess_p; | ||
446 | break; | ||
447 | } | ||
448 | } | ||
449 | spin_unlock_bh(&se_tpg->session_lock); | ||
450 | |||
451 | /* | ||
452 | * If the Time2Retain handler has expired, the session is already gone. | ||
453 | */ | ||
454 | if (!sess) { | ||
455 | pr_err("Initiator attempting to add a connection to" | ||
456 | " a non-existent session, rejecting iSCSI Login.\n"); | ||
457 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, | ||
458 | ISCSI_LOGIN_STATUS_NO_SESSION); | ||
459 | return -1; | ||
460 | } | ||
461 | |||
462 | /* | ||
463 | * Stop the Time2Retain timer if this is a failed session, we restart | ||
464 | * the timer if the login is not successful. | ||
465 | */ | ||
466 | spin_lock_bh(&sess->conn_lock); | ||
467 | if (sess->session_state == TARG_SESS_STATE_FAILED) | ||
468 | atomic_set(&sess->session_continuation, 1); | ||
469 | spin_unlock_bh(&sess->conn_lock); | ||
470 | |||
471 | iscsi_login_set_conn_values(sess, conn, pdu->cid); | ||
472 | |||
473 | if (iscsi_copy_param_list(&conn->param_list, | ||
474 | ISCSI_TPG_C(conn)->param_list, 0) < 0) { | ||
475 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, | ||
476 | ISCSI_LOGIN_STATUS_NO_RESOURCES); | ||
477 | return -1; | ||
478 | } | ||
479 | |||
480 | iscsi_set_keys_to_negotiate(0, conn->param_list); | ||
481 | /* | ||
482 | * Need to send TargetPortalGroupTag back in first login response | ||
483 | * on any iSCSI connection where the Initiator provides TargetName. | ||
484 | * See 5.3.1. Login Phase Start | ||
485 | * | ||
486 | * In our case, we have already located the struct iscsi_tiqn at this point. | ||
487 | */ | ||
488 | memset(buf, 0, 32); | ||
489 | sprintf(buf, "TargetPortalGroupTag=%hu", ISCSI_TPG_S(sess)->tpgt); | ||
490 | if (iscsi_change_param_value(buf, conn->param_list, 0) < 0) { | ||
491 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, | ||
492 | ISCSI_LOGIN_STATUS_NO_RESOURCES); | ||
493 | return -1; | ||
494 | } | ||
495 | |||
496 | return iscsi_login_disable_FIM_keys(conn->param_list, conn); | ||
497 | } | ||
498 | |||
499 | int iscsi_login_post_auth_non_zero_tsih( | ||
500 | struct iscsi_conn *conn, | ||
501 | u16 cid, | ||
502 | u32 exp_statsn) | ||
503 | { | ||
504 | struct iscsi_conn *conn_ptr = NULL; | ||
505 | struct iscsi_conn_recovery *cr = NULL; | ||
506 | struct iscsi_session *sess = conn->sess; | ||
507 | |||
508 | /* | ||
509 | * By following item 5 in the login table, if we have found | ||
510 | * an existing ISID and a valid/existing TSIH and an existing | ||
511 | * CID we do connection reinstatement. Currently we dont not | ||
512 | * support it so we send back an non-zero status class to the | ||
513 | * initiator and release the new connection. | ||
514 | */ | ||
515 | conn_ptr = iscsit_get_conn_from_cid_rcfr(sess, cid); | ||
516 | if ((conn_ptr)) { | ||
517 | pr_err("Connection exists with CID %hu for %s," | ||
518 | " performing connection reinstatement.\n", | ||
519 | conn_ptr->cid, sess->sess_ops->InitiatorName); | ||
520 | |||
521 | iscsit_connection_reinstatement_rcfr(conn_ptr); | ||
522 | iscsit_dec_conn_usage_count(conn_ptr); | ||
523 | } | ||
524 | |||
525 | /* | ||
526 | * Check for any connection recovery entires containing CID. | ||
527 | * We use the original ExpStatSN sent in the first login request | ||
528 | * to acknowledge commands for the failed connection. | ||
529 | * | ||
530 | * Also note that an explict logout may have already been sent, | ||
531 | * but the response may not be sent due to additional connection | ||
532 | * loss. | ||
533 | */ | ||
534 | if (sess->sess_ops->ErrorRecoveryLevel == 2) { | ||
535 | cr = iscsit_get_inactive_connection_recovery_entry( | ||
536 | sess, cid); | ||
537 | if ((cr)) { | ||
538 | pr_debug("Performing implicit logout" | ||
539 | " for connection recovery on CID: %hu\n", | ||
540 | conn->cid); | ||
541 | iscsit_discard_cr_cmds_by_expstatsn(cr, exp_statsn); | ||
542 | } | ||
543 | } | ||
544 | |||
545 | /* | ||
546 | * Else we follow item 4 from the login table in that we have | ||
547 | * found an existing ISID and a valid/existing TSIH and a new | ||
548 | * CID we go ahead and continue to add a new connection to the | ||
549 | * session. | ||
550 | */ | ||
551 | pr_debug("Adding CID %hu to existing session for %s.\n", | ||
552 | cid, sess->sess_ops->InitiatorName); | ||
553 | |||
554 | if ((atomic_read(&sess->nconn) + 1) > sess->sess_ops->MaxConnections) { | ||
555 | pr_err("Adding additional connection to this session" | ||
556 | " would exceed MaxConnections %d, login failed.\n", | ||
557 | sess->sess_ops->MaxConnections); | ||
558 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, | ||
559 | ISCSI_LOGIN_STATUS_ISID_ERROR); | ||
560 | return -1; | ||
561 | } | ||
562 | |||
563 | return 0; | ||
564 | } | ||
565 | |||
566 | static void iscsi_post_login_start_timers(struct iscsi_conn *conn) | ||
567 | { | ||
568 | struct iscsi_session *sess = conn->sess; | ||
569 | |||
570 | if (!sess->sess_ops->SessionType) | ||
571 | iscsit_start_nopin_timer(conn); | ||
572 | } | ||
573 | |||
574 | static int iscsi_post_login_handler( | ||
575 | struct iscsi_np *np, | ||
576 | struct iscsi_conn *conn, | ||
577 | u8 zero_tsih) | ||
578 | { | ||
579 | int stop_timer = 0; | ||
580 | struct iscsi_session *sess = conn->sess; | ||
581 | struct se_session *se_sess = sess->se_sess; | ||
582 | struct iscsi_portal_group *tpg = ISCSI_TPG_S(sess); | ||
583 | struct se_portal_group *se_tpg = &tpg->tpg_se_tpg; | ||
584 | struct iscsi_thread_set *ts; | ||
585 | |||
586 | iscsit_inc_conn_usage_count(conn); | ||
587 | |||
588 | iscsit_collect_login_stats(conn, ISCSI_STATUS_CLS_SUCCESS, | ||
589 | ISCSI_LOGIN_STATUS_ACCEPT); | ||
590 | |||
591 | pr_debug("Moving to TARG_CONN_STATE_LOGGED_IN.\n"); | ||
592 | conn->conn_state = TARG_CONN_STATE_LOGGED_IN; | ||
593 | |||
594 | iscsi_set_connection_parameters(conn->conn_ops, conn->param_list); | ||
595 | iscsit_set_sync_and_steering_values(conn); | ||
596 | /* | ||
597 | * SCSI Initiator -> SCSI Target Port Mapping | ||
598 | */ | ||
599 | ts = iscsi_get_thread_set(); | ||
600 | if (!zero_tsih) { | ||
601 | iscsi_set_session_parameters(sess->sess_ops, | ||
602 | conn->param_list, 0); | ||
603 | iscsi_release_param_list(conn->param_list); | ||
604 | conn->param_list = NULL; | ||
605 | |||
606 | spin_lock_bh(&sess->conn_lock); | ||
607 | atomic_set(&sess->session_continuation, 0); | ||
608 | if (sess->session_state == TARG_SESS_STATE_FAILED) { | ||
609 | pr_debug("Moving to" | ||
610 | " TARG_SESS_STATE_LOGGED_IN.\n"); | ||
611 | sess->session_state = TARG_SESS_STATE_LOGGED_IN; | ||
612 | stop_timer = 1; | ||
613 | } | ||
614 | |||
615 | pr_debug("iSCSI Login successful on CID: %hu from %s to" | ||
616 | " %s:%hu,%hu\n", conn->cid, conn->login_ip, np->np_ip, | ||
617 | np->np_port, tpg->tpgt); | ||
618 | |||
619 | list_add_tail(&conn->conn_list, &sess->sess_conn_list); | ||
620 | atomic_inc(&sess->nconn); | ||
621 | pr_debug("Incremented iSCSI Connection count to %hu" | ||
622 | " from node: %s\n", atomic_read(&sess->nconn), | ||
623 | sess->sess_ops->InitiatorName); | ||
624 | spin_unlock_bh(&sess->conn_lock); | ||
625 | |||
626 | iscsi_post_login_start_timers(conn); | ||
627 | iscsi_activate_thread_set(conn, ts); | ||
628 | /* | ||
629 | * Determine CPU mask to ensure connection's RX and TX kthreads | ||
630 | * are scheduled on the same CPU. | ||
631 | */ | ||
632 | iscsit_thread_get_cpumask(conn); | ||
633 | conn->conn_rx_reset_cpumask = 1; | ||
634 | conn->conn_tx_reset_cpumask = 1; | ||
635 | |||
636 | iscsit_dec_conn_usage_count(conn); | ||
637 | if (stop_timer) { | ||
638 | spin_lock_bh(&se_tpg->session_lock); | ||
639 | iscsit_stop_time2retain_timer(sess); | ||
640 | spin_unlock_bh(&se_tpg->session_lock); | ||
641 | } | ||
642 | iscsit_dec_session_usage_count(sess); | ||
643 | return 0; | ||
644 | } | ||
645 | |||
646 | iscsi_set_session_parameters(sess->sess_ops, conn->param_list, 1); | ||
647 | iscsi_release_param_list(conn->param_list); | ||
648 | conn->param_list = NULL; | ||
649 | |||
650 | iscsit_determine_maxcmdsn(sess); | ||
651 | |||
652 | spin_lock_bh(&se_tpg->session_lock); | ||
653 | __transport_register_session(&sess->tpg->tpg_se_tpg, | ||
654 | se_sess->se_node_acl, se_sess, (void *)sess); | ||
655 | pr_debug("Moving to TARG_SESS_STATE_LOGGED_IN.\n"); | ||
656 | sess->session_state = TARG_SESS_STATE_LOGGED_IN; | ||
657 | |||
658 | pr_debug("iSCSI Login successful on CID: %hu from %s to %s:%hu,%hu\n", | ||
659 | conn->cid, conn->login_ip, np->np_ip, np->np_port, tpg->tpgt); | ||
660 | |||
661 | spin_lock_bh(&sess->conn_lock); | ||
662 | list_add_tail(&conn->conn_list, &sess->sess_conn_list); | ||
663 | atomic_inc(&sess->nconn); | ||
664 | pr_debug("Incremented iSCSI Connection count to %hu from node:" | ||
665 | " %s\n", atomic_read(&sess->nconn), | ||
666 | sess->sess_ops->InitiatorName); | ||
667 | spin_unlock_bh(&sess->conn_lock); | ||
668 | |||
669 | sess->sid = tpg->sid++; | ||
670 | if (!sess->sid) | ||
671 | sess->sid = tpg->sid++; | ||
672 | pr_debug("Established iSCSI session from node: %s\n", | ||
673 | sess->sess_ops->InitiatorName); | ||
674 | |||
675 | tpg->nsessions++; | ||
676 | if (tpg->tpg_tiqn) | ||
677 | tpg->tpg_tiqn->tiqn_nsessions++; | ||
678 | |||
679 | pr_debug("Incremented number of active iSCSI sessions to %u on" | ||
680 | " iSCSI Target Portal Group: %hu\n", tpg->nsessions, tpg->tpgt); | ||
681 | spin_unlock_bh(&se_tpg->session_lock); | ||
682 | |||
683 | iscsi_post_login_start_timers(conn); | ||
684 | iscsi_activate_thread_set(conn, ts); | ||
685 | /* | ||
686 | * Determine CPU mask to ensure connection's RX and TX kthreads | ||
687 | * are scheduled on the same CPU. | ||
688 | */ | ||
689 | iscsit_thread_get_cpumask(conn); | ||
690 | conn->conn_rx_reset_cpumask = 1; | ||
691 | conn->conn_tx_reset_cpumask = 1; | ||
692 | |||
693 | iscsit_dec_conn_usage_count(conn); | ||
694 | |||
695 | return 0; | ||
696 | } | ||
697 | |||
698 | static void iscsi_handle_login_thread_timeout(unsigned long data) | ||
699 | { | ||
700 | struct iscsi_np *np = (struct iscsi_np *) data; | ||
701 | |||
702 | spin_lock_bh(&np->np_thread_lock); | ||
703 | pr_err("iSCSI Login timeout on Network Portal %s:%hu\n", | ||
704 | np->np_ip, np->np_port); | ||
705 | |||
706 | if (np->np_login_timer_flags & ISCSI_TF_STOP) { | ||
707 | spin_unlock_bh(&np->np_thread_lock); | ||
708 | return; | ||
709 | } | ||
710 | |||
711 | if (np->np_thread) | ||
712 | send_sig(SIGINT, np->np_thread, 1); | ||
713 | |||
714 | np->np_login_timer_flags &= ~ISCSI_TF_RUNNING; | ||
715 | spin_unlock_bh(&np->np_thread_lock); | ||
716 | } | ||
717 | |||
718 | static void iscsi_start_login_thread_timer(struct iscsi_np *np) | ||
719 | { | ||
720 | /* | ||
721 | * This used the TA_LOGIN_TIMEOUT constant because at this | ||
722 | * point we do not have access to ISCSI_TPG_ATTRIB(tpg)->login_timeout | ||
723 | */ | ||
724 | spin_lock_bh(&np->np_thread_lock); | ||
725 | init_timer(&np->np_login_timer); | ||
726 | np->np_login_timer.expires = (get_jiffies_64() + TA_LOGIN_TIMEOUT * HZ); | ||
727 | np->np_login_timer.data = (unsigned long)np; | ||
728 | np->np_login_timer.function = iscsi_handle_login_thread_timeout; | ||
729 | np->np_login_timer_flags &= ~ISCSI_TF_STOP; | ||
730 | np->np_login_timer_flags |= ISCSI_TF_RUNNING; | ||
731 | add_timer(&np->np_login_timer); | ||
732 | |||
733 | pr_debug("Added timeout timer to iSCSI login request for" | ||
734 | " %u seconds.\n", TA_LOGIN_TIMEOUT); | ||
735 | spin_unlock_bh(&np->np_thread_lock); | ||
736 | } | ||
737 | |||
738 | static void iscsi_stop_login_thread_timer(struct iscsi_np *np) | ||
739 | { | ||
740 | spin_lock_bh(&np->np_thread_lock); | ||
741 | if (!(np->np_login_timer_flags & ISCSI_TF_RUNNING)) { | ||
742 | spin_unlock_bh(&np->np_thread_lock); | ||
743 | return; | ||
744 | } | ||
745 | np->np_login_timer_flags |= ISCSI_TF_STOP; | ||
746 | spin_unlock_bh(&np->np_thread_lock); | ||
747 | |||
748 | del_timer_sync(&np->np_login_timer); | ||
749 | |||
750 | spin_lock_bh(&np->np_thread_lock); | ||
751 | np->np_login_timer_flags &= ~ISCSI_TF_RUNNING; | ||
752 | spin_unlock_bh(&np->np_thread_lock); | ||
753 | } | ||
754 | |||
755 | int iscsi_target_setup_login_socket( | ||
756 | struct iscsi_np *np, | ||
757 | struct __kernel_sockaddr_storage *sockaddr) | ||
758 | { | ||
759 | struct socket *sock; | ||
760 | int backlog = 5, ret, opt = 0, len; | ||
761 | |||
762 | switch (np->np_network_transport) { | ||
763 | case ISCSI_TCP: | ||
764 | np->np_ip_proto = IPPROTO_TCP; | ||
765 | np->np_sock_type = SOCK_STREAM; | ||
766 | break; | ||
767 | case ISCSI_SCTP_TCP: | ||
768 | np->np_ip_proto = IPPROTO_SCTP; | ||
769 | np->np_sock_type = SOCK_STREAM; | ||
770 | break; | ||
771 | case ISCSI_SCTP_UDP: | ||
772 | np->np_ip_proto = IPPROTO_SCTP; | ||
773 | np->np_sock_type = SOCK_SEQPACKET; | ||
774 | break; | ||
775 | case ISCSI_IWARP_TCP: | ||
776 | case ISCSI_IWARP_SCTP: | ||
777 | case ISCSI_INFINIBAND: | ||
778 | default: | ||
779 | pr_err("Unsupported network_transport: %d\n", | ||
780 | np->np_network_transport); | ||
781 | return -EINVAL; | ||
782 | } | ||
783 | |||
784 | ret = sock_create(sockaddr->ss_family, np->np_sock_type, | ||
785 | np->np_ip_proto, &sock); | ||
786 | if (ret < 0) { | ||
787 | pr_err("sock_create() failed.\n"); | ||
788 | return ret; | ||
789 | } | ||
790 | np->np_socket = sock; | ||
791 | /* | ||
792 | * The SCTP stack needs struct socket->file. | ||
793 | */ | ||
794 | if ((np->np_network_transport == ISCSI_SCTP_TCP) || | ||
795 | (np->np_network_transport == ISCSI_SCTP_UDP)) { | ||
796 | if (!sock->file) { | ||
797 | sock->file = kzalloc(sizeof(struct file), GFP_KERNEL); | ||
798 | if (!sock->file) { | ||
799 | pr_err("Unable to allocate struct" | ||
800 | " file for SCTP\n"); | ||
801 | ret = -ENOMEM; | ||
802 | goto fail; | ||
803 | } | ||
804 | np->np_flags |= NPF_SCTP_STRUCT_FILE; | ||
805 | } | ||
806 | } | ||
807 | /* | ||
808 | * Setup the np->np_sockaddr from the passed sockaddr setup | ||
809 | * in iscsi_target_configfs.c code.. | ||
810 | */ | ||
811 | memcpy((void *)&np->np_sockaddr, (void *)sockaddr, | ||
812 | sizeof(struct __kernel_sockaddr_storage)); | ||
813 | |||
814 | if (sockaddr->ss_family == AF_INET6) | ||
815 | len = sizeof(struct sockaddr_in6); | ||
816 | else | ||
817 | len = sizeof(struct sockaddr_in); | ||
818 | /* | ||
819 | * Set SO_REUSEADDR, and disable Nagel Algorithm with TCP_NODELAY. | ||
820 | */ | ||
821 | opt = 1; | ||
822 | if (np->np_network_transport == ISCSI_TCP) { | ||
823 | ret = kernel_setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, | ||
824 | (char *)&opt, sizeof(opt)); | ||
825 | if (ret < 0) { | ||
826 | pr_err("kernel_setsockopt() for TCP_NODELAY" | ||
827 | " failed: %d\n", ret); | ||
828 | goto fail; | ||
829 | } | ||
830 | } | ||
831 | |||
832 | ret = kernel_setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, | ||
833 | (char *)&opt, sizeof(opt)); | ||
834 | if (ret < 0) { | ||
835 | pr_err("kernel_setsockopt() for SO_REUSEADDR" | ||
836 | " failed\n"); | ||
837 | goto fail; | ||
838 | } | ||
839 | |||
840 | ret = kernel_bind(sock, (struct sockaddr *)&np->np_sockaddr, len); | ||
841 | if (ret < 0) { | ||
842 | pr_err("kernel_bind() failed: %d\n", ret); | ||
843 | goto fail; | ||
844 | } | ||
845 | |||
846 | ret = kernel_listen(sock, backlog); | ||
847 | if (ret != 0) { | ||
848 | pr_err("kernel_listen() failed: %d\n", ret); | ||
849 | goto fail; | ||
850 | } | ||
851 | |||
852 | return 0; | ||
853 | |||
854 | fail: | ||
855 | np->np_socket = NULL; | ||
856 | if (sock) { | ||
857 | if (np->np_flags & NPF_SCTP_STRUCT_FILE) { | ||
858 | kfree(sock->file); | ||
859 | sock->file = NULL; | ||
860 | } | ||
861 | |||
862 | sock_release(sock); | ||
863 | } | ||
864 | return ret; | ||
865 | } | ||
866 | |||
867 | static int __iscsi_target_login_thread(struct iscsi_np *np) | ||
868 | { | ||
869 | u8 buffer[ISCSI_HDR_LEN], iscsi_opcode, zero_tsih = 0; | ||
870 | int err, ret = 0, ip_proto, sock_type, set_sctp_conn_flag, stop; | ||
871 | struct iscsi_conn *conn = NULL; | ||
872 | struct iscsi_login *login; | ||
873 | struct iscsi_portal_group *tpg = NULL; | ||
874 | struct socket *new_sock, *sock; | ||
875 | struct kvec iov; | ||
876 | struct iscsi_login_req *pdu; | ||
877 | struct sockaddr_in sock_in; | ||
878 | struct sockaddr_in6 sock_in6; | ||
879 | |||
880 | flush_signals(current); | ||
881 | set_sctp_conn_flag = 0; | ||
882 | sock = np->np_socket; | ||
883 | ip_proto = np->np_ip_proto; | ||
884 | sock_type = np->np_sock_type; | ||
885 | |||
886 | spin_lock_bh(&np->np_thread_lock); | ||
887 | if (np->np_thread_state == ISCSI_NP_THREAD_RESET) { | ||
888 | np->np_thread_state = ISCSI_NP_THREAD_ACTIVE; | ||
889 | complete(&np->np_restart_comp); | ||
890 | } else { | ||
891 | np->np_thread_state = ISCSI_NP_THREAD_ACTIVE; | ||
892 | } | ||
893 | spin_unlock_bh(&np->np_thread_lock); | ||
894 | |||
895 | if (kernel_accept(sock, &new_sock, 0) < 0) { | ||
896 | spin_lock_bh(&np->np_thread_lock); | ||
897 | if (np->np_thread_state == ISCSI_NP_THREAD_RESET) { | ||
898 | spin_unlock_bh(&np->np_thread_lock); | ||
899 | complete(&np->np_restart_comp); | ||
900 | /* Get another socket */ | ||
901 | return 1; | ||
902 | } | ||
903 | spin_unlock_bh(&np->np_thread_lock); | ||
904 | goto out; | ||
905 | } | ||
906 | /* | ||
907 | * The SCTP stack needs struct socket->file. | ||
908 | */ | ||
909 | if ((np->np_network_transport == ISCSI_SCTP_TCP) || | ||
910 | (np->np_network_transport == ISCSI_SCTP_UDP)) { | ||
911 | if (!new_sock->file) { | ||
912 | new_sock->file = kzalloc( | ||
913 | sizeof(struct file), GFP_KERNEL); | ||
914 | if (!new_sock->file) { | ||
915 | pr_err("Unable to allocate struct" | ||
916 | " file for SCTP\n"); | ||
917 | sock_release(new_sock); | ||
918 | /* Get another socket */ | ||
919 | return 1; | ||
920 | } | ||
921 | set_sctp_conn_flag = 1; | ||
922 | } | ||
923 | } | ||
924 | |||
925 | iscsi_start_login_thread_timer(np); | ||
926 | |||
927 | conn = kzalloc(sizeof(struct iscsi_conn), GFP_KERNEL); | ||
928 | if (!conn) { | ||
929 | pr_err("Could not allocate memory for" | ||
930 | " new connection\n"); | ||
931 | if (set_sctp_conn_flag) { | ||
932 | kfree(new_sock->file); | ||
933 | new_sock->file = NULL; | ||
934 | } | ||
935 | sock_release(new_sock); | ||
936 | /* Get another socket */ | ||
937 | return 1; | ||
938 | } | ||
939 | |||
940 | pr_debug("Moving to TARG_CONN_STATE_FREE.\n"); | ||
941 | conn->conn_state = TARG_CONN_STATE_FREE; | ||
942 | conn->sock = new_sock; | ||
943 | |||
944 | if (set_sctp_conn_flag) | ||
945 | conn->conn_flags |= CONNFLAG_SCTP_STRUCT_FILE; | ||
946 | |||
947 | pr_debug("Moving to TARG_CONN_STATE_XPT_UP.\n"); | ||
948 | conn->conn_state = TARG_CONN_STATE_XPT_UP; | ||
949 | |||
950 | /* | ||
951 | * Allocate conn->conn_ops early as a failure calling | ||
952 | * iscsit_tx_login_rsp() below will call tx_data(). | ||
953 | */ | ||
954 | conn->conn_ops = kzalloc(sizeof(struct iscsi_conn_ops), GFP_KERNEL); | ||
955 | if (!conn->conn_ops) { | ||
956 | pr_err("Unable to allocate memory for" | ||
957 | " struct iscsi_conn_ops.\n"); | ||
958 | goto new_sess_out; | ||
959 | } | ||
960 | /* | ||
961 | * Perform the remaining iSCSI connection initialization items.. | ||
962 | */ | ||
963 | if (iscsi_login_init_conn(conn) < 0) | ||
964 | goto new_sess_out; | ||
965 | |||
966 | memset(buffer, 0, ISCSI_HDR_LEN); | ||
967 | memset(&iov, 0, sizeof(struct kvec)); | ||
968 | iov.iov_base = buffer; | ||
969 | iov.iov_len = ISCSI_HDR_LEN; | ||
970 | |||
971 | if (rx_data(conn, &iov, 1, ISCSI_HDR_LEN) <= 0) { | ||
972 | pr_err("rx_data() returned an error.\n"); | ||
973 | goto new_sess_out; | ||
974 | } | ||
975 | |||
976 | iscsi_opcode = (buffer[0] & ISCSI_OPCODE_MASK); | ||
977 | if (!(iscsi_opcode & ISCSI_OP_LOGIN)) { | ||
978 | pr_err("First opcode is not login request," | ||
979 | " failing login request.\n"); | ||
980 | goto new_sess_out; | ||
981 | } | ||
982 | |||
983 | pdu = (struct iscsi_login_req *) buffer; | ||
984 | pdu->cid = be16_to_cpu(pdu->cid); | ||
985 | pdu->tsih = be16_to_cpu(pdu->tsih); | ||
986 | pdu->itt = be32_to_cpu(pdu->itt); | ||
987 | pdu->cmdsn = be32_to_cpu(pdu->cmdsn); | ||
988 | pdu->exp_statsn = be32_to_cpu(pdu->exp_statsn); | ||
989 | /* | ||
990 | * Used by iscsit_tx_login_rsp() for Login Resonses PDUs | ||
991 | * when Status-Class != 0. | ||
992 | */ | ||
993 | conn->login_itt = pdu->itt; | ||
994 | |||
995 | spin_lock_bh(&np->np_thread_lock); | ||
996 | if (np->np_thread_state != ISCSI_NP_THREAD_ACTIVE) { | ||
997 | spin_unlock_bh(&np->np_thread_lock); | ||
998 | pr_err("iSCSI Network Portal on %s:%hu currently not" | ||
999 | " active.\n", np->np_ip, np->np_port); | ||
1000 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, | ||
1001 | ISCSI_LOGIN_STATUS_SVC_UNAVAILABLE); | ||
1002 | goto new_sess_out; | ||
1003 | } | ||
1004 | spin_unlock_bh(&np->np_thread_lock); | ||
1005 | |||
1006 | if (np->np_sockaddr.ss_family == AF_INET6) { | ||
1007 | memset(&sock_in6, 0, sizeof(struct sockaddr_in6)); | ||
1008 | |||
1009 | if (conn->sock->ops->getname(conn->sock, | ||
1010 | (struct sockaddr *)&sock_in6, &err, 1) < 0) { | ||
1011 | pr_err("sock_ops->getname() failed.\n"); | ||
1012 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, | ||
1013 | ISCSI_LOGIN_STATUS_TARGET_ERROR); | ||
1014 | goto new_sess_out; | ||
1015 | } | ||
1016 | #if 0 | ||
1017 | if (!iscsi_ntop6((const unsigned char *) | ||
1018 | &sock_in6.sin6_addr.in6_u, | ||
1019 | (char *)&conn->ipv6_login_ip[0], | ||
1020 | IPV6_ADDRESS_SPACE)) { | ||
1021 | pr_err("iscsi_ntop6() failed\n"); | ||
1022 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, | ||
1023 | ISCSI_LOGIN_STATUS_TARGET_ERROR); | ||
1024 | goto new_sess_out; | ||
1025 | } | ||
1026 | #else | ||
1027 | pr_debug("Skipping iscsi_ntop6()\n"); | ||
1028 | #endif | ||
1029 | } else { | ||
1030 | memset(&sock_in, 0, sizeof(struct sockaddr_in)); | ||
1031 | |||
1032 | if (conn->sock->ops->getname(conn->sock, | ||
1033 | (struct sockaddr *)&sock_in, &err, 1) < 0) { | ||
1034 | pr_err("sock_ops->getname() failed.\n"); | ||
1035 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, | ||
1036 | ISCSI_LOGIN_STATUS_TARGET_ERROR); | ||
1037 | goto new_sess_out; | ||
1038 | } | ||
1039 | sprintf(conn->login_ip, "%pI4", &sock_in.sin_addr.s_addr); | ||
1040 | conn->login_port = ntohs(sock_in.sin_port); | ||
1041 | } | ||
1042 | |||
1043 | conn->network_transport = np->np_network_transport; | ||
1044 | |||
1045 | pr_debug("Received iSCSI login request from %s on %s Network" | ||
1046 | " Portal %s:%hu\n", conn->login_ip, | ||
1047 | (conn->network_transport == ISCSI_TCP) ? "TCP" : "SCTP", | ||
1048 | np->np_ip, np->np_port); | ||
1049 | |||
1050 | pr_debug("Moving to TARG_CONN_STATE_IN_LOGIN.\n"); | ||
1051 | conn->conn_state = TARG_CONN_STATE_IN_LOGIN; | ||
1052 | |||
1053 | if (iscsi_login_check_initiator_version(conn, pdu->max_version, | ||
1054 | pdu->min_version) < 0) | ||
1055 | goto new_sess_out; | ||
1056 | |||
1057 | zero_tsih = (pdu->tsih == 0x0000); | ||
1058 | if ((zero_tsih)) { | ||
1059 | /* | ||
1060 | * This is the leading connection of a new session. | ||
1061 | * We wait until after authentication to check for | ||
1062 | * session reinstatement. | ||
1063 | */ | ||
1064 | if (iscsi_login_zero_tsih_s1(conn, buffer) < 0) | ||
1065 | goto new_sess_out; | ||
1066 | } else { | ||
1067 | /* | ||
1068 | * Add a new connection to an existing session. | ||
1069 | * We check for a non-existant session in | ||
1070 | * iscsi_login_non_zero_tsih_s2() below based | ||
1071 | * on ISID/TSIH, but wait until after authentication | ||
1072 | * to check for connection reinstatement, etc. | ||
1073 | */ | ||
1074 | if (iscsi_login_non_zero_tsih_s1(conn, buffer) < 0) | ||
1075 | goto new_sess_out; | ||
1076 | } | ||
1077 | |||
1078 | /* | ||
1079 | * This will process the first login request, and call | ||
1080 | * iscsi_target_locate_portal(), and return a valid struct iscsi_login. | ||
1081 | */ | ||
1082 | login = iscsi_target_init_negotiation(np, conn, buffer); | ||
1083 | if (!login) { | ||
1084 | tpg = conn->tpg; | ||
1085 | goto new_sess_out; | ||
1086 | } | ||
1087 | |||
1088 | tpg = conn->tpg; | ||
1089 | if (!tpg) { | ||
1090 | pr_err("Unable to locate struct iscsi_conn->tpg\n"); | ||
1091 | goto new_sess_out; | ||
1092 | } | ||
1093 | |||
1094 | if (zero_tsih) { | ||
1095 | if (iscsi_login_zero_tsih_s2(conn) < 0) { | ||
1096 | iscsi_target_nego_release(login, conn); | ||
1097 | goto new_sess_out; | ||
1098 | } | ||
1099 | } else { | ||
1100 | if (iscsi_login_non_zero_tsih_s2(conn, buffer) < 0) { | ||
1101 | iscsi_target_nego_release(login, conn); | ||
1102 | goto old_sess_out; | ||
1103 | } | ||
1104 | } | ||
1105 | |||
1106 | if (iscsi_target_start_negotiation(login, conn) < 0) | ||
1107 | goto new_sess_out; | ||
1108 | |||
1109 | if (!conn->sess) { | ||
1110 | pr_err("struct iscsi_conn session pointer is NULL!\n"); | ||
1111 | goto new_sess_out; | ||
1112 | } | ||
1113 | |||
1114 | iscsi_stop_login_thread_timer(np); | ||
1115 | |||
1116 | if (signal_pending(current)) | ||
1117 | goto new_sess_out; | ||
1118 | |||
1119 | ret = iscsi_post_login_handler(np, conn, zero_tsih); | ||
1120 | |||
1121 | if (ret < 0) | ||
1122 | goto new_sess_out; | ||
1123 | |||
1124 | iscsit_deaccess_np(np, tpg); | ||
1125 | tpg = NULL; | ||
1126 | /* Get another socket */ | ||
1127 | return 1; | ||
1128 | |||
1129 | new_sess_out: | ||
1130 | pr_err("iSCSI Login negotiation failed.\n"); | ||
1131 | iscsit_collect_login_stats(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, | ||
1132 | ISCSI_LOGIN_STATUS_INIT_ERR); | ||
1133 | if (!zero_tsih || !conn->sess) | ||
1134 | goto old_sess_out; | ||
1135 | if (conn->sess->se_sess) | ||
1136 | transport_free_session(conn->sess->se_sess); | ||
1137 | if (conn->sess->session_index != 0) { | ||
1138 | spin_lock_bh(&sess_idr_lock); | ||
1139 | idr_remove(&sess_idr, conn->sess->session_index); | ||
1140 | spin_unlock_bh(&sess_idr_lock); | ||
1141 | } | ||
1142 | if (conn->sess->sess_ops) | ||
1143 | kfree(conn->sess->sess_ops); | ||
1144 | if (conn->sess) | ||
1145 | kfree(conn->sess); | ||
1146 | old_sess_out: | ||
1147 | iscsi_stop_login_thread_timer(np); | ||
1148 | /* | ||
1149 | * If login negotiation fails check if the Time2Retain timer | ||
1150 | * needs to be restarted. | ||
1151 | */ | ||
1152 | if (!zero_tsih && conn->sess) { | ||
1153 | spin_lock_bh(&conn->sess->conn_lock); | ||
1154 | if (conn->sess->session_state == TARG_SESS_STATE_FAILED) { | ||
1155 | struct se_portal_group *se_tpg = | ||
1156 | &ISCSI_TPG_C(conn)->tpg_se_tpg; | ||
1157 | |||
1158 | atomic_set(&conn->sess->session_continuation, 0); | ||
1159 | spin_unlock_bh(&conn->sess->conn_lock); | ||
1160 | spin_lock_bh(&se_tpg->session_lock); | ||
1161 | iscsit_start_time2retain_handler(conn->sess); | ||
1162 | spin_unlock_bh(&se_tpg->session_lock); | ||
1163 | } else | ||
1164 | spin_unlock_bh(&conn->sess->conn_lock); | ||
1165 | iscsit_dec_session_usage_count(conn->sess); | ||
1166 | } | ||
1167 | |||
1168 | if (!IS_ERR(conn->conn_rx_hash.tfm)) | ||
1169 | crypto_free_hash(conn->conn_rx_hash.tfm); | ||
1170 | if (!IS_ERR(conn->conn_tx_hash.tfm)) | ||
1171 | crypto_free_hash(conn->conn_tx_hash.tfm); | ||
1172 | |||
1173 | if (conn->conn_cpumask) | ||
1174 | free_cpumask_var(conn->conn_cpumask); | ||
1175 | |||
1176 | kfree(conn->conn_ops); | ||
1177 | |||
1178 | if (conn->param_list) { | ||
1179 | iscsi_release_param_list(conn->param_list); | ||
1180 | conn->param_list = NULL; | ||
1181 | } | ||
1182 | if (conn->sock) { | ||
1183 | if (conn->conn_flags & CONNFLAG_SCTP_STRUCT_FILE) { | ||
1184 | kfree(conn->sock->file); | ||
1185 | conn->sock->file = NULL; | ||
1186 | } | ||
1187 | sock_release(conn->sock); | ||
1188 | } | ||
1189 | kfree(conn); | ||
1190 | |||
1191 | if (tpg) { | ||
1192 | iscsit_deaccess_np(np, tpg); | ||
1193 | tpg = NULL; | ||
1194 | } | ||
1195 | |||
1196 | out: | ||
1197 | stop = kthread_should_stop(); | ||
1198 | if (!stop && signal_pending(current)) { | ||
1199 | spin_lock_bh(&np->np_thread_lock); | ||
1200 | stop = (np->np_thread_state == ISCSI_NP_THREAD_SHUTDOWN); | ||
1201 | spin_unlock_bh(&np->np_thread_lock); | ||
1202 | } | ||
1203 | /* Wait for another socket.. */ | ||
1204 | if (!stop) | ||
1205 | return 1; | ||
1206 | |||
1207 | iscsi_stop_login_thread_timer(np); | ||
1208 | spin_lock_bh(&np->np_thread_lock); | ||
1209 | np->np_thread_state = ISCSI_NP_THREAD_EXIT; | ||
1210 | spin_unlock_bh(&np->np_thread_lock); | ||
1211 | return 0; | ||
1212 | } | ||
1213 | |||
1214 | int iscsi_target_login_thread(void *arg) | ||
1215 | { | ||
1216 | struct iscsi_np *np = (struct iscsi_np *)arg; | ||
1217 | int ret; | ||
1218 | |||
1219 | allow_signal(SIGINT); | ||
1220 | |||
1221 | while (!kthread_should_stop()) { | ||
1222 | ret = __iscsi_target_login_thread(np); | ||
1223 | /* | ||
1224 | * We break and exit here unless another sock_accept() call | ||
1225 | * is expected. | ||
1226 | */ | ||
1227 | if (ret != 1) | ||
1228 | break; | ||
1229 | } | ||
1230 | |||
1231 | return 0; | ||
1232 | } | ||
diff --git a/drivers/target/iscsi/iscsi_target_login.h b/drivers/target/iscsi/iscsi_target_login.h new file mode 100644 index 000000000000..091dcae2532b --- /dev/null +++ b/drivers/target/iscsi/iscsi_target_login.h | |||
@@ -0,0 +1,12 @@ | |||
1 | #ifndef ISCSI_TARGET_LOGIN_H | ||
2 | #define ISCSI_TARGET_LOGIN_H | ||
3 | |||
4 | extern int iscsi_login_setup_crypto(struct iscsi_conn *); | ||
5 | extern int iscsi_check_for_session_reinstatement(struct iscsi_conn *); | ||
6 | extern int iscsi_login_post_auth_non_zero_tsih(struct iscsi_conn *, u16, u32); | ||
7 | extern int iscsi_target_setup_login_socket(struct iscsi_np *, | ||
8 | struct __kernel_sockaddr_storage *); | ||
9 | extern int iscsi_target_login_thread(void *); | ||
10 | extern int iscsi_login_disable_FIM_keys(struct iscsi_param_list *, struct iscsi_conn *); | ||
11 | |||
12 | #endif /*** ISCSI_TARGET_LOGIN_H ***/ | ||
diff --git a/drivers/target/iscsi/iscsi_target_nego.c b/drivers/target/iscsi/iscsi_target_nego.c new file mode 100644 index 000000000000..713a4d23557a --- /dev/null +++ b/drivers/target/iscsi/iscsi_target_nego.c | |||
@@ -0,0 +1,1067 @@ | |||
1 | /******************************************************************************* | ||
2 | * This file contains main functions related to iSCSI Parameter negotiation. | ||
3 | * | ||
4 | * \u00a9 Copyright 2007-2011 RisingTide Systems LLC. | ||
5 | * | ||
6 | * Licensed to the Linux Foundation under the General Public License (GPL) version 2. | ||
7 | * | ||
8 | * Author: Nicholas A. Bellinger <nab@linux-iscsi.org> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | ******************************************************************************/ | ||
20 | |||
21 | #include <linux/ctype.h> | ||
22 | #include <scsi/iscsi_proto.h> | ||
23 | #include <target/target_core_base.h> | ||
24 | #include <target/target_core_tpg.h> | ||
25 | |||
26 | #include "iscsi_target_core.h" | ||
27 | #include "iscsi_target_parameters.h" | ||
28 | #include "iscsi_target_login.h" | ||
29 | #include "iscsi_target_nego.h" | ||
30 | #include "iscsi_target_tpg.h" | ||
31 | #include "iscsi_target_util.h" | ||
32 | #include "iscsi_target.h" | ||
33 | #include "iscsi_target_auth.h" | ||
34 | |||
35 | #define MAX_LOGIN_PDUS 7 | ||
36 | #define TEXT_LEN 4096 | ||
37 | |||
38 | void convert_null_to_semi(char *buf, int len) | ||
39 | { | ||
40 | int i; | ||
41 | |||
42 | for (i = 0; i < len; i++) | ||
43 | if (buf[i] == '\0') | ||
44 | buf[i] = ';'; | ||
45 | } | ||
46 | |||
47 | int strlen_semi(char *buf) | ||
48 | { | ||
49 | int i = 0; | ||
50 | |||
51 | while (buf[i] != '\0') { | ||
52 | if (buf[i] == ';') | ||
53 | return i; | ||
54 | i++; | ||
55 | } | ||
56 | |||
57 | return -1; | ||
58 | } | ||
59 | |||
60 | int extract_param( | ||
61 | const char *in_buf, | ||
62 | const char *pattern, | ||
63 | unsigned int max_length, | ||
64 | char *out_buf, | ||
65 | unsigned char *type) | ||
66 | { | ||
67 | char *ptr; | ||
68 | int len; | ||
69 | |||
70 | if (!in_buf || !pattern || !out_buf || !type) | ||
71 | return -1; | ||
72 | |||
73 | ptr = strstr(in_buf, pattern); | ||
74 | if (!ptr) | ||
75 | return -1; | ||
76 | |||
77 | ptr = strstr(ptr, "="); | ||
78 | if (!ptr) | ||
79 | return -1; | ||
80 | |||
81 | ptr += 1; | ||
82 | if (*ptr == '0' && (*(ptr+1) == 'x' || *(ptr+1) == 'X')) { | ||
83 | ptr += 2; /* skip 0x */ | ||
84 | *type = HEX; | ||
85 | } else | ||
86 | *type = DECIMAL; | ||
87 | |||
88 | len = strlen_semi(ptr); | ||
89 | if (len < 0) | ||
90 | return -1; | ||
91 | |||
92 | if (len > max_length) { | ||
93 | pr_err("Length of input: %d exeeds max_length:" | ||
94 | " %d\n", len, max_length); | ||
95 | return -1; | ||
96 | } | ||
97 | memcpy(out_buf, ptr, len); | ||
98 | out_buf[len] = '\0'; | ||
99 | |||
100 | return 0; | ||
101 | } | ||
102 | |||
103 | static u32 iscsi_handle_authentication( | ||
104 | struct iscsi_conn *conn, | ||
105 | char *in_buf, | ||
106 | char *out_buf, | ||
107 | int in_length, | ||
108 | int *out_length, | ||
109 | unsigned char *authtype) | ||
110 | { | ||
111 | struct iscsi_session *sess = conn->sess; | ||
112 | struct iscsi_node_auth *auth; | ||
113 | struct iscsi_node_acl *iscsi_nacl; | ||
114 | struct se_node_acl *se_nacl; | ||
115 | |||
116 | if (!sess->sess_ops->SessionType) { | ||
117 | /* | ||
118 | * For SessionType=Normal | ||
119 | */ | ||
120 | se_nacl = conn->sess->se_sess->se_node_acl; | ||
121 | if (!se_nacl) { | ||
122 | pr_err("Unable to locate struct se_node_acl for" | ||
123 | " CHAP auth\n"); | ||
124 | return -1; | ||
125 | } | ||
126 | iscsi_nacl = container_of(se_nacl, struct iscsi_node_acl, | ||
127 | se_node_acl); | ||
128 | if (!iscsi_nacl) { | ||
129 | pr_err("Unable to locate struct iscsi_node_acl for" | ||
130 | " CHAP auth\n"); | ||
131 | return -1; | ||
132 | } | ||
133 | |||
134 | auth = ISCSI_NODE_AUTH(iscsi_nacl); | ||
135 | } else { | ||
136 | /* | ||
137 | * For SessionType=Discovery | ||
138 | */ | ||
139 | auth = &iscsit_global->discovery_acl.node_auth; | ||
140 | } | ||
141 | |||
142 | if (strstr("CHAP", authtype)) | ||
143 | strcpy(conn->sess->auth_type, "CHAP"); | ||
144 | else | ||
145 | strcpy(conn->sess->auth_type, NONE); | ||
146 | |||
147 | if (strstr("None", authtype)) | ||
148 | return 1; | ||
149 | #ifdef CANSRP | ||
150 | else if (strstr("SRP", authtype)) | ||
151 | return srp_main_loop(conn, auth, in_buf, out_buf, | ||
152 | &in_length, out_length); | ||
153 | #endif | ||
154 | else if (strstr("CHAP", authtype)) | ||
155 | return chap_main_loop(conn, auth, in_buf, out_buf, | ||
156 | &in_length, out_length); | ||
157 | else if (strstr("SPKM1", authtype)) | ||
158 | return 2; | ||
159 | else if (strstr("SPKM2", authtype)) | ||
160 | return 2; | ||
161 | else if (strstr("KRB5", authtype)) | ||
162 | return 2; | ||
163 | else | ||
164 | return 2; | ||
165 | } | ||
166 | |||
167 | static void iscsi_remove_failed_auth_entry(struct iscsi_conn *conn) | ||
168 | { | ||
169 | kfree(conn->auth_protocol); | ||
170 | } | ||
171 | |||
172 | static int iscsi_target_check_login_request( | ||
173 | struct iscsi_conn *conn, | ||
174 | struct iscsi_login *login) | ||
175 | { | ||
176 | int req_csg, req_nsg, rsp_csg, rsp_nsg; | ||
177 | u32 payload_length; | ||
178 | struct iscsi_login_req *login_req; | ||
179 | struct iscsi_login_rsp *login_rsp; | ||
180 | |||
181 | login_req = (struct iscsi_login_req *) login->req; | ||
182 | login_rsp = (struct iscsi_login_rsp *) login->rsp; | ||
183 | payload_length = ntoh24(login_req->dlength); | ||
184 | |||
185 | switch (login_req->opcode & ISCSI_OPCODE_MASK) { | ||
186 | case ISCSI_OP_LOGIN: | ||
187 | break; | ||
188 | default: | ||
189 | pr_err("Received unknown opcode 0x%02x.\n", | ||
190 | login_req->opcode & ISCSI_OPCODE_MASK); | ||
191 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, | ||
192 | ISCSI_LOGIN_STATUS_INIT_ERR); | ||
193 | return -1; | ||
194 | } | ||
195 | |||
196 | if ((login_req->flags & ISCSI_FLAG_LOGIN_CONTINUE) && | ||
197 | (login_req->flags & ISCSI_FLAG_LOGIN_TRANSIT)) { | ||
198 | pr_err("Login request has both ISCSI_FLAG_LOGIN_CONTINUE" | ||
199 | " and ISCSI_FLAG_LOGIN_TRANSIT set, protocol error.\n"); | ||
200 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, | ||
201 | ISCSI_LOGIN_STATUS_INIT_ERR); | ||
202 | return -1; | ||
203 | } | ||
204 | |||
205 | req_csg = (login_req->flags & ISCSI_FLAG_LOGIN_CURRENT_STAGE_MASK) >> 2; | ||
206 | rsp_csg = (login_rsp->flags & ISCSI_FLAG_LOGIN_CURRENT_STAGE_MASK) >> 2; | ||
207 | req_nsg = (login_req->flags & ISCSI_FLAG_LOGIN_NEXT_STAGE_MASK); | ||
208 | rsp_nsg = (login_rsp->flags & ISCSI_FLAG_LOGIN_NEXT_STAGE_MASK); | ||
209 | |||
210 | if (req_csg != login->current_stage) { | ||
211 | pr_err("Initiator unexpectedly changed login stage" | ||
212 | " from %d to %d, login failed.\n", login->current_stage, | ||
213 | req_csg); | ||
214 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, | ||
215 | ISCSI_LOGIN_STATUS_INIT_ERR); | ||
216 | return -1; | ||
217 | } | ||
218 | |||
219 | if ((req_nsg == 2) || (req_csg >= 2) || | ||
220 | ((login_req->flags & ISCSI_FLAG_LOGIN_TRANSIT) && | ||
221 | (req_nsg <= req_csg))) { | ||
222 | pr_err("Illegal login_req->flags Combination, CSG: %d," | ||
223 | " NSG: %d, ISCSI_FLAG_LOGIN_TRANSIT: %d.\n", req_csg, | ||
224 | req_nsg, (login_req->flags & ISCSI_FLAG_LOGIN_TRANSIT)); | ||
225 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, | ||
226 | ISCSI_LOGIN_STATUS_INIT_ERR); | ||
227 | return -1; | ||
228 | } | ||
229 | |||
230 | if ((login_req->max_version != login->version_max) || | ||
231 | (login_req->min_version != login->version_min)) { | ||
232 | pr_err("Login request changed Version Max/Nin" | ||
233 | " unexpectedly to 0x%02x/0x%02x, protocol error\n", | ||
234 | login_req->max_version, login_req->min_version); | ||
235 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, | ||
236 | ISCSI_LOGIN_STATUS_INIT_ERR); | ||
237 | return -1; | ||
238 | } | ||
239 | |||
240 | if (memcmp(login_req->isid, login->isid, 6) != 0) { | ||
241 | pr_err("Login request changed ISID unexpectedly," | ||
242 | " protocol error.\n"); | ||
243 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, | ||
244 | ISCSI_LOGIN_STATUS_INIT_ERR); | ||
245 | return -1; | ||
246 | } | ||
247 | |||
248 | if (login_req->itt != login->init_task_tag) { | ||
249 | pr_err("Login request changed ITT unexpectedly to" | ||
250 | " 0x%08x, protocol error.\n", login_req->itt); | ||
251 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, | ||
252 | ISCSI_LOGIN_STATUS_INIT_ERR); | ||
253 | return -1; | ||
254 | } | ||
255 | |||
256 | if (payload_length > MAX_KEY_VALUE_PAIRS) { | ||
257 | pr_err("Login request payload exceeds default" | ||
258 | " MaxRecvDataSegmentLength: %u, protocol error.\n", | ||
259 | MAX_KEY_VALUE_PAIRS); | ||
260 | return -1; | ||
261 | } | ||
262 | |||
263 | return 0; | ||
264 | } | ||
265 | |||
266 | static int iscsi_target_check_first_request( | ||
267 | struct iscsi_conn *conn, | ||
268 | struct iscsi_login *login) | ||
269 | { | ||
270 | struct iscsi_param *param = NULL; | ||
271 | struct se_node_acl *se_nacl; | ||
272 | |||
273 | login->first_request = 0; | ||
274 | |||
275 | list_for_each_entry(param, &conn->param_list->param_list, p_list) { | ||
276 | if (!strncmp(param->name, SESSIONTYPE, 11)) { | ||
277 | if (!IS_PSTATE_ACCEPTOR(param)) { | ||
278 | pr_err("SessionType key not received" | ||
279 | " in first login request.\n"); | ||
280 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, | ||
281 | ISCSI_LOGIN_STATUS_MISSING_FIELDS); | ||
282 | return -1; | ||
283 | } | ||
284 | if (!strncmp(param->value, DISCOVERY, 9)) | ||
285 | return 0; | ||
286 | } | ||
287 | |||
288 | if (!strncmp(param->name, INITIATORNAME, 13)) { | ||
289 | if (!IS_PSTATE_ACCEPTOR(param)) { | ||
290 | if (!login->leading_connection) | ||
291 | continue; | ||
292 | |||
293 | pr_err("InitiatorName key not received" | ||
294 | " in first login request.\n"); | ||
295 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, | ||
296 | ISCSI_LOGIN_STATUS_MISSING_FIELDS); | ||
297 | return -1; | ||
298 | } | ||
299 | |||
300 | /* | ||
301 | * For non-leading connections, double check that the | ||
302 | * received InitiatorName matches the existing session's | ||
303 | * struct iscsi_node_acl. | ||
304 | */ | ||
305 | if (!login->leading_connection) { | ||
306 | se_nacl = conn->sess->se_sess->se_node_acl; | ||
307 | if (!se_nacl) { | ||
308 | pr_err("Unable to locate" | ||
309 | " struct se_node_acl\n"); | ||
310 | iscsit_tx_login_rsp(conn, | ||
311 | ISCSI_STATUS_CLS_INITIATOR_ERR, | ||
312 | ISCSI_LOGIN_STATUS_TGT_NOT_FOUND); | ||
313 | return -1; | ||
314 | } | ||
315 | |||
316 | if (strcmp(param->value, | ||
317 | se_nacl->initiatorname)) { | ||
318 | pr_err("Incorrect" | ||
319 | " InitiatorName: %s for this" | ||
320 | " iSCSI Initiator Node.\n", | ||
321 | param->value); | ||
322 | iscsit_tx_login_rsp(conn, | ||
323 | ISCSI_STATUS_CLS_INITIATOR_ERR, | ||
324 | ISCSI_LOGIN_STATUS_TGT_NOT_FOUND); | ||
325 | return -1; | ||
326 | } | ||
327 | } | ||
328 | } | ||
329 | } | ||
330 | |||
331 | return 0; | ||
332 | } | ||
333 | |||
334 | static int iscsi_target_do_tx_login_io(struct iscsi_conn *conn, struct iscsi_login *login) | ||
335 | { | ||
336 | u32 padding = 0; | ||
337 | struct iscsi_session *sess = conn->sess; | ||
338 | struct iscsi_login_rsp *login_rsp; | ||
339 | |||
340 | login_rsp = (struct iscsi_login_rsp *) login->rsp; | ||
341 | |||
342 | login_rsp->opcode = ISCSI_OP_LOGIN_RSP; | ||
343 | hton24(login_rsp->dlength, login->rsp_length); | ||
344 | memcpy(login_rsp->isid, login->isid, 6); | ||
345 | login_rsp->tsih = cpu_to_be16(login->tsih); | ||
346 | login_rsp->itt = cpu_to_be32(login->init_task_tag); | ||
347 | login_rsp->statsn = cpu_to_be32(conn->stat_sn++); | ||
348 | login_rsp->exp_cmdsn = cpu_to_be32(conn->sess->exp_cmd_sn); | ||
349 | login_rsp->max_cmdsn = cpu_to_be32(conn->sess->max_cmd_sn); | ||
350 | |||
351 | pr_debug("Sending Login Response, Flags: 0x%02x, ITT: 0x%08x," | ||
352 | " ExpCmdSN; 0x%08x, MaxCmdSN: 0x%08x, StatSN: 0x%08x, Length:" | ||
353 | " %u\n", login_rsp->flags, ntohl(login_rsp->itt), | ||
354 | ntohl(login_rsp->exp_cmdsn), ntohl(login_rsp->max_cmdsn), | ||
355 | ntohl(login_rsp->statsn), login->rsp_length); | ||
356 | |||
357 | padding = ((-login->rsp_length) & 3); | ||
358 | |||
359 | if (iscsi_login_tx_data( | ||
360 | conn, | ||
361 | login->rsp, | ||
362 | login->rsp_buf, | ||
363 | login->rsp_length + padding) < 0) | ||
364 | return -1; | ||
365 | |||
366 | login->rsp_length = 0; | ||
367 | login_rsp->tsih = be16_to_cpu(login_rsp->tsih); | ||
368 | login_rsp->itt = be32_to_cpu(login_rsp->itt); | ||
369 | login_rsp->statsn = be32_to_cpu(login_rsp->statsn); | ||
370 | mutex_lock(&sess->cmdsn_mutex); | ||
371 | login_rsp->exp_cmdsn = be32_to_cpu(sess->exp_cmd_sn); | ||
372 | login_rsp->max_cmdsn = be32_to_cpu(sess->max_cmd_sn); | ||
373 | mutex_unlock(&sess->cmdsn_mutex); | ||
374 | |||
375 | return 0; | ||
376 | } | ||
377 | |||
378 | static int iscsi_target_do_rx_login_io(struct iscsi_conn *conn, struct iscsi_login *login) | ||
379 | { | ||
380 | u32 padding = 0, payload_length; | ||
381 | struct iscsi_login_req *login_req; | ||
382 | |||
383 | if (iscsi_login_rx_data(conn, login->req, ISCSI_HDR_LEN) < 0) | ||
384 | return -1; | ||
385 | |||
386 | login_req = (struct iscsi_login_req *) login->req; | ||
387 | payload_length = ntoh24(login_req->dlength); | ||
388 | login_req->tsih = be16_to_cpu(login_req->tsih); | ||
389 | login_req->itt = be32_to_cpu(login_req->itt); | ||
390 | login_req->cid = be16_to_cpu(login_req->cid); | ||
391 | login_req->cmdsn = be32_to_cpu(login_req->cmdsn); | ||
392 | login_req->exp_statsn = be32_to_cpu(login_req->exp_statsn); | ||
393 | |||
394 | pr_debug("Got Login Command, Flags 0x%02x, ITT: 0x%08x," | ||
395 | " CmdSN: 0x%08x, ExpStatSN: 0x%08x, CID: %hu, Length: %u\n", | ||
396 | login_req->flags, login_req->itt, login_req->cmdsn, | ||
397 | login_req->exp_statsn, login_req->cid, payload_length); | ||
398 | |||
399 | if (iscsi_target_check_login_request(conn, login) < 0) | ||
400 | return -1; | ||
401 | |||
402 | padding = ((-payload_length) & 3); | ||
403 | memset(login->req_buf, 0, MAX_KEY_VALUE_PAIRS); | ||
404 | |||
405 | if (iscsi_login_rx_data( | ||
406 | conn, | ||
407 | login->req_buf, | ||
408 | payload_length + padding) < 0) | ||
409 | return -1; | ||
410 | |||
411 | return 0; | ||
412 | } | ||
413 | |||
414 | static int iscsi_target_do_login_io(struct iscsi_conn *conn, struct iscsi_login *login) | ||
415 | { | ||
416 | if (iscsi_target_do_tx_login_io(conn, login) < 0) | ||
417 | return -1; | ||
418 | |||
419 | if (iscsi_target_do_rx_login_io(conn, login) < 0) | ||
420 | return -1; | ||
421 | |||
422 | return 0; | ||
423 | } | ||
424 | |||
425 | static int iscsi_target_get_initial_payload( | ||
426 | struct iscsi_conn *conn, | ||
427 | struct iscsi_login *login) | ||
428 | { | ||
429 | u32 padding = 0, payload_length; | ||
430 | struct iscsi_login_req *login_req; | ||
431 | |||
432 | login_req = (struct iscsi_login_req *) login->req; | ||
433 | payload_length = ntoh24(login_req->dlength); | ||
434 | |||
435 | pr_debug("Got Login Command, Flags 0x%02x, ITT: 0x%08x," | ||
436 | " CmdSN: 0x%08x, ExpStatSN: 0x%08x, Length: %u\n", | ||
437 | login_req->flags, login_req->itt, login_req->cmdsn, | ||
438 | login_req->exp_statsn, payload_length); | ||
439 | |||
440 | if (iscsi_target_check_login_request(conn, login) < 0) | ||
441 | return -1; | ||
442 | |||
443 | padding = ((-payload_length) & 3); | ||
444 | |||
445 | if (iscsi_login_rx_data( | ||
446 | conn, | ||
447 | login->req_buf, | ||
448 | payload_length + padding) < 0) | ||
449 | return -1; | ||
450 | |||
451 | return 0; | ||
452 | } | ||
453 | |||
454 | /* | ||
455 | * NOTE: We check for existing sessions or connections AFTER the initiator | ||
456 | * has been successfully authenticated in order to protect against faked | ||
457 | * ISID/TSIH combinations. | ||
458 | */ | ||
459 | static int iscsi_target_check_for_existing_instances( | ||
460 | struct iscsi_conn *conn, | ||
461 | struct iscsi_login *login) | ||
462 | { | ||
463 | if (login->checked_for_existing) | ||
464 | return 0; | ||
465 | |||
466 | login->checked_for_existing = 1; | ||
467 | |||
468 | if (!login->tsih) | ||
469 | return iscsi_check_for_session_reinstatement(conn); | ||
470 | else | ||
471 | return iscsi_login_post_auth_non_zero_tsih(conn, login->cid, | ||
472 | login->initial_exp_statsn); | ||
473 | } | ||
474 | |||
475 | static int iscsi_target_do_authentication( | ||
476 | struct iscsi_conn *conn, | ||
477 | struct iscsi_login *login) | ||
478 | { | ||
479 | int authret; | ||
480 | u32 payload_length; | ||
481 | struct iscsi_param *param; | ||
482 | struct iscsi_login_req *login_req; | ||
483 | struct iscsi_login_rsp *login_rsp; | ||
484 | |||
485 | login_req = (struct iscsi_login_req *) login->req; | ||
486 | login_rsp = (struct iscsi_login_rsp *) login->rsp; | ||
487 | payload_length = ntoh24(login_req->dlength); | ||
488 | |||
489 | param = iscsi_find_param_from_key(AUTHMETHOD, conn->param_list); | ||
490 | if (!param) | ||
491 | return -1; | ||
492 | |||
493 | authret = iscsi_handle_authentication( | ||
494 | conn, | ||
495 | login->req_buf, | ||
496 | login->rsp_buf, | ||
497 | payload_length, | ||
498 | &login->rsp_length, | ||
499 | param->value); | ||
500 | switch (authret) { | ||
501 | case 0: | ||
502 | pr_debug("Received OK response" | ||
503 | " from LIO Authentication, continuing.\n"); | ||
504 | break; | ||
505 | case 1: | ||
506 | pr_debug("iSCSI security negotiation" | ||
507 | " completed sucessfully.\n"); | ||
508 | login->auth_complete = 1; | ||
509 | if ((login_req->flags & ISCSI_FLAG_LOGIN_NEXT_STAGE1) && | ||
510 | (login_req->flags & ISCSI_FLAG_LOGIN_TRANSIT)) { | ||
511 | login_rsp->flags |= (ISCSI_FLAG_LOGIN_NEXT_STAGE1 | | ||
512 | ISCSI_FLAG_LOGIN_TRANSIT); | ||
513 | login->current_stage = 1; | ||
514 | } | ||
515 | return iscsi_target_check_for_existing_instances( | ||
516 | conn, login); | ||
517 | case 2: | ||
518 | pr_err("Security negotiation" | ||
519 | " failed.\n"); | ||
520 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, | ||
521 | ISCSI_LOGIN_STATUS_AUTH_FAILED); | ||
522 | return -1; | ||
523 | default: | ||
524 | pr_err("Received unknown error %d from LIO" | ||
525 | " Authentication\n", authret); | ||
526 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, | ||
527 | ISCSI_LOGIN_STATUS_TARGET_ERROR); | ||
528 | return -1; | ||
529 | } | ||
530 | |||
531 | return 0; | ||
532 | } | ||
533 | |||
534 | static int iscsi_target_handle_csg_zero( | ||
535 | struct iscsi_conn *conn, | ||
536 | struct iscsi_login *login) | ||
537 | { | ||
538 | int ret; | ||
539 | u32 payload_length; | ||
540 | struct iscsi_param *param; | ||
541 | struct iscsi_login_req *login_req; | ||
542 | struct iscsi_login_rsp *login_rsp; | ||
543 | |||
544 | login_req = (struct iscsi_login_req *) login->req; | ||
545 | login_rsp = (struct iscsi_login_rsp *) login->rsp; | ||
546 | payload_length = ntoh24(login_req->dlength); | ||
547 | |||
548 | param = iscsi_find_param_from_key(AUTHMETHOD, conn->param_list); | ||
549 | if (!param) | ||
550 | return -1; | ||
551 | |||
552 | ret = iscsi_decode_text_input( | ||
553 | PHASE_SECURITY|PHASE_DECLARATIVE, | ||
554 | SENDER_INITIATOR|SENDER_RECEIVER, | ||
555 | login->req_buf, | ||
556 | payload_length, | ||
557 | conn->param_list); | ||
558 | if (ret < 0) | ||
559 | return -1; | ||
560 | |||
561 | if (ret > 0) { | ||
562 | if (login->auth_complete) { | ||
563 | pr_err("Initiator has already been" | ||
564 | " successfully authenticated, but is still" | ||
565 | " sending %s keys.\n", param->value); | ||
566 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, | ||
567 | ISCSI_LOGIN_STATUS_INIT_ERR); | ||
568 | return -1; | ||
569 | } | ||
570 | |||
571 | goto do_auth; | ||
572 | } | ||
573 | |||
574 | if (login->first_request) | ||
575 | if (iscsi_target_check_first_request(conn, login) < 0) | ||
576 | return -1; | ||
577 | |||
578 | ret = iscsi_encode_text_output( | ||
579 | PHASE_SECURITY|PHASE_DECLARATIVE, | ||
580 | SENDER_TARGET, | ||
581 | login->rsp_buf, | ||
582 | &login->rsp_length, | ||
583 | conn->param_list); | ||
584 | if (ret < 0) | ||
585 | return -1; | ||
586 | |||
587 | if (!iscsi_check_negotiated_keys(conn->param_list)) { | ||
588 | if (ISCSI_TPG_ATTRIB(ISCSI_TPG_C(conn))->authentication && | ||
589 | !strncmp(param->value, NONE, 4)) { | ||
590 | pr_err("Initiator sent AuthMethod=None but" | ||
591 | " Target is enforcing iSCSI Authentication," | ||
592 | " login failed.\n"); | ||
593 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, | ||
594 | ISCSI_LOGIN_STATUS_AUTH_FAILED); | ||
595 | return -1; | ||
596 | } | ||
597 | |||
598 | if (ISCSI_TPG_ATTRIB(ISCSI_TPG_C(conn))->authentication && | ||
599 | !login->auth_complete) | ||
600 | return 0; | ||
601 | |||
602 | if (strncmp(param->value, NONE, 4) && !login->auth_complete) | ||
603 | return 0; | ||
604 | |||
605 | if ((login_req->flags & ISCSI_FLAG_LOGIN_NEXT_STAGE1) && | ||
606 | (login_req->flags & ISCSI_FLAG_LOGIN_TRANSIT)) { | ||
607 | login_rsp->flags |= ISCSI_FLAG_LOGIN_NEXT_STAGE1 | | ||
608 | ISCSI_FLAG_LOGIN_TRANSIT; | ||
609 | login->current_stage = 1; | ||
610 | } | ||
611 | } | ||
612 | |||
613 | return 0; | ||
614 | do_auth: | ||
615 | return iscsi_target_do_authentication(conn, login); | ||
616 | } | ||
617 | |||
618 | static int iscsi_target_handle_csg_one(struct iscsi_conn *conn, struct iscsi_login *login) | ||
619 | { | ||
620 | int ret; | ||
621 | u32 payload_length; | ||
622 | struct iscsi_login_req *login_req; | ||
623 | struct iscsi_login_rsp *login_rsp; | ||
624 | |||
625 | login_req = (struct iscsi_login_req *) login->req; | ||
626 | login_rsp = (struct iscsi_login_rsp *) login->rsp; | ||
627 | payload_length = ntoh24(login_req->dlength); | ||
628 | |||
629 | ret = iscsi_decode_text_input( | ||
630 | PHASE_OPERATIONAL|PHASE_DECLARATIVE, | ||
631 | SENDER_INITIATOR|SENDER_RECEIVER, | ||
632 | login->req_buf, | ||
633 | payload_length, | ||
634 | conn->param_list); | ||
635 | if (ret < 0) | ||
636 | return -1; | ||
637 | |||
638 | if (login->first_request) | ||
639 | if (iscsi_target_check_first_request(conn, login) < 0) | ||
640 | return -1; | ||
641 | |||
642 | if (iscsi_target_check_for_existing_instances(conn, login) < 0) | ||
643 | return -1; | ||
644 | |||
645 | ret = iscsi_encode_text_output( | ||
646 | PHASE_OPERATIONAL|PHASE_DECLARATIVE, | ||
647 | SENDER_TARGET, | ||
648 | login->rsp_buf, | ||
649 | &login->rsp_length, | ||
650 | conn->param_list); | ||
651 | if (ret < 0) | ||
652 | return -1; | ||
653 | |||
654 | if (!login->auth_complete && | ||
655 | ISCSI_TPG_ATTRIB(ISCSI_TPG_C(conn))->authentication) { | ||
656 | pr_err("Initiator is requesting CSG: 1, has not been" | ||
657 | " successfully authenticated, and the Target is" | ||
658 | " enforcing iSCSI Authentication, login failed.\n"); | ||
659 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, | ||
660 | ISCSI_LOGIN_STATUS_AUTH_FAILED); | ||
661 | return -1; | ||
662 | } | ||
663 | |||
664 | if (!iscsi_check_negotiated_keys(conn->param_list)) | ||
665 | if ((login_req->flags & ISCSI_FLAG_LOGIN_NEXT_STAGE3) && | ||
666 | (login_req->flags & ISCSI_FLAG_LOGIN_TRANSIT)) | ||
667 | login_rsp->flags |= ISCSI_FLAG_LOGIN_NEXT_STAGE3 | | ||
668 | ISCSI_FLAG_LOGIN_TRANSIT; | ||
669 | |||
670 | return 0; | ||
671 | } | ||
672 | |||
673 | static int iscsi_target_do_login(struct iscsi_conn *conn, struct iscsi_login *login) | ||
674 | { | ||
675 | int pdu_count = 0; | ||
676 | struct iscsi_login_req *login_req; | ||
677 | struct iscsi_login_rsp *login_rsp; | ||
678 | |||
679 | login_req = (struct iscsi_login_req *) login->req; | ||
680 | login_rsp = (struct iscsi_login_rsp *) login->rsp; | ||
681 | |||
682 | while (1) { | ||
683 | if (++pdu_count > MAX_LOGIN_PDUS) { | ||
684 | pr_err("MAX_LOGIN_PDUS count reached.\n"); | ||
685 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, | ||
686 | ISCSI_LOGIN_STATUS_TARGET_ERROR); | ||
687 | return -1; | ||
688 | } | ||
689 | |||
690 | switch ((login_req->flags & ISCSI_FLAG_LOGIN_CURRENT_STAGE_MASK) >> 2) { | ||
691 | case 0: | ||
692 | login_rsp->flags |= (0 & ISCSI_FLAG_LOGIN_CURRENT_STAGE_MASK); | ||
693 | if (iscsi_target_handle_csg_zero(conn, login) < 0) | ||
694 | return -1; | ||
695 | break; | ||
696 | case 1: | ||
697 | login_rsp->flags |= ISCSI_FLAG_LOGIN_CURRENT_STAGE1; | ||
698 | if (iscsi_target_handle_csg_one(conn, login) < 0) | ||
699 | return -1; | ||
700 | if (login_rsp->flags & ISCSI_FLAG_LOGIN_TRANSIT) { | ||
701 | login->tsih = conn->sess->tsih; | ||
702 | if (iscsi_target_do_tx_login_io(conn, | ||
703 | login) < 0) | ||
704 | return -1; | ||
705 | return 0; | ||
706 | } | ||
707 | break; | ||
708 | default: | ||
709 | pr_err("Illegal CSG: %d received from" | ||
710 | " Initiator, protocol error.\n", | ||
711 | (login_req->flags & ISCSI_FLAG_LOGIN_CURRENT_STAGE_MASK) | ||
712 | >> 2); | ||
713 | break; | ||
714 | } | ||
715 | |||
716 | if (iscsi_target_do_login_io(conn, login) < 0) | ||
717 | return -1; | ||
718 | |||
719 | if (login_rsp->flags & ISCSI_FLAG_LOGIN_TRANSIT) { | ||
720 | login_rsp->flags &= ~ISCSI_FLAG_LOGIN_TRANSIT; | ||
721 | login_rsp->flags &= ~ISCSI_FLAG_LOGIN_NEXT_STAGE_MASK; | ||
722 | } | ||
723 | } | ||
724 | |||
725 | return 0; | ||
726 | } | ||
727 | |||
728 | static void iscsi_initiatorname_tolower( | ||
729 | char *param_buf) | ||
730 | { | ||
731 | char *c; | ||
732 | u32 iqn_size = strlen(param_buf), i; | ||
733 | |||
734 | for (i = 0; i < iqn_size; i++) { | ||
735 | c = (char *)¶m_buf[i]; | ||
736 | if (!isupper(*c)) | ||
737 | continue; | ||
738 | |||
739 | *c = tolower(*c); | ||
740 | } | ||
741 | } | ||
742 | |||
743 | /* | ||
744 | * Processes the first Login Request.. | ||
745 | */ | ||
746 | static int iscsi_target_locate_portal( | ||
747 | struct iscsi_np *np, | ||
748 | struct iscsi_conn *conn, | ||
749 | struct iscsi_login *login) | ||
750 | { | ||
751 | char *i_buf = NULL, *s_buf = NULL, *t_buf = NULL; | ||
752 | char *tmpbuf, *start = NULL, *end = NULL, *key, *value; | ||
753 | struct iscsi_session *sess = conn->sess; | ||
754 | struct iscsi_tiqn *tiqn; | ||
755 | struct iscsi_login_req *login_req; | ||
756 | struct iscsi_targ_login_rsp *login_rsp; | ||
757 | u32 payload_length; | ||
758 | int sessiontype = 0, ret = 0; | ||
759 | |||
760 | login_req = (struct iscsi_login_req *) login->req; | ||
761 | login_rsp = (struct iscsi_targ_login_rsp *) login->rsp; | ||
762 | payload_length = ntoh24(login_req->dlength); | ||
763 | |||
764 | login->first_request = 1; | ||
765 | login->leading_connection = (!login_req->tsih) ? 1 : 0; | ||
766 | login->current_stage = | ||
767 | (login_req->flags & ISCSI_FLAG_LOGIN_CURRENT_STAGE_MASK) >> 2; | ||
768 | login->version_min = login_req->min_version; | ||
769 | login->version_max = login_req->max_version; | ||
770 | memcpy(login->isid, login_req->isid, 6); | ||
771 | login->cmd_sn = login_req->cmdsn; | ||
772 | login->init_task_tag = login_req->itt; | ||
773 | login->initial_exp_statsn = login_req->exp_statsn; | ||
774 | login->cid = login_req->cid; | ||
775 | login->tsih = login_req->tsih; | ||
776 | |||
777 | if (iscsi_target_get_initial_payload(conn, login) < 0) | ||
778 | return -1; | ||
779 | |||
780 | tmpbuf = kzalloc(payload_length + 1, GFP_KERNEL); | ||
781 | if (!tmpbuf) { | ||
782 | pr_err("Unable to allocate memory for tmpbuf.\n"); | ||
783 | return -1; | ||
784 | } | ||
785 | |||
786 | memcpy(tmpbuf, login->req_buf, payload_length); | ||
787 | tmpbuf[payload_length] = '\0'; | ||
788 | start = tmpbuf; | ||
789 | end = (start + payload_length); | ||
790 | |||
791 | /* | ||
792 | * Locate the initial keys expected from the Initiator node in | ||
793 | * the first login request in order to progress with the login phase. | ||
794 | */ | ||
795 | while (start < end) { | ||
796 | if (iscsi_extract_key_value(start, &key, &value) < 0) { | ||
797 | ret = -1; | ||
798 | goto out; | ||
799 | } | ||
800 | |||
801 | if (!strncmp(key, "InitiatorName", 13)) | ||
802 | i_buf = value; | ||
803 | else if (!strncmp(key, "SessionType", 11)) | ||
804 | s_buf = value; | ||
805 | else if (!strncmp(key, "TargetName", 10)) | ||
806 | t_buf = value; | ||
807 | |||
808 | start += strlen(key) + strlen(value) + 2; | ||
809 | } | ||
810 | |||
811 | /* | ||
812 | * See 5.3. Login Phase. | ||
813 | */ | ||
814 | if (!i_buf) { | ||
815 | pr_err("InitiatorName key not received" | ||
816 | " in first login request.\n"); | ||
817 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, | ||
818 | ISCSI_LOGIN_STATUS_MISSING_FIELDS); | ||
819 | ret = -1; | ||
820 | goto out; | ||
821 | } | ||
822 | /* | ||
823 | * Convert the incoming InitiatorName to lowercase following | ||
824 | * RFC-3720 3.2.6.1. section c) that says that iSCSI IQNs | ||
825 | * are NOT case sensitive. | ||
826 | */ | ||
827 | iscsi_initiatorname_tolower(i_buf); | ||
828 | |||
829 | if (!s_buf) { | ||
830 | if (!login->leading_connection) | ||
831 | goto get_target; | ||
832 | |||
833 | pr_err("SessionType key not received" | ||
834 | " in first login request.\n"); | ||
835 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, | ||
836 | ISCSI_LOGIN_STATUS_MISSING_FIELDS); | ||
837 | ret = -1; | ||
838 | goto out; | ||
839 | } | ||
840 | |||
841 | /* | ||
842 | * Use default portal group for discovery sessions. | ||
843 | */ | ||
844 | sessiontype = strncmp(s_buf, DISCOVERY, 9); | ||
845 | if (!sessiontype) { | ||
846 | conn->tpg = iscsit_global->discovery_tpg; | ||
847 | if (!login->leading_connection) | ||
848 | goto get_target; | ||
849 | |||
850 | sess->sess_ops->SessionType = 1; | ||
851 | /* | ||
852 | * Setup crc32c modules from libcrypto | ||
853 | */ | ||
854 | if (iscsi_login_setup_crypto(conn) < 0) { | ||
855 | pr_err("iscsi_login_setup_crypto() failed\n"); | ||
856 | ret = -1; | ||
857 | goto out; | ||
858 | } | ||
859 | /* | ||
860 | * Serialize access across the discovery struct iscsi_portal_group to | ||
861 | * process login attempt. | ||
862 | */ | ||
863 | if (iscsit_access_np(np, conn->tpg) < 0) { | ||
864 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, | ||
865 | ISCSI_LOGIN_STATUS_SVC_UNAVAILABLE); | ||
866 | ret = -1; | ||
867 | goto out; | ||
868 | } | ||
869 | ret = 0; | ||
870 | goto out; | ||
871 | } | ||
872 | |||
873 | get_target: | ||
874 | if (!t_buf) { | ||
875 | pr_err("TargetName key not received" | ||
876 | " in first login request while" | ||
877 | " SessionType=Normal.\n"); | ||
878 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, | ||
879 | ISCSI_LOGIN_STATUS_MISSING_FIELDS); | ||
880 | ret = -1; | ||
881 | goto out; | ||
882 | } | ||
883 | |||
884 | /* | ||
885 | * Locate Target IQN from Storage Node. | ||
886 | */ | ||
887 | tiqn = iscsit_get_tiqn_for_login(t_buf); | ||
888 | if (!tiqn) { | ||
889 | pr_err("Unable to locate Target IQN: %s in" | ||
890 | " Storage Node\n", t_buf); | ||
891 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, | ||
892 | ISCSI_LOGIN_STATUS_SVC_UNAVAILABLE); | ||
893 | ret = -1; | ||
894 | goto out; | ||
895 | } | ||
896 | pr_debug("Located Storage Object: %s\n", tiqn->tiqn); | ||
897 | |||
898 | /* | ||
899 | * Locate Target Portal Group from Storage Node. | ||
900 | */ | ||
901 | conn->tpg = iscsit_get_tpg_from_np(tiqn, np); | ||
902 | if (!conn->tpg) { | ||
903 | pr_err("Unable to locate Target Portal Group" | ||
904 | " on %s\n", tiqn->tiqn); | ||
905 | iscsit_put_tiqn_for_login(tiqn); | ||
906 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, | ||
907 | ISCSI_LOGIN_STATUS_SVC_UNAVAILABLE); | ||
908 | ret = -1; | ||
909 | goto out; | ||
910 | } | ||
911 | pr_debug("Located Portal Group Object: %hu\n", conn->tpg->tpgt); | ||
912 | /* | ||
913 | * Setup crc32c modules from libcrypto | ||
914 | */ | ||
915 | if (iscsi_login_setup_crypto(conn) < 0) { | ||
916 | pr_err("iscsi_login_setup_crypto() failed\n"); | ||
917 | ret = -1; | ||
918 | goto out; | ||
919 | } | ||
920 | /* | ||
921 | * Serialize access across the struct iscsi_portal_group to | ||
922 | * process login attempt. | ||
923 | */ | ||
924 | if (iscsit_access_np(np, conn->tpg) < 0) { | ||
925 | iscsit_put_tiqn_for_login(tiqn); | ||
926 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, | ||
927 | ISCSI_LOGIN_STATUS_SVC_UNAVAILABLE); | ||
928 | ret = -1; | ||
929 | conn->tpg = NULL; | ||
930 | goto out; | ||
931 | } | ||
932 | |||
933 | /* | ||
934 | * conn->sess->node_acl will be set when the referenced | ||
935 | * struct iscsi_session is located from received ISID+TSIH in | ||
936 | * iscsi_login_non_zero_tsih_s2(). | ||
937 | */ | ||
938 | if (!login->leading_connection) { | ||
939 | ret = 0; | ||
940 | goto out; | ||
941 | } | ||
942 | |||
943 | /* | ||
944 | * This value is required in iscsi_login_zero_tsih_s2() | ||
945 | */ | ||
946 | sess->sess_ops->SessionType = 0; | ||
947 | |||
948 | /* | ||
949 | * Locate incoming Initiator IQN reference from Storage Node. | ||
950 | */ | ||
951 | sess->se_sess->se_node_acl = core_tpg_check_initiator_node_acl( | ||
952 | &conn->tpg->tpg_se_tpg, i_buf); | ||
953 | if (!sess->se_sess->se_node_acl) { | ||
954 | pr_err("iSCSI Initiator Node: %s is not authorized to" | ||
955 | " access iSCSI target portal group: %hu.\n", | ||
956 | i_buf, conn->tpg->tpgt); | ||
957 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, | ||
958 | ISCSI_LOGIN_STATUS_TGT_FORBIDDEN); | ||
959 | ret = -1; | ||
960 | goto out; | ||
961 | } | ||
962 | |||
963 | ret = 0; | ||
964 | out: | ||
965 | kfree(tmpbuf); | ||
966 | return ret; | ||
967 | } | ||
968 | |||
969 | struct iscsi_login *iscsi_target_init_negotiation( | ||
970 | struct iscsi_np *np, | ||
971 | struct iscsi_conn *conn, | ||
972 | char *login_pdu) | ||
973 | { | ||
974 | struct iscsi_login *login; | ||
975 | |||
976 | login = kzalloc(sizeof(struct iscsi_login), GFP_KERNEL); | ||
977 | if (!login) { | ||
978 | pr_err("Unable to allocate memory for struct iscsi_login.\n"); | ||
979 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, | ||
980 | ISCSI_LOGIN_STATUS_NO_RESOURCES); | ||
981 | goto out; | ||
982 | } | ||
983 | |||
984 | login->req = kzalloc(ISCSI_HDR_LEN, GFP_KERNEL); | ||
985 | if (!login->req) { | ||
986 | pr_err("Unable to allocate memory for Login Request.\n"); | ||
987 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, | ||
988 | ISCSI_LOGIN_STATUS_NO_RESOURCES); | ||
989 | goto out; | ||
990 | } | ||
991 | memcpy(login->req, login_pdu, ISCSI_HDR_LEN); | ||
992 | |||
993 | login->req_buf = kzalloc(MAX_KEY_VALUE_PAIRS, GFP_KERNEL); | ||
994 | if (!login->req_buf) { | ||
995 | pr_err("Unable to allocate memory for response buffer.\n"); | ||
996 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, | ||
997 | ISCSI_LOGIN_STATUS_NO_RESOURCES); | ||
998 | goto out; | ||
999 | } | ||
1000 | /* | ||
1001 | * SessionType: Discovery | ||
1002 | * | ||
1003 | * Locates Default Portal | ||
1004 | * | ||
1005 | * SessionType: Normal | ||
1006 | * | ||
1007 | * Locates Target Portal from NP -> Target IQN | ||
1008 | */ | ||
1009 | if (iscsi_target_locate_portal(np, conn, login) < 0) { | ||
1010 | pr_err("iSCSI Login negotiation failed.\n"); | ||
1011 | goto out; | ||
1012 | } | ||
1013 | |||
1014 | return login; | ||
1015 | out: | ||
1016 | kfree(login->req); | ||
1017 | kfree(login->req_buf); | ||
1018 | kfree(login); | ||
1019 | |||
1020 | return NULL; | ||
1021 | } | ||
1022 | |||
1023 | int iscsi_target_start_negotiation( | ||
1024 | struct iscsi_login *login, | ||
1025 | struct iscsi_conn *conn) | ||
1026 | { | ||
1027 | int ret = -1; | ||
1028 | |||
1029 | login->rsp = kzalloc(ISCSI_HDR_LEN, GFP_KERNEL); | ||
1030 | if (!login->rsp) { | ||
1031 | pr_err("Unable to allocate memory for" | ||
1032 | " Login Response.\n"); | ||
1033 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, | ||
1034 | ISCSI_LOGIN_STATUS_NO_RESOURCES); | ||
1035 | ret = -1; | ||
1036 | goto out; | ||
1037 | } | ||
1038 | |||
1039 | login->rsp_buf = kzalloc(MAX_KEY_VALUE_PAIRS, GFP_KERNEL); | ||
1040 | if (!login->rsp_buf) { | ||
1041 | pr_err("Unable to allocate memory for" | ||
1042 | " request buffer.\n"); | ||
1043 | iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, | ||
1044 | ISCSI_LOGIN_STATUS_NO_RESOURCES); | ||
1045 | ret = -1; | ||
1046 | goto out; | ||
1047 | } | ||
1048 | |||
1049 | ret = iscsi_target_do_login(conn, login); | ||
1050 | out: | ||
1051 | if (ret != 0) | ||
1052 | iscsi_remove_failed_auth_entry(conn); | ||
1053 | |||
1054 | iscsi_target_nego_release(login, conn); | ||
1055 | return ret; | ||
1056 | } | ||
1057 | |||
1058 | void iscsi_target_nego_release( | ||
1059 | struct iscsi_login *login, | ||
1060 | struct iscsi_conn *conn) | ||
1061 | { | ||
1062 | kfree(login->req); | ||
1063 | kfree(login->rsp); | ||
1064 | kfree(login->req_buf); | ||
1065 | kfree(login->rsp_buf); | ||
1066 | kfree(login); | ||
1067 | } | ||
diff --git a/drivers/target/iscsi/iscsi_target_nego.h b/drivers/target/iscsi/iscsi_target_nego.h new file mode 100644 index 000000000000..92e133a5158f --- /dev/null +++ b/drivers/target/iscsi/iscsi_target_nego.h | |||
@@ -0,0 +1,17 @@ | |||
1 | #ifndef ISCSI_TARGET_NEGO_H | ||
2 | #define ISCSI_TARGET_NEGO_H | ||
3 | |||
4 | #define DECIMAL 0 | ||
5 | #define HEX 1 | ||
6 | |||
7 | extern void convert_null_to_semi(char *, int); | ||
8 | extern int extract_param(const char *, const char *, unsigned int, char *, | ||
9 | unsigned char *); | ||
10 | extern struct iscsi_login *iscsi_target_init_negotiation( | ||
11 | struct iscsi_np *, struct iscsi_conn *, char *); | ||
12 | extern int iscsi_target_start_negotiation( | ||
13 | struct iscsi_login *, struct iscsi_conn *); | ||
14 | extern void iscsi_target_nego_release( | ||
15 | struct iscsi_login *, struct iscsi_conn *); | ||
16 | |||
17 | #endif /* ISCSI_TARGET_NEGO_H */ | ||
diff --git a/drivers/target/iscsi/iscsi_target_nodeattrib.c b/drivers/target/iscsi/iscsi_target_nodeattrib.c new file mode 100644 index 000000000000..aeafbe0cd7d1 --- /dev/null +++ b/drivers/target/iscsi/iscsi_target_nodeattrib.c | |||
@@ -0,0 +1,263 @@ | |||
1 | /******************************************************************************* | ||
2 | * This file contains the main functions related to Initiator Node Attributes. | ||
3 | * | ||
4 | * \u00a9 Copyright 2007-2011 RisingTide Systems LLC. | ||
5 | * | ||
6 | * Licensed to the Linux Foundation under the General Public License (GPL) version 2. | ||
7 | * | ||
8 | * Author: Nicholas A. Bellinger <nab@linux-iscsi.org> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | ******************************************************************************/ | ||
20 | |||
21 | #include <target/target_core_base.h> | ||
22 | #include <target/target_core_transport.h> | ||
23 | |||
24 | #include "iscsi_target_core.h" | ||
25 | #include "iscsi_target_device.h" | ||
26 | #include "iscsi_target_tpg.h" | ||
27 | #include "iscsi_target_util.h" | ||
28 | #include "iscsi_target_nodeattrib.h" | ||
29 | |||
30 | static inline char *iscsit_na_get_initiatorname( | ||
31 | struct iscsi_node_acl *nacl) | ||
32 | { | ||
33 | struct se_node_acl *se_nacl = &nacl->se_node_acl; | ||
34 | |||
35 | return &se_nacl->initiatorname[0]; | ||
36 | } | ||
37 | |||
38 | void iscsit_set_default_node_attribues( | ||
39 | struct iscsi_node_acl *acl) | ||
40 | { | ||
41 | struct iscsi_node_attrib *a = &acl->node_attrib; | ||
42 | |||
43 | a->dataout_timeout = NA_DATAOUT_TIMEOUT; | ||
44 | a->dataout_timeout_retries = NA_DATAOUT_TIMEOUT_RETRIES; | ||
45 | a->nopin_timeout = NA_NOPIN_TIMEOUT; | ||
46 | a->nopin_response_timeout = NA_NOPIN_RESPONSE_TIMEOUT; | ||
47 | a->random_datain_pdu_offsets = NA_RANDOM_DATAIN_PDU_OFFSETS; | ||
48 | a->random_datain_seq_offsets = NA_RANDOM_DATAIN_SEQ_OFFSETS; | ||
49 | a->random_r2t_offsets = NA_RANDOM_R2T_OFFSETS; | ||
50 | a->default_erl = NA_DEFAULT_ERL; | ||
51 | } | ||
52 | |||
53 | extern int iscsit_na_dataout_timeout( | ||
54 | struct iscsi_node_acl *acl, | ||
55 | u32 dataout_timeout) | ||
56 | { | ||
57 | struct iscsi_node_attrib *a = &acl->node_attrib; | ||
58 | |||
59 | if (dataout_timeout > NA_DATAOUT_TIMEOUT_MAX) { | ||
60 | pr_err("Requested DataOut Timeout %u larger than" | ||
61 | " maximum %u\n", dataout_timeout, | ||
62 | NA_DATAOUT_TIMEOUT_MAX); | ||
63 | return -EINVAL; | ||
64 | } else if (dataout_timeout < NA_DATAOUT_TIMEOUT_MIX) { | ||
65 | pr_err("Requested DataOut Timeout %u smaller than" | ||
66 | " minimum %u\n", dataout_timeout, | ||
67 | NA_DATAOUT_TIMEOUT_MIX); | ||
68 | return -EINVAL; | ||
69 | } | ||
70 | |||
71 | a->dataout_timeout = dataout_timeout; | ||
72 | pr_debug("Set DataOut Timeout to %u for Initiator Node" | ||
73 | " %s\n", a->dataout_timeout, iscsit_na_get_initiatorname(acl)); | ||
74 | |||
75 | return 0; | ||
76 | } | ||
77 | |||
78 | extern int iscsit_na_dataout_timeout_retries( | ||
79 | struct iscsi_node_acl *acl, | ||
80 | u32 dataout_timeout_retries) | ||
81 | { | ||
82 | struct iscsi_node_attrib *a = &acl->node_attrib; | ||
83 | |||
84 | if (dataout_timeout_retries > NA_DATAOUT_TIMEOUT_RETRIES_MAX) { | ||
85 | pr_err("Requested DataOut Timeout Retries %u larger" | ||
86 | " than maximum %u", dataout_timeout_retries, | ||
87 | NA_DATAOUT_TIMEOUT_RETRIES_MAX); | ||
88 | return -EINVAL; | ||
89 | } else if (dataout_timeout_retries < NA_DATAOUT_TIMEOUT_RETRIES_MIN) { | ||
90 | pr_err("Requested DataOut Timeout Retries %u smaller" | ||
91 | " than minimum %u", dataout_timeout_retries, | ||
92 | NA_DATAOUT_TIMEOUT_RETRIES_MIN); | ||
93 | return -EINVAL; | ||
94 | } | ||
95 | |||
96 | a->dataout_timeout_retries = dataout_timeout_retries; | ||
97 | pr_debug("Set DataOut Timeout Retries to %u for" | ||
98 | " Initiator Node %s\n", a->dataout_timeout_retries, | ||
99 | iscsit_na_get_initiatorname(acl)); | ||
100 | |||
101 | return 0; | ||
102 | } | ||
103 | |||
104 | extern int iscsit_na_nopin_timeout( | ||
105 | struct iscsi_node_acl *acl, | ||
106 | u32 nopin_timeout) | ||
107 | { | ||
108 | struct iscsi_node_attrib *a = &acl->node_attrib; | ||
109 | struct iscsi_session *sess; | ||
110 | struct iscsi_conn *conn; | ||
111 | struct se_node_acl *se_nacl = &a->nacl->se_node_acl; | ||
112 | struct se_session *se_sess; | ||
113 | u32 orig_nopin_timeout = a->nopin_timeout; | ||
114 | |||
115 | if (nopin_timeout > NA_NOPIN_TIMEOUT_MAX) { | ||
116 | pr_err("Requested NopIn Timeout %u larger than maximum" | ||
117 | " %u\n", nopin_timeout, NA_NOPIN_TIMEOUT_MAX); | ||
118 | return -EINVAL; | ||
119 | } else if ((nopin_timeout < NA_NOPIN_TIMEOUT_MIN) && | ||
120 | (nopin_timeout != 0)) { | ||
121 | pr_err("Requested NopIn Timeout %u smaller than" | ||
122 | " minimum %u and not 0\n", nopin_timeout, | ||
123 | NA_NOPIN_TIMEOUT_MIN); | ||
124 | return -EINVAL; | ||
125 | } | ||
126 | |||
127 | a->nopin_timeout = nopin_timeout; | ||
128 | pr_debug("Set NopIn Timeout to %u for Initiator" | ||
129 | " Node %s\n", a->nopin_timeout, | ||
130 | iscsit_na_get_initiatorname(acl)); | ||
131 | /* | ||
132 | * Reenable disabled nopin_timeout timer for all iSCSI connections. | ||
133 | */ | ||
134 | if (!orig_nopin_timeout) { | ||
135 | spin_lock_bh(&se_nacl->nacl_sess_lock); | ||
136 | se_sess = se_nacl->nacl_sess; | ||
137 | if (se_sess) { | ||
138 | sess = (struct iscsi_session *)se_sess->fabric_sess_ptr; | ||
139 | |||
140 | spin_lock(&sess->conn_lock); | ||
141 | list_for_each_entry(conn, &sess->sess_conn_list, | ||
142 | conn_list) { | ||
143 | if (conn->conn_state != | ||
144 | TARG_CONN_STATE_LOGGED_IN) | ||
145 | continue; | ||
146 | |||
147 | spin_lock(&conn->nopin_timer_lock); | ||
148 | __iscsit_start_nopin_timer(conn); | ||
149 | spin_unlock(&conn->nopin_timer_lock); | ||
150 | } | ||
151 | spin_unlock(&sess->conn_lock); | ||
152 | } | ||
153 | spin_unlock_bh(&se_nacl->nacl_sess_lock); | ||
154 | } | ||
155 | |||
156 | return 0; | ||
157 | } | ||
158 | |||
159 | extern int iscsit_na_nopin_response_timeout( | ||
160 | struct iscsi_node_acl *acl, | ||
161 | u32 nopin_response_timeout) | ||
162 | { | ||
163 | struct iscsi_node_attrib *a = &acl->node_attrib; | ||
164 | |||
165 | if (nopin_response_timeout > NA_NOPIN_RESPONSE_TIMEOUT_MAX) { | ||
166 | pr_err("Requested NopIn Response Timeout %u larger" | ||
167 | " than maximum %u\n", nopin_response_timeout, | ||
168 | NA_NOPIN_RESPONSE_TIMEOUT_MAX); | ||
169 | return -EINVAL; | ||
170 | } else if (nopin_response_timeout < NA_NOPIN_RESPONSE_TIMEOUT_MIN) { | ||
171 | pr_err("Requested NopIn Response Timeout %u smaller" | ||
172 | " than minimum %u\n", nopin_response_timeout, | ||
173 | NA_NOPIN_RESPONSE_TIMEOUT_MIN); | ||
174 | return -EINVAL; | ||
175 | } | ||
176 | |||
177 | a->nopin_response_timeout = nopin_response_timeout; | ||
178 | pr_debug("Set NopIn Response Timeout to %u for" | ||
179 | " Initiator Node %s\n", a->nopin_timeout, | ||
180 | iscsit_na_get_initiatorname(acl)); | ||
181 | |||
182 | return 0; | ||
183 | } | ||
184 | |||
185 | extern int iscsit_na_random_datain_pdu_offsets( | ||
186 | struct iscsi_node_acl *acl, | ||
187 | u32 random_datain_pdu_offsets) | ||
188 | { | ||
189 | struct iscsi_node_attrib *a = &acl->node_attrib; | ||
190 | |||
191 | if (random_datain_pdu_offsets != 0 && random_datain_pdu_offsets != 1) { | ||
192 | pr_err("Requested Random DataIN PDU Offsets: %u not" | ||
193 | " 0 or 1\n", random_datain_pdu_offsets); | ||
194 | return -EINVAL; | ||
195 | } | ||
196 | |||
197 | a->random_datain_pdu_offsets = random_datain_pdu_offsets; | ||
198 | pr_debug("Set Random DataIN PDU Offsets to %u for" | ||
199 | " Initiator Node %s\n", a->random_datain_pdu_offsets, | ||
200 | iscsit_na_get_initiatorname(acl)); | ||
201 | |||
202 | return 0; | ||
203 | } | ||
204 | |||
205 | extern int iscsit_na_random_datain_seq_offsets( | ||
206 | struct iscsi_node_acl *acl, | ||
207 | u32 random_datain_seq_offsets) | ||
208 | { | ||
209 | struct iscsi_node_attrib *a = &acl->node_attrib; | ||
210 | |||
211 | if (random_datain_seq_offsets != 0 && random_datain_seq_offsets != 1) { | ||
212 | pr_err("Requested Random DataIN Sequence Offsets: %u" | ||
213 | " not 0 or 1\n", random_datain_seq_offsets); | ||
214 | return -EINVAL; | ||
215 | } | ||
216 | |||
217 | a->random_datain_seq_offsets = random_datain_seq_offsets; | ||
218 | pr_debug("Set Random DataIN Sequence Offsets to %u for" | ||
219 | " Initiator Node %s\n", a->random_datain_seq_offsets, | ||
220 | iscsit_na_get_initiatorname(acl)); | ||
221 | |||
222 | return 0; | ||
223 | } | ||
224 | |||
225 | extern int iscsit_na_random_r2t_offsets( | ||
226 | struct iscsi_node_acl *acl, | ||
227 | u32 random_r2t_offsets) | ||
228 | { | ||
229 | struct iscsi_node_attrib *a = &acl->node_attrib; | ||
230 | |||
231 | if (random_r2t_offsets != 0 && random_r2t_offsets != 1) { | ||
232 | pr_err("Requested Random R2T Offsets: %u not" | ||
233 | " 0 or 1\n", random_r2t_offsets); | ||
234 | return -EINVAL; | ||
235 | } | ||
236 | |||
237 | a->random_r2t_offsets = random_r2t_offsets; | ||
238 | pr_debug("Set Random R2T Offsets to %u for" | ||
239 | " Initiator Node %s\n", a->random_r2t_offsets, | ||
240 | iscsit_na_get_initiatorname(acl)); | ||
241 | |||
242 | return 0; | ||
243 | } | ||
244 | |||
245 | extern int iscsit_na_default_erl( | ||
246 | struct iscsi_node_acl *acl, | ||
247 | u32 default_erl) | ||
248 | { | ||
249 | struct iscsi_node_attrib *a = &acl->node_attrib; | ||
250 | |||
251 | if (default_erl != 0 && default_erl != 1 && default_erl != 2) { | ||
252 | pr_err("Requested default ERL: %u not 0, 1, or 2\n", | ||
253 | default_erl); | ||
254 | return -EINVAL; | ||
255 | } | ||
256 | |||
257 | a->default_erl = default_erl; | ||
258 | pr_debug("Set use ERL0 flag to %u for Initiator" | ||
259 | " Node %s\n", a->default_erl, | ||
260 | iscsit_na_get_initiatorname(acl)); | ||
261 | |||
262 | return 0; | ||
263 | } | ||
diff --git a/drivers/target/iscsi/iscsi_target_nodeattrib.h b/drivers/target/iscsi/iscsi_target_nodeattrib.h new file mode 100644 index 000000000000..c970b326ef23 --- /dev/null +++ b/drivers/target/iscsi/iscsi_target_nodeattrib.h | |||
@@ -0,0 +1,14 @@ | |||
1 | #ifndef ISCSI_TARGET_NODEATTRIB_H | ||
2 | #define ISCSI_TARGET_NODEATTRIB_H | ||
3 | |||
4 | extern void iscsit_set_default_node_attribues(struct iscsi_node_acl *); | ||
5 | extern int iscsit_na_dataout_timeout(struct iscsi_node_acl *, u32); | ||
6 | extern int iscsit_na_dataout_timeout_retries(struct iscsi_node_acl *, u32); | ||
7 | extern int iscsit_na_nopin_timeout(struct iscsi_node_acl *, u32); | ||
8 | extern int iscsit_na_nopin_response_timeout(struct iscsi_node_acl *, u32); | ||
9 | extern int iscsit_na_random_datain_pdu_offsets(struct iscsi_node_acl *, u32); | ||
10 | extern int iscsit_na_random_datain_seq_offsets(struct iscsi_node_acl *, u32); | ||
11 | extern int iscsit_na_random_r2t_offsets(struct iscsi_node_acl *, u32); | ||
12 | extern int iscsit_na_default_erl(struct iscsi_node_acl *, u32); | ||
13 | |||
14 | #endif /* ISCSI_TARGET_NODEATTRIB_H */ | ||
diff --git a/drivers/target/iscsi/iscsi_target_parameters.c b/drivers/target/iscsi/iscsi_target_parameters.c new file mode 100644 index 000000000000..252e246cf51e --- /dev/null +++ b/drivers/target/iscsi/iscsi_target_parameters.c | |||
@@ -0,0 +1,1905 @@ | |||
1 | /******************************************************************************* | ||
2 | * This file contains main functions related to iSCSI Parameter negotiation. | ||
3 | * | ||
4 | * \u00a9 Copyright 2007-2011 RisingTide Systems LLC. | ||
5 | * | ||
6 | * Licensed to the Linux Foundation under the General Public License (GPL) version 2. | ||
7 | * | ||
8 | * Author: Nicholas A. Bellinger <nab@linux-iscsi.org> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | ******************************************************************************/ | ||
20 | |||
21 | #include <linux/slab.h> | ||
22 | |||
23 | #include "iscsi_target_core.h" | ||
24 | #include "iscsi_target_util.h" | ||
25 | #include "iscsi_target_parameters.h" | ||
26 | |||
27 | int iscsi_login_rx_data( | ||
28 | struct iscsi_conn *conn, | ||
29 | char *buf, | ||
30 | int length) | ||
31 | { | ||
32 | int rx_got; | ||
33 | struct kvec iov; | ||
34 | |||
35 | memset(&iov, 0, sizeof(struct kvec)); | ||
36 | iov.iov_len = length; | ||
37 | iov.iov_base = buf; | ||
38 | |||
39 | /* | ||
40 | * Initial Marker-less Interval. | ||
41 | * Add the values regardless of IFMarker/OFMarker, considering | ||
42 | * it may not be negoitated yet. | ||
43 | */ | ||
44 | conn->of_marker += length; | ||
45 | |||
46 | rx_got = rx_data(conn, &iov, 1, length); | ||
47 | if (rx_got != length) { | ||
48 | pr_err("rx_data returned %d, expecting %d.\n", | ||
49 | rx_got, length); | ||
50 | return -1; | ||
51 | } | ||
52 | |||
53 | return 0 ; | ||
54 | } | ||
55 | |||
56 | int iscsi_login_tx_data( | ||
57 | struct iscsi_conn *conn, | ||
58 | char *pdu_buf, | ||
59 | char *text_buf, | ||
60 | int text_length) | ||
61 | { | ||
62 | int length, tx_sent; | ||
63 | struct kvec iov[2]; | ||
64 | |||
65 | length = (ISCSI_HDR_LEN + text_length); | ||
66 | |||
67 | memset(&iov[0], 0, 2 * sizeof(struct kvec)); | ||
68 | iov[0].iov_len = ISCSI_HDR_LEN; | ||
69 | iov[0].iov_base = pdu_buf; | ||
70 | iov[1].iov_len = text_length; | ||
71 | iov[1].iov_base = text_buf; | ||
72 | |||
73 | /* | ||
74 | * Initial Marker-less Interval. | ||
75 | * Add the values regardless of IFMarker/OFMarker, considering | ||
76 | * it may not be negoitated yet. | ||
77 | */ | ||
78 | conn->if_marker += length; | ||
79 | |||
80 | tx_sent = tx_data(conn, &iov[0], 2, length); | ||
81 | if (tx_sent != length) { | ||
82 | pr_err("tx_data returned %d, expecting %d.\n", | ||
83 | tx_sent, length); | ||
84 | return -1; | ||
85 | } | ||
86 | |||
87 | return 0; | ||
88 | } | ||
89 | |||
90 | void iscsi_dump_conn_ops(struct iscsi_conn_ops *conn_ops) | ||
91 | { | ||
92 | pr_debug("HeaderDigest: %s\n", (conn_ops->HeaderDigest) ? | ||
93 | "CRC32C" : "None"); | ||
94 | pr_debug("DataDigest: %s\n", (conn_ops->DataDigest) ? | ||
95 | "CRC32C" : "None"); | ||
96 | pr_debug("MaxRecvDataSegmentLength: %u\n", | ||
97 | conn_ops->MaxRecvDataSegmentLength); | ||
98 | pr_debug("OFMarker: %s\n", (conn_ops->OFMarker) ? "Yes" : "No"); | ||
99 | pr_debug("IFMarker: %s\n", (conn_ops->IFMarker) ? "Yes" : "No"); | ||
100 | if (conn_ops->OFMarker) | ||
101 | pr_debug("OFMarkInt: %u\n", conn_ops->OFMarkInt); | ||
102 | if (conn_ops->IFMarker) | ||
103 | pr_debug("IFMarkInt: %u\n", conn_ops->IFMarkInt); | ||
104 | } | ||
105 | |||
106 | void iscsi_dump_sess_ops(struct iscsi_sess_ops *sess_ops) | ||
107 | { | ||
108 | pr_debug("InitiatorName: %s\n", sess_ops->InitiatorName); | ||
109 | pr_debug("InitiatorAlias: %s\n", sess_ops->InitiatorAlias); | ||
110 | pr_debug("TargetName: %s\n", sess_ops->TargetName); | ||
111 | pr_debug("TargetAlias: %s\n", sess_ops->TargetAlias); | ||
112 | pr_debug("TargetPortalGroupTag: %hu\n", | ||
113 | sess_ops->TargetPortalGroupTag); | ||
114 | pr_debug("MaxConnections: %hu\n", sess_ops->MaxConnections); | ||
115 | pr_debug("InitialR2T: %s\n", | ||
116 | (sess_ops->InitialR2T) ? "Yes" : "No"); | ||
117 | pr_debug("ImmediateData: %s\n", (sess_ops->ImmediateData) ? | ||
118 | "Yes" : "No"); | ||
119 | pr_debug("MaxBurstLength: %u\n", sess_ops->MaxBurstLength); | ||
120 | pr_debug("FirstBurstLength: %u\n", sess_ops->FirstBurstLength); | ||
121 | pr_debug("DefaultTime2Wait: %hu\n", sess_ops->DefaultTime2Wait); | ||
122 | pr_debug("DefaultTime2Retain: %hu\n", | ||
123 | sess_ops->DefaultTime2Retain); | ||
124 | pr_debug("MaxOutstandingR2T: %hu\n", | ||
125 | sess_ops->MaxOutstandingR2T); | ||
126 | pr_debug("DataPDUInOrder: %s\n", | ||
127 | (sess_ops->DataPDUInOrder) ? "Yes" : "No"); | ||
128 | pr_debug("DataSequenceInOrder: %s\n", | ||
129 | (sess_ops->DataSequenceInOrder) ? "Yes" : "No"); | ||
130 | pr_debug("ErrorRecoveryLevel: %hu\n", | ||
131 | sess_ops->ErrorRecoveryLevel); | ||
132 | pr_debug("SessionType: %s\n", (sess_ops->SessionType) ? | ||
133 | "Discovery" : "Normal"); | ||
134 | } | ||
135 | |||
136 | void iscsi_print_params(struct iscsi_param_list *param_list) | ||
137 | { | ||
138 | struct iscsi_param *param; | ||
139 | |||
140 | list_for_each_entry(param, ¶m_list->param_list, p_list) | ||
141 | pr_debug("%s: %s\n", param->name, param->value); | ||
142 | } | ||
143 | |||
144 | static struct iscsi_param *iscsi_set_default_param(struct iscsi_param_list *param_list, | ||
145 | char *name, char *value, u8 phase, u8 scope, u8 sender, | ||
146 | u16 type_range, u8 use) | ||
147 | { | ||
148 | struct iscsi_param *param = NULL; | ||
149 | |||
150 | param = kzalloc(sizeof(struct iscsi_param), GFP_KERNEL); | ||
151 | if (!param) { | ||
152 | pr_err("Unable to allocate memory for parameter.\n"); | ||
153 | goto out; | ||
154 | } | ||
155 | INIT_LIST_HEAD(¶m->p_list); | ||
156 | |||
157 | param->name = kzalloc(strlen(name) + 1, GFP_KERNEL); | ||
158 | if (!param->name) { | ||
159 | pr_err("Unable to allocate memory for parameter name.\n"); | ||
160 | goto out; | ||
161 | } | ||
162 | |||
163 | param->value = kzalloc(strlen(value) + 1, GFP_KERNEL); | ||
164 | if (!param->value) { | ||
165 | pr_err("Unable to allocate memory for parameter value.\n"); | ||
166 | goto out; | ||
167 | } | ||
168 | |||
169 | memcpy(param->name, name, strlen(name)); | ||
170 | param->name[strlen(name)] = '\0'; | ||
171 | memcpy(param->value, value, strlen(value)); | ||
172 | param->value[strlen(value)] = '\0'; | ||
173 | param->phase = phase; | ||
174 | param->scope = scope; | ||
175 | param->sender = sender; | ||
176 | param->use = use; | ||
177 | param->type_range = type_range; | ||
178 | |||
179 | switch (param->type_range) { | ||
180 | case TYPERANGE_BOOL_AND: | ||
181 | param->type = TYPE_BOOL_AND; | ||
182 | break; | ||
183 | case TYPERANGE_BOOL_OR: | ||
184 | param->type = TYPE_BOOL_OR; | ||
185 | break; | ||
186 | case TYPERANGE_0_TO_2: | ||
187 | case TYPERANGE_0_TO_3600: | ||
188 | case TYPERANGE_0_TO_32767: | ||
189 | case TYPERANGE_0_TO_65535: | ||
190 | case TYPERANGE_1_TO_65535: | ||
191 | case TYPERANGE_2_TO_3600: | ||
192 | case TYPERANGE_512_TO_16777215: | ||
193 | param->type = TYPE_NUMBER; | ||
194 | break; | ||
195 | case TYPERANGE_AUTH: | ||
196 | case TYPERANGE_DIGEST: | ||
197 | param->type = TYPE_VALUE_LIST | TYPE_STRING; | ||
198 | break; | ||
199 | case TYPERANGE_MARKINT: | ||
200 | param->type = TYPE_NUMBER_RANGE; | ||
201 | param->type_range |= TYPERANGE_1_TO_65535; | ||
202 | break; | ||
203 | case TYPERANGE_ISCSINAME: | ||
204 | case TYPERANGE_SESSIONTYPE: | ||
205 | case TYPERANGE_TARGETADDRESS: | ||
206 | case TYPERANGE_UTF8: | ||
207 | param->type = TYPE_STRING; | ||
208 | break; | ||
209 | default: | ||
210 | pr_err("Unknown type_range 0x%02x\n", | ||
211 | param->type_range); | ||
212 | goto out; | ||
213 | } | ||
214 | list_add_tail(¶m->p_list, ¶m_list->param_list); | ||
215 | |||
216 | return param; | ||
217 | out: | ||
218 | if (param) { | ||
219 | kfree(param->value); | ||
220 | kfree(param->name); | ||
221 | kfree(param); | ||
222 | } | ||
223 | |||
224 | return NULL; | ||
225 | } | ||
226 | |||
227 | /* #warning Add extension keys */ | ||
228 | int iscsi_create_default_params(struct iscsi_param_list **param_list_ptr) | ||
229 | { | ||
230 | struct iscsi_param *param = NULL; | ||
231 | struct iscsi_param_list *pl; | ||
232 | |||
233 | pl = kzalloc(sizeof(struct iscsi_param_list), GFP_KERNEL); | ||
234 | if (!pl) { | ||
235 | pr_err("Unable to allocate memory for" | ||
236 | " struct iscsi_param_list.\n"); | ||
237 | return -1 ; | ||
238 | } | ||
239 | INIT_LIST_HEAD(&pl->param_list); | ||
240 | INIT_LIST_HEAD(&pl->extra_response_list); | ||
241 | |||
242 | /* | ||
243 | * The format for setting the initial parameter definitions are: | ||
244 | * | ||
245 | * Parameter name: | ||
246 | * Initial value: | ||
247 | * Allowable phase: | ||
248 | * Scope: | ||
249 | * Allowable senders: | ||
250 | * Typerange: | ||
251 | * Use: | ||
252 | */ | ||
253 | param = iscsi_set_default_param(pl, AUTHMETHOD, INITIAL_AUTHMETHOD, | ||
254 | PHASE_SECURITY, SCOPE_CONNECTION_ONLY, SENDER_BOTH, | ||
255 | TYPERANGE_AUTH, USE_INITIAL_ONLY); | ||
256 | if (!param) | ||
257 | goto out; | ||
258 | |||
259 | param = iscsi_set_default_param(pl, HEADERDIGEST, INITIAL_HEADERDIGEST, | ||
260 | PHASE_OPERATIONAL, SCOPE_CONNECTION_ONLY, SENDER_BOTH, | ||
261 | TYPERANGE_DIGEST, USE_INITIAL_ONLY); | ||
262 | if (!param) | ||
263 | goto out; | ||
264 | |||
265 | param = iscsi_set_default_param(pl, DATADIGEST, INITIAL_DATADIGEST, | ||
266 | PHASE_OPERATIONAL, SCOPE_CONNECTION_ONLY, SENDER_BOTH, | ||
267 | TYPERANGE_DIGEST, USE_INITIAL_ONLY); | ||
268 | if (!param) | ||
269 | goto out; | ||
270 | |||
271 | param = iscsi_set_default_param(pl, MAXCONNECTIONS, | ||
272 | INITIAL_MAXCONNECTIONS, PHASE_OPERATIONAL, | ||
273 | SCOPE_SESSION_WIDE, SENDER_BOTH, | ||
274 | TYPERANGE_1_TO_65535, USE_LEADING_ONLY); | ||
275 | if (!param) | ||
276 | goto out; | ||
277 | |||
278 | param = iscsi_set_default_param(pl, SENDTARGETS, INITIAL_SENDTARGETS, | ||
279 | PHASE_FFP0, SCOPE_SESSION_WIDE, SENDER_INITIATOR, | ||
280 | TYPERANGE_UTF8, 0); | ||
281 | if (!param) | ||
282 | goto out; | ||
283 | |||
284 | param = iscsi_set_default_param(pl, TARGETNAME, INITIAL_TARGETNAME, | ||
285 | PHASE_DECLARATIVE, SCOPE_SESSION_WIDE, SENDER_BOTH, | ||
286 | TYPERANGE_ISCSINAME, USE_ALL); | ||
287 | if (!param) | ||
288 | goto out; | ||
289 | |||
290 | param = iscsi_set_default_param(pl, INITIATORNAME, | ||
291 | INITIAL_INITIATORNAME, PHASE_DECLARATIVE, | ||
292 | SCOPE_SESSION_WIDE, SENDER_INITIATOR, | ||
293 | TYPERANGE_ISCSINAME, USE_INITIAL_ONLY); | ||
294 | if (!param) | ||
295 | goto out; | ||
296 | |||
297 | param = iscsi_set_default_param(pl, TARGETALIAS, INITIAL_TARGETALIAS, | ||
298 | PHASE_DECLARATIVE, SCOPE_SESSION_WIDE, SENDER_TARGET, | ||
299 | TYPERANGE_UTF8, USE_ALL); | ||
300 | if (!param) | ||
301 | goto out; | ||
302 | |||
303 | param = iscsi_set_default_param(pl, INITIATORALIAS, | ||
304 | INITIAL_INITIATORALIAS, PHASE_DECLARATIVE, | ||
305 | SCOPE_SESSION_WIDE, SENDER_INITIATOR, TYPERANGE_UTF8, | ||
306 | USE_ALL); | ||
307 | if (!param) | ||
308 | goto out; | ||
309 | |||
310 | param = iscsi_set_default_param(pl, TARGETADDRESS, | ||
311 | INITIAL_TARGETADDRESS, PHASE_DECLARATIVE, | ||
312 | SCOPE_SESSION_WIDE, SENDER_TARGET, | ||
313 | TYPERANGE_TARGETADDRESS, USE_ALL); | ||
314 | if (!param) | ||
315 | goto out; | ||
316 | |||
317 | param = iscsi_set_default_param(pl, TARGETPORTALGROUPTAG, | ||
318 | INITIAL_TARGETPORTALGROUPTAG, | ||
319 | PHASE_DECLARATIVE, SCOPE_SESSION_WIDE, SENDER_TARGET, | ||
320 | TYPERANGE_0_TO_65535, USE_INITIAL_ONLY); | ||
321 | if (!param) | ||
322 | goto out; | ||
323 | |||
324 | param = iscsi_set_default_param(pl, INITIALR2T, INITIAL_INITIALR2T, | ||
325 | PHASE_OPERATIONAL, SCOPE_SESSION_WIDE, SENDER_BOTH, | ||
326 | TYPERANGE_BOOL_OR, USE_LEADING_ONLY); | ||
327 | if (!param) | ||
328 | goto out; | ||
329 | |||
330 | param = iscsi_set_default_param(pl, IMMEDIATEDATA, | ||
331 | INITIAL_IMMEDIATEDATA, PHASE_OPERATIONAL, | ||
332 | SCOPE_SESSION_WIDE, SENDER_BOTH, TYPERANGE_BOOL_AND, | ||
333 | USE_LEADING_ONLY); | ||
334 | if (!param) | ||
335 | goto out; | ||
336 | |||
337 | param = iscsi_set_default_param(pl, MAXRECVDATASEGMENTLENGTH, | ||
338 | INITIAL_MAXRECVDATASEGMENTLENGTH, | ||
339 | PHASE_OPERATIONAL, SCOPE_CONNECTION_ONLY, SENDER_BOTH, | ||
340 | TYPERANGE_512_TO_16777215, USE_ALL); | ||
341 | if (!param) | ||
342 | goto out; | ||
343 | |||
344 | param = iscsi_set_default_param(pl, MAXBURSTLENGTH, | ||
345 | INITIAL_MAXBURSTLENGTH, PHASE_OPERATIONAL, | ||
346 | SCOPE_SESSION_WIDE, SENDER_BOTH, | ||
347 | TYPERANGE_512_TO_16777215, USE_LEADING_ONLY); | ||
348 | if (!param) | ||
349 | goto out; | ||
350 | |||
351 | param = iscsi_set_default_param(pl, FIRSTBURSTLENGTH, | ||
352 | INITIAL_FIRSTBURSTLENGTH, | ||
353 | PHASE_OPERATIONAL, SCOPE_SESSION_WIDE, SENDER_BOTH, | ||
354 | TYPERANGE_512_TO_16777215, USE_LEADING_ONLY); | ||
355 | if (!param) | ||
356 | goto out; | ||
357 | |||
358 | param = iscsi_set_default_param(pl, DEFAULTTIME2WAIT, | ||
359 | INITIAL_DEFAULTTIME2WAIT, | ||
360 | PHASE_OPERATIONAL, SCOPE_SESSION_WIDE, SENDER_BOTH, | ||
361 | TYPERANGE_0_TO_3600, USE_LEADING_ONLY); | ||
362 | if (!param) | ||
363 | goto out; | ||
364 | |||
365 | param = iscsi_set_default_param(pl, DEFAULTTIME2RETAIN, | ||
366 | INITIAL_DEFAULTTIME2RETAIN, | ||
367 | PHASE_OPERATIONAL, SCOPE_SESSION_WIDE, SENDER_BOTH, | ||
368 | TYPERANGE_0_TO_3600, USE_LEADING_ONLY); | ||
369 | if (!param) | ||
370 | goto out; | ||
371 | |||
372 | param = iscsi_set_default_param(pl, MAXOUTSTANDINGR2T, | ||
373 | INITIAL_MAXOUTSTANDINGR2T, | ||
374 | PHASE_OPERATIONAL, SCOPE_SESSION_WIDE, SENDER_BOTH, | ||
375 | TYPERANGE_1_TO_65535, USE_LEADING_ONLY); | ||
376 | if (!param) | ||
377 | goto out; | ||
378 | |||
379 | param = iscsi_set_default_param(pl, DATAPDUINORDER, | ||
380 | INITIAL_DATAPDUINORDER, PHASE_OPERATIONAL, | ||
381 | SCOPE_SESSION_WIDE, SENDER_BOTH, TYPERANGE_BOOL_OR, | ||
382 | USE_LEADING_ONLY); | ||
383 | if (!param) | ||
384 | goto out; | ||
385 | |||
386 | param = iscsi_set_default_param(pl, DATASEQUENCEINORDER, | ||
387 | INITIAL_DATASEQUENCEINORDER, | ||
388 | PHASE_OPERATIONAL, SCOPE_SESSION_WIDE, SENDER_BOTH, | ||
389 | TYPERANGE_BOOL_OR, USE_LEADING_ONLY); | ||
390 | if (!param) | ||
391 | goto out; | ||
392 | |||
393 | param = iscsi_set_default_param(pl, ERRORRECOVERYLEVEL, | ||
394 | INITIAL_ERRORRECOVERYLEVEL, | ||
395 | PHASE_OPERATIONAL, SCOPE_SESSION_WIDE, SENDER_BOTH, | ||
396 | TYPERANGE_0_TO_2, USE_LEADING_ONLY); | ||
397 | if (!param) | ||
398 | goto out; | ||
399 | |||
400 | param = iscsi_set_default_param(pl, SESSIONTYPE, INITIAL_SESSIONTYPE, | ||
401 | PHASE_DECLARATIVE, SCOPE_SESSION_WIDE, SENDER_INITIATOR, | ||
402 | TYPERANGE_SESSIONTYPE, USE_LEADING_ONLY); | ||
403 | if (!param) | ||
404 | goto out; | ||
405 | |||
406 | param = iscsi_set_default_param(pl, IFMARKER, INITIAL_IFMARKER, | ||
407 | PHASE_OPERATIONAL, SCOPE_CONNECTION_ONLY, SENDER_BOTH, | ||
408 | TYPERANGE_BOOL_AND, USE_INITIAL_ONLY); | ||
409 | if (!param) | ||
410 | goto out; | ||
411 | |||
412 | param = iscsi_set_default_param(pl, OFMARKER, INITIAL_OFMARKER, | ||
413 | PHASE_OPERATIONAL, SCOPE_CONNECTION_ONLY, SENDER_BOTH, | ||
414 | TYPERANGE_BOOL_AND, USE_INITIAL_ONLY); | ||
415 | if (!param) | ||
416 | goto out; | ||
417 | |||
418 | param = iscsi_set_default_param(pl, IFMARKINT, INITIAL_IFMARKINT, | ||
419 | PHASE_OPERATIONAL, SCOPE_CONNECTION_ONLY, SENDER_BOTH, | ||
420 | TYPERANGE_MARKINT, USE_INITIAL_ONLY); | ||
421 | if (!param) | ||
422 | goto out; | ||
423 | |||
424 | param = iscsi_set_default_param(pl, OFMARKINT, INITIAL_OFMARKINT, | ||
425 | PHASE_OPERATIONAL, SCOPE_CONNECTION_ONLY, SENDER_BOTH, | ||
426 | TYPERANGE_MARKINT, USE_INITIAL_ONLY); | ||
427 | if (!param) | ||
428 | goto out; | ||
429 | |||
430 | *param_list_ptr = pl; | ||
431 | return 0; | ||
432 | out: | ||
433 | iscsi_release_param_list(pl); | ||
434 | return -1; | ||
435 | } | ||
436 | |||
437 | int iscsi_set_keys_to_negotiate( | ||
438 | int sessiontype, | ||
439 | struct iscsi_param_list *param_list) | ||
440 | { | ||
441 | struct iscsi_param *param; | ||
442 | |||
443 | list_for_each_entry(param, ¶m_list->param_list, p_list) { | ||
444 | param->state = 0; | ||
445 | if (!strcmp(param->name, AUTHMETHOD)) { | ||
446 | SET_PSTATE_NEGOTIATE(param); | ||
447 | } else if (!strcmp(param->name, HEADERDIGEST)) { | ||
448 | SET_PSTATE_NEGOTIATE(param); | ||
449 | } else if (!strcmp(param->name, DATADIGEST)) { | ||
450 | SET_PSTATE_NEGOTIATE(param); | ||
451 | } else if (!strcmp(param->name, MAXCONNECTIONS)) { | ||
452 | SET_PSTATE_NEGOTIATE(param); | ||
453 | } else if (!strcmp(param->name, TARGETNAME)) { | ||
454 | continue; | ||
455 | } else if (!strcmp(param->name, INITIATORNAME)) { | ||
456 | continue; | ||
457 | } else if (!strcmp(param->name, TARGETALIAS)) { | ||
458 | if (param->value) | ||
459 | SET_PSTATE_NEGOTIATE(param); | ||
460 | } else if (!strcmp(param->name, INITIATORALIAS)) { | ||
461 | continue; | ||
462 | } else if (!strcmp(param->name, TARGETPORTALGROUPTAG)) { | ||
463 | SET_PSTATE_NEGOTIATE(param); | ||
464 | } else if (!strcmp(param->name, INITIALR2T)) { | ||
465 | SET_PSTATE_NEGOTIATE(param); | ||
466 | } else if (!strcmp(param->name, IMMEDIATEDATA)) { | ||
467 | SET_PSTATE_NEGOTIATE(param); | ||
468 | } else if (!strcmp(param->name, MAXRECVDATASEGMENTLENGTH)) { | ||
469 | SET_PSTATE_NEGOTIATE(param); | ||
470 | } else if (!strcmp(param->name, MAXBURSTLENGTH)) { | ||
471 | SET_PSTATE_NEGOTIATE(param); | ||
472 | } else if (!strcmp(param->name, FIRSTBURSTLENGTH)) { | ||
473 | SET_PSTATE_NEGOTIATE(param); | ||
474 | } else if (!strcmp(param->name, DEFAULTTIME2WAIT)) { | ||
475 | SET_PSTATE_NEGOTIATE(param); | ||
476 | } else if (!strcmp(param->name, DEFAULTTIME2RETAIN)) { | ||
477 | SET_PSTATE_NEGOTIATE(param); | ||
478 | } else if (!strcmp(param->name, MAXOUTSTANDINGR2T)) { | ||
479 | SET_PSTATE_NEGOTIATE(param); | ||
480 | } else if (!strcmp(param->name, DATAPDUINORDER)) { | ||
481 | SET_PSTATE_NEGOTIATE(param); | ||
482 | } else if (!strcmp(param->name, DATASEQUENCEINORDER)) { | ||
483 | SET_PSTATE_NEGOTIATE(param); | ||
484 | } else if (!strcmp(param->name, ERRORRECOVERYLEVEL)) { | ||
485 | SET_PSTATE_NEGOTIATE(param); | ||
486 | } else if (!strcmp(param->name, SESSIONTYPE)) { | ||
487 | SET_PSTATE_NEGOTIATE(param); | ||
488 | } else if (!strcmp(param->name, IFMARKER)) { | ||
489 | SET_PSTATE_NEGOTIATE(param); | ||
490 | } else if (!strcmp(param->name, OFMARKER)) { | ||
491 | SET_PSTATE_NEGOTIATE(param); | ||
492 | } else if (!strcmp(param->name, IFMARKINT)) { | ||
493 | SET_PSTATE_NEGOTIATE(param); | ||
494 | } else if (!strcmp(param->name, OFMARKINT)) { | ||
495 | SET_PSTATE_NEGOTIATE(param); | ||
496 | } | ||
497 | } | ||
498 | |||
499 | return 0; | ||
500 | } | ||
501 | |||
502 | int iscsi_set_keys_irrelevant_for_discovery( | ||
503 | struct iscsi_param_list *param_list) | ||
504 | { | ||
505 | struct iscsi_param *param; | ||
506 | |||
507 | list_for_each_entry(param, ¶m_list->param_list, p_list) { | ||
508 | if (!strcmp(param->name, MAXCONNECTIONS)) | ||
509 | param->state &= ~PSTATE_NEGOTIATE; | ||
510 | else if (!strcmp(param->name, INITIALR2T)) | ||
511 | param->state &= ~PSTATE_NEGOTIATE; | ||
512 | else if (!strcmp(param->name, IMMEDIATEDATA)) | ||
513 | param->state &= ~PSTATE_NEGOTIATE; | ||
514 | else if (!strcmp(param->name, MAXBURSTLENGTH)) | ||
515 | param->state &= ~PSTATE_NEGOTIATE; | ||
516 | else if (!strcmp(param->name, FIRSTBURSTLENGTH)) | ||
517 | param->state &= ~PSTATE_NEGOTIATE; | ||
518 | else if (!strcmp(param->name, MAXOUTSTANDINGR2T)) | ||
519 | param->state &= ~PSTATE_NEGOTIATE; | ||
520 | else if (!strcmp(param->name, DATAPDUINORDER)) | ||
521 | param->state &= ~PSTATE_NEGOTIATE; | ||
522 | else if (!strcmp(param->name, DATASEQUENCEINORDER)) | ||
523 | param->state &= ~PSTATE_NEGOTIATE; | ||
524 | else if (!strcmp(param->name, ERRORRECOVERYLEVEL)) | ||
525 | param->state &= ~PSTATE_NEGOTIATE; | ||
526 | else if (!strcmp(param->name, DEFAULTTIME2WAIT)) | ||
527 | param->state &= ~PSTATE_NEGOTIATE; | ||
528 | else if (!strcmp(param->name, DEFAULTTIME2RETAIN)) | ||
529 | param->state &= ~PSTATE_NEGOTIATE; | ||
530 | else if (!strcmp(param->name, IFMARKER)) | ||
531 | param->state &= ~PSTATE_NEGOTIATE; | ||
532 | else if (!strcmp(param->name, OFMARKER)) | ||
533 | param->state &= ~PSTATE_NEGOTIATE; | ||
534 | else if (!strcmp(param->name, IFMARKINT)) | ||
535 | param->state &= ~PSTATE_NEGOTIATE; | ||
536 | else if (!strcmp(param->name, OFMARKINT)) | ||
537 | param->state &= ~PSTATE_NEGOTIATE; | ||
538 | } | ||
539 | |||
540 | return 0; | ||
541 | } | ||
542 | |||
543 | int iscsi_copy_param_list( | ||
544 | struct iscsi_param_list **dst_param_list, | ||
545 | struct iscsi_param_list *src_param_list, | ||
546 | int leading) | ||
547 | { | ||
548 | struct iscsi_param *new_param = NULL, *param = NULL; | ||
549 | struct iscsi_param_list *param_list = NULL; | ||
550 | |||
551 | param_list = kzalloc(sizeof(struct iscsi_param_list), GFP_KERNEL); | ||
552 | if (!param_list) { | ||
553 | pr_err("Unable to allocate memory for" | ||
554 | " struct iscsi_param_list.\n"); | ||
555 | goto err_out; | ||
556 | } | ||
557 | INIT_LIST_HEAD(¶m_list->param_list); | ||
558 | INIT_LIST_HEAD(¶m_list->extra_response_list); | ||
559 | |||
560 | list_for_each_entry(param, &src_param_list->param_list, p_list) { | ||
561 | if (!leading && (param->scope & SCOPE_SESSION_WIDE)) { | ||
562 | if ((strcmp(param->name, "TargetName") != 0) && | ||
563 | (strcmp(param->name, "InitiatorName") != 0) && | ||
564 | (strcmp(param->name, "TargetPortalGroupTag") != 0)) | ||
565 | continue; | ||
566 | } | ||
567 | |||
568 | new_param = kzalloc(sizeof(struct iscsi_param), GFP_KERNEL); | ||
569 | if (!new_param) { | ||
570 | pr_err("Unable to allocate memory for" | ||
571 | " struct iscsi_param.\n"); | ||
572 | goto err_out; | ||
573 | } | ||
574 | |||
575 | new_param->set_param = param->set_param; | ||
576 | new_param->phase = param->phase; | ||
577 | new_param->scope = param->scope; | ||
578 | new_param->sender = param->sender; | ||
579 | new_param->type = param->type; | ||
580 | new_param->use = param->use; | ||
581 | new_param->type_range = param->type_range; | ||
582 | |||
583 | new_param->name = kzalloc(strlen(param->name) + 1, GFP_KERNEL); | ||
584 | if (!new_param->name) { | ||
585 | pr_err("Unable to allocate memory for" | ||
586 | " parameter name.\n"); | ||
587 | goto err_out; | ||
588 | } | ||
589 | |||
590 | new_param->value = kzalloc(strlen(param->value) + 1, | ||
591 | GFP_KERNEL); | ||
592 | if (!new_param->value) { | ||
593 | pr_err("Unable to allocate memory for" | ||
594 | " parameter value.\n"); | ||
595 | goto err_out; | ||
596 | } | ||
597 | |||
598 | memcpy(new_param->name, param->name, strlen(param->name)); | ||
599 | new_param->name[strlen(param->name)] = '\0'; | ||
600 | memcpy(new_param->value, param->value, strlen(param->value)); | ||
601 | new_param->value[strlen(param->value)] = '\0'; | ||
602 | |||
603 | list_add_tail(&new_param->p_list, ¶m_list->param_list); | ||
604 | } | ||
605 | |||
606 | if (!list_empty(¶m_list->param_list)) | ||
607 | *dst_param_list = param_list; | ||
608 | else { | ||
609 | pr_err("No parameters allocated.\n"); | ||
610 | goto err_out; | ||
611 | } | ||
612 | |||
613 | return 0; | ||
614 | |||
615 | err_out: | ||
616 | iscsi_release_param_list(param_list); | ||
617 | return -1; | ||
618 | } | ||
619 | |||
620 | static void iscsi_release_extra_responses(struct iscsi_param_list *param_list) | ||
621 | { | ||
622 | struct iscsi_extra_response *er, *er_tmp; | ||
623 | |||
624 | list_for_each_entry_safe(er, er_tmp, ¶m_list->extra_response_list, | ||
625 | er_list) { | ||
626 | list_del(&er->er_list); | ||
627 | kfree(er); | ||
628 | } | ||
629 | } | ||
630 | |||
631 | void iscsi_release_param_list(struct iscsi_param_list *param_list) | ||
632 | { | ||
633 | struct iscsi_param *param, *param_tmp; | ||
634 | |||
635 | list_for_each_entry_safe(param, param_tmp, ¶m_list->param_list, | ||
636 | p_list) { | ||
637 | list_del(¶m->p_list); | ||
638 | |||
639 | kfree(param->name); | ||
640 | param->name = NULL; | ||
641 | kfree(param->value); | ||
642 | param->value = NULL; | ||
643 | kfree(param); | ||
644 | param = NULL; | ||
645 | } | ||
646 | |||
647 | iscsi_release_extra_responses(param_list); | ||
648 | |||
649 | kfree(param_list); | ||
650 | } | ||
651 | |||
652 | struct iscsi_param *iscsi_find_param_from_key( | ||
653 | char *key, | ||
654 | struct iscsi_param_list *param_list) | ||
655 | { | ||
656 | struct iscsi_param *param; | ||
657 | |||
658 | if (!key || !param_list) { | ||
659 | pr_err("Key or parameter list pointer is NULL.\n"); | ||
660 | return NULL; | ||
661 | } | ||
662 | |||
663 | list_for_each_entry(param, ¶m_list->param_list, p_list) { | ||
664 | if (!strcmp(key, param->name)) | ||
665 | return param; | ||
666 | } | ||
667 | |||
668 | pr_err("Unable to locate key \"%s\".\n", key); | ||
669 | return NULL; | ||
670 | } | ||
671 | |||
672 | int iscsi_extract_key_value(char *textbuf, char **key, char **value) | ||
673 | { | ||
674 | *value = strchr(textbuf, '='); | ||
675 | if (!*value) { | ||
676 | pr_err("Unable to locate \"=\" seperator for key," | ||
677 | " ignoring request.\n"); | ||
678 | return -1; | ||
679 | } | ||
680 | |||
681 | *key = textbuf; | ||
682 | **value = '\0'; | ||
683 | *value = *value + 1; | ||
684 | |||
685 | return 0; | ||
686 | } | ||
687 | |||
688 | int iscsi_update_param_value(struct iscsi_param *param, char *value) | ||
689 | { | ||
690 | kfree(param->value); | ||
691 | |||
692 | param->value = kzalloc(strlen(value) + 1, GFP_KERNEL); | ||
693 | if (!param->value) { | ||
694 | pr_err("Unable to allocate memory for value.\n"); | ||
695 | return -1; | ||
696 | } | ||
697 | |||
698 | memcpy(param->value, value, strlen(value)); | ||
699 | param->value[strlen(value)] = '\0'; | ||
700 | |||
701 | pr_debug("iSCSI Parameter updated to %s=%s\n", | ||
702 | param->name, param->value); | ||
703 | return 0; | ||
704 | } | ||
705 | |||
706 | static int iscsi_add_notunderstood_response( | ||
707 | char *key, | ||
708 | char *value, | ||
709 | struct iscsi_param_list *param_list) | ||
710 | { | ||
711 | struct iscsi_extra_response *extra_response; | ||
712 | |||
713 | if (strlen(value) > VALUE_MAXLEN) { | ||
714 | pr_err("Value for notunderstood key \"%s\" exceeds %d," | ||
715 | " protocol error.\n", key, VALUE_MAXLEN); | ||
716 | return -1; | ||
717 | } | ||
718 | |||
719 | extra_response = kzalloc(sizeof(struct iscsi_extra_response), GFP_KERNEL); | ||
720 | if (!extra_response) { | ||
721 | pr_err("Unable to allocate memory for" | ||
722 | " struct iscsi_extra_response.\n"); | ||
723 | return -1; | ||
724 | } | ||
725 | INIT_LIST_HEAD(&extra_response->er_list); | ||
726 | |||
727 | strncpy(extra_response->key, key, strlen(key) + 1); | ||
728 | strncpy(extra_response->value, NOTUNDERSTOOD, | ||
729 | strlen(NOTUNDERSTOOD) + 1); | ||
730 | |||
731 | list_add_tail(&extra_response->er_list, | ||
732 | ¶m_list->extra_response_list); | ||
733 | return 0; | ||
734 | } | ||
735 | |||
736 | static int iscsi_check_for_auth_key(char *key) | ||
737 | { | ||
738 | /* | ||
739 | * RFC 1994 | ||
740 | */ | ||
741 | if (!strcmp(key, "CHAP_A") || !strcmp(key, "CHAP_I") || | ||
742 | !strcmp(key, "CHAP_C") || !strcmp(key, "CHAP_N") || | ||
743 | !strcmp(key, "CHAP_R")) | ||
744 | return 1; | ||
745 | |||
746 | /* | ||
747 | * RFC 2945 | ||
748 | */ | ||
749 | if (!strcmp(key, "SRP_U") || !strcmp(key, "SRP_N") || | ||
750 | !strcmp(key, "SRP_g") || !strcmp(key, "SRP_s") || | ||
751 | !strcmp(key, "SRP_A") || !strcmp(key, "SRP_B") || | ||
752 | !strcmp(key, "SRP_M") || !strcmp(key, "SRP_HM")) | ||
753 | return 1; | ||
754 | |||
755 | return 0; | ||
756 | } | ||
757 | |||
758 | static void iscsi_check_proposer_for_optional_reply(struct iscsi_param *param) | ||
759 | { | ||
760 | if (IS_TYPE_BOOL_AND(param)) { | ||
761 | if (!strcmp(param->value, NO)) | ||
762 | SET_PSTATE_REPLY_OPTIONAL(param); | ||
763 | } else if (IS_TYPE_BOOL_OR(param)) { | ||
764 | if (!strcmp(param->value, YES)) | ||
765 | SET_PSTATE_REPLY_OPTIONAL(param); | ||
766 | /* | ||
767 | * Required for gPXE iSCSI boot client | ||
768 | */ | ||
769 | if (!strcmp(param->name, IMMEDIATEDATA)) | ||
770 | SET_PSTATE_REPLY_OPTIONAL(param); | ||
771 | } else if (IS_TYPE_NUMBER(param)) { | ||
772 | if (!strcmp(param->name, MAXRECVDATASEGMENTLENGTH)) | ||
773 | SET_PSTATE_REPLY_OPTIONAL(param); | ||
774 | /* | ||
775 | * The GlobalSAN iSCSI Initiator for MacOSX does | ||
776 | * not respond to MaxBurstLength, FirstBurstLength, | ||
777 | * DefaultTime2Wait or DefaultTime2Retain parameter keys. | ||
778 | * So, we set them to 'reply optional' here, and assume the | ||
779 | * the defaults from iscsi_parameters.h if the initiator | ||
780 | * is not RFC compliant and the keys are not negotiated. | ||
781 | */ | ||
782 | if (!strcmp(param->name, MAXBURSTLENGTH)) | ||
783 | SET_PSTATE_REPLY_OPTIONAL(param); | ||
784 | if (!strcmp(param->name, FIRSTBURSTLENGTH)) | ||
785 | SET_PSTATE_REPLY_OPTIONAL(param); | ||
786 | if (!strcmp(param->name, DEFAULTTIME2WAIT)) | ||
787 | SET_PSTATE_REPLY_OPTIONAL(param); | ||
788 | if (!strcmp(param->name, DEFAULTTIME2RETAIN)) | ||
789 | SET_PSTATE_REPLY_OPTIONAL(param); | ||
790 | /* | ||
791 | * Required for gPXE iSCSI boot client | ||
792 | */ | ||
793 | if (!strcmp(param->name, MAXCONNECTIONS)) | ||
794 | SET_PSTATE_REPLY_OPTIONAL(param); | ||
795 | } else if (IS_PHASE_DECLARATIVE(param)) | ||
796 | SET_PSTATE_REPLY_OPTIONAL(param); | ||
797 | } | ||
798 | |||
799 | static int iscsi_check_boolean_value(struct iscsi_param *param, char *value) | ||
800 | { | ||
801 | if (strcmp(value, YES) && strcmp(value, NO)) { | ||
802 | pr_err("Illegal value for \"%s\", must be either" | ||
803 | " \"%s\" or \"%s\".\n", param->name, YES, NO); | ||
804 | return -1; | ||
805 | } | ||
806 | |||
807 | return 0; | ||
808 | } | ||
809 | |||
810 | static int iscsi_check_numerical_value(struct iscsi_param *param, char *value_ptr) | ||
811 | { | ||
812 | char *tmpptr; | ||
813 | int value = 0; | ||
814 | |||
815 | value = simple_strtoul(value_ptr, &tmpptr, 0); | ||
816 | |||
817 | /* #warning FIXME: Fix this */ | ||
818 | #if 0 | ||
819 | if (strspn(endptr, WHITE_SPACE) != strlen(endptr)) { | ||
820 | pr_err("Illegal value \"%s\" for \"%s\".\n", | ||
821 | value, param->name); | ||
822 | return -1; | ||
823 | } | ||
824 | #endif | ||
825 | if (IS_TYPERANGE_0_TO_2(param)) { | ||
826 | if ((value < 0) || (value > 2)) { | ||
827 | pr_err("Illegal value for \"%s\", must be" | ||
828 | " between 0 and 2.\n", param->name); | ||
829 | return -1; | ||
830 | } | ||
831 | return 0; | ||
832 | } | ||
833 | if (IS_TYPERANGE_0_TO_3600(param)) { | ||
834 | if ((value < 0) || (value > 3600)) { | ||
835 | pr_err("Illegal value for \"%s\", must be" | ||
836 | " between 0 and 3600.\n", param->name); | ||
837 | return -1; | ||
838 | } | ||
839 | return 0; | ||
840 | } | ||
841 | if (IS_TYPERANGE_0_TO_32767(param)) { | ||
842 | if ((value < 0) || (value > 32767)) { | ||
843 | pr_err("Illegal value for \"%s\", must be" | ||
844 | " between 0 and 32767.\n", param->name); | ||
845 | return -1; | ||
846 | } | ||
847 | return 0; | ||
848 | } | ||
849 | if (IS_TYPERANGE_0_TO_65535(param)) { | ||
850 | if ((value < 0) || (value > 65535)) { | ||
851 | pr_err("Illegal value for \"%s\", must be" | ||
852 | " between 0 and 65535.\n", param->name); | ||
853 | return -1; | ||
854 | } | ||
855 | return 0; | ||
856 | } | ||
857 | if (IS_TYPERANGE_1_TO_65535(param)) { | ||
858 | if ((value < 1) || (value > 65535)) { | ||
859 | pr_err("Illegal value for \"%s\", must be" | ||
860 | " between 1 and 65535.\n", param->name); | ||
861 | return -1; | ||
862 | } | ||
863 | return 0; | ||
864 | } | ||
865 | if (IS_TYPERANGE_2_TO_3600(param)) { | ||
866 | if ((value < 2) || (value > 3600)) { | ||
867 | pr_err("Illegal value for \"%s\", must be" | ||
868 | " between 2 and 3600.\n", param->name); | ||
869 | return -1; | ||
870 | } | ||
871 | return 0; | ||
872 | } | ||
873 | if (IS_TYPERANGE_512_TO_16777215(param)) { | ||
874 | if ((value < 512) || (value > 16777215)) { | ||
875 | pr_err("Illegal value for \"%s\", must be" | ||
876 | " between 512 and 16777215.\n", param->name); | ||
877 | return -1; | ||
878 | } | ||
879 | return 0; | ||
880 | } | ||
881 | |||
882 | return 0; | ||
883 | } | ||
884 | |||
885 | static int iscsi_check_numerical_range_value(struct iscsi_param *param, char *value) | ||
886 | { | ||
887 | char *left_val_ptr = NULL, *right_val_ptr = NULL; | ||
888 | char *tilde_ptr = NULL, *tmp_ptr = NULL; | ||
889 | u32 left_val, right_val, local_left_val, local_right_val; | ||
890 | |||
891 | if (strcmp(param->name, IFMARKINT) && | ||
892 | strcmp(param->name, OFMARKINT)) { | ||
893 | pr_err("Only parameters \"%s\" or \"%s\" may contain a" | ||
894 | " numerical range value.\n", IFMARKINT, OFMARKINT); | ||
895 | return -1; | ||
896 | } | ||
897 | |||
898 | if (IS_PSTATE_PROPOSER(param)) | ||
899 | return 0; | ||
900 | |||
901 | tilde_ptr = strchr(value, '~'); | ||
902 | if (!tilde_ptr) { | ||
903 | pr_err("Unable to locate numerical range indicator" | ||
904 | " \"~\" for \"%s\".\n", param->name); | ||
905 | return -1; | ||
906 | } | ||
907 | *tilde_ptr = '\0'; | ||
908 | |||
909 | left_val_ptr = value; | ||
910 | right_val_ptr = value + strlen(left_val_ptr) + 1; | ||
911 | |||
912 | if (iscsi_check_numerical_value(param, left_val_ptr) < 0) | ||
913 | return -1; | ||
914 | if (iscsi_check_numerical_value(param, right_val_ptr) < 0) | ||
915 | return -1; | ||
916 | |||
917 | left_val = simple_strtoul(left_val_ptr, &tmp_ptr, 0); | ||
918 | right_val = simple_strtoul(right_val_ptr, &tmp_ptr, 0); | ||
919 | *tilde_ptr = '~'; | ||
920 | |||
921 | if (right_val < left_val) { | ||
922 | pr_err("Numerical range for parameter \"%s\" contains" | ||
923 | " a right value which is less than the left.\n", | ||
924 | param->name); | ||
925 | return -1; | ||
926 | } | ||
927 | |||
928 | /* | ||
929 | * For now, enforce reasonable defaults for [I,O]FMarkInt. | ||
930 | */ | ||
931 | tilde_ptr = strchr(param->value, '~'); | ||
932 | if (!tilde_ptr) { | ||
933 | pr_err("Unable to locate numerical range indicator" | ||
934 | " \"~\" for \"%s\".\n", param->name); | ||
935 | return -1; | ||
936 | } | ||
937 | *tilde_ptr = '\0'; | ||
938 | |||
939 | left_val_ptr = param->value; | ||
940 | right_val_ptr = param->value + strlen(left_val_ptr) + 1; | ||
941 | |||
942 | local_left_val = simple_strtoul(left_val_ptr, &tmp_ptr, 0); | ||
943 | local_right_val = simple_strtoul(right_val_ptr, &tmp_ptr, 0); | ||
944 | *tilde_ptr = '~'; | ||
945 | |||
946 | if (param->set_param) { | ||
947 | if ((left_val < local_left_val) || | ||
948 | (right_val < local_left_val)) { | ||
949 | pr_err("Passed value range \"%u~%u\" is below" | ||
950 | " minimum left value \"%u\" for key \"%s\"," | ||
951 | " rejecting.\n", left_val, right_val, | ||
952 | local_left_val, param->name); | ||
953 | return -1; | ||
954 | } | ||
955 | } else { | ||
956 | if ((left_val < local_left_val) && | ||
957 | (right_val < local_left_val)) { | ||
958 | pr_err("Received value range \"%u~%u\" is" | ||
959 | " below minimum left value \"%u\" for key" | ||
960 | " \"%s\", rejecting.\n", left_val, right_val, | ||
961 | local_left_val, param->name); | ||
962 | SET_PSTATE_REJECT(param); | ||
963 | if (iscsi_update_param_value(param, REJECT) < 0) | ||
964 | return -1; | ||
965 | } | ||
966 | } | ||
967 | |||
968 | return 0; | ||
969 | } | ||
970 | |||
971 | static int iscsi_check_string_or_list_value(struct iscsi_param *param, char *value) | ||
972 | { | ||
973 | if (IS_PSTATE_PROPOSER(param)) | ||
974 | return 0; | ||
975 | |||
976 | if (IS_TYPERANGE_AUTH_PARAM(param)) { | ||
977 | if (strcmp(value, KRB5) && strcmp(value, SPKM1) && | ||
978 | strcmp(value, SPKM2) && strcmp(value, SRP) && | ||
979 | strcmp(value, CHAP) && strcmp(value, NONE)) { | ||
980 | pr_err("Illegal value for \"%s\", must be" | ||
981 | " \"%s\", \"%s\", \"%s\", \"%s\", \"%s\"" | ||
982 | " or \"%s\".\n", param->name, KRB5, | ||
983 | SPKM1, SPKM2, SRP, CHAP, NONE); | ||
984 | return -1; | ||
985 | } | ||
986 | } | ||
987 | if (IS_TYPERANGE_DIGEST_PARAM(param)) { | ||
988 | if (strcmp(value, CRC32C) && strcmp(value, NONE)) { | ||
989 | pr_err("Illegal value for \"%s\", must be" | ||
990 | " \"%s\" or \"%s\".\n", param->name, | ||
991 | CRC32C, NONE); | ||
992 | return -1; | ||
993 | } | ||
994 | } | ||
995 | if (IS_TYPERANGE_SESSIONTYPE(param)) { | ||
996 | if (strcmp(value, DISCOVERY) && strcmp(value, NORMAL)) { | ||
997 | pr_err("Illegal value for \"%s\", must be" | ||
998 | " \"%s\" or \"%s\".\n", param->name, | ||
999 | DISCOVERY, NORMAL); | ||
1000 | return -1; | ||
1001 | } | ||
1002 | } | ||
1003 | |||
1004 | return 0; | ||
1005 | } | ||
1006 | |||
1007 | /* | ||
1008 | * This function is used to pick a value range number, currently just | ||
1009 | * returns the lesser of both right values. | ||
1010 | */ | ||
1011 | static char *iscsi_get_value_from_number_range( | ||
1012 | struct iscsi_param *param, | ||
1013 | char *value) | ||
1014 | { | ||
1015 | char *end_ptr, *tilde_ptr1 = NULL, *tilde_ptr2 = NULL; | ||
1016 | u32 acceptor_right_value, proposer_right_value; | ||
1017 | |||
1018 | tilde_ptr1 = strchr(value, '~'); | ||
1019 | if (!tilde_ptr1) | ||
1020 | return NULL; | ||
1021 | *tilde_ptr1++ = '\0'; | ||
1022 | proposer_right_value = simple_strtoul(tilde_ptr1, &end_ptr, 0); | ||
1023 | |||
1024 | tilde_ptr2 = strchr(param->value, '~'); | ||
1025 | if (!tilde_ptr2) | ||
1026 | return NULL; | ||
1027 | *tilde_ptr2++ = '\0'; | ||
1028 | acceptor_right_value = simple_strtoul(tilde_ptr2, &end_ptr, 0); | ||
1029 | |||
1030 | return (acceptor_right_value >= proposer_right_value) ? | ||
1031 | tilde_ptr1 : tilde_ptr2; | ||
1032 | } | ||
1033 | |||
1034 | static char *iscsi_check_valuelist_for_support( | ||
1035 | struct iscsi_param *param, | ||
1036 | char *value) | ||
1037 | { | ||
1038 | char *tmp1 = NULL, *tmp2 = NULL; | ||
1039 | char *acceptor_values = NULL, *proposer_values = NULL; | ||
1040 | |||
1041 | acceptor_values = param->value; | ||
1042 | proposer_values = value; | ||
1043 | |||
1044 | do { | ||
1045 | if (!proposer_values) | ||
1046 | return NULL; | ||
1047 | tmp1 = strchr(proposer_values, ','); | ||
1048 | if (tmp1) | ||
1049 | *tmp1 = '\0'; | ||
1050 | acceptor_values = param->value; | ||
1051 | do { | ||
1052 | if (!acceptor_values) { | ||
1053 | if (tmp1) | ||
1054 | *tmp1 = ','; | ||
1055 | return NULL; | ||
1056 | } | ||
1057 | tmp2 = strchr(acceptor_values, ','); | ||
1058 | if (tmp2) | ||
1059 | *tmp2 = '\0'; | ||
1060 | if (!acceptor_values || !proposer_values) { | ||
1061 | if (tmp1) | ||
1062 | *tmp1 = ','; | ||
1063 | if (tmp2) | ||
1064 | *tmp2 = ','; | ||
1065 | return NULL; | ||
1066 | } | ||
1067 | if (!strcmp(acceptor_values, proposer_values)) { | ||
1068 | if (tmp2) | ||
1069 | *tmp2 = ','; | ||
1070 | goto out; | ||
1071 | } | ||
1072 | if (tmp2) | ||
1073 | *tmp2++ = ','; | ||
1074 | |||
1075 | acceptor_values = tmp2; | ||
1076 | if (!acceptor_values) | ||
1077 | break; | ||
1078 | } while (acceptor_values); | ||
1079 | if (tmp1) | ||
1080 | *tmp1++ = ','; | ||
1081 | proposer_values = tmp1; | ||
1082 | } while (proposer_values); | ||
1083 | |||
1084 | out: | ||
1085 | return proposer_values; | ||
1086 | } | ||
1087 | |||
1088 | static int iscsi_check_acceptor_state(struct iscsi_param *param, char *value) | ||
1089 | { | ||
1090 | u8 acceptor_boolean_value = 0, proposer_boolean_value = 0; | ||
1091 | char *negoitated_value = NULL; | ||
1092 | |||
1093 | if (IS_PSTATE_ACCEPTOR(param)) { | ||
1094 | pr_err("Received key \"%s\" twice, protocol error.\n", | ||
1095 | param->name); | ||
1096 | return -1; | ||
1097 | } | ||
1098 | |||
1099 | if (IS_PSTATE_REJECT(param)) | ||
1100 | return 0; | ||
1101 | |||
1102 | if (IS_TYPE_BOOL_AND(param)) { | ||
1103 | if (!strcmp(value, YES)) | ||
1104 | proposer_boolean_value = 1; | ||
1105 | if (!strcmp(param->value, YES)) | ||
1106 | acceptor_boolean_value = 1; | ||
1107 | if (acceptor_boolean_value && proposer_boolean_value) | ||
1108 | do {} while (0); | ||
1109 | else { | ||
1110 | if (iscsi_update_param_value(param, NO) < 0) | ||
1111 | return -1; | ||
1112 | if (!proposer_boolean_value) | ||
1113 | SET_PSTATE_REPLY_OPTIONAL(param); | ||
1114 | } | ||
1115 | } else if (IS_TYPE_BOOL_OR(param)) { | ||
1116 | if (!strcmp(value, YES)) | ||
1117 | proposer_boolean_value = 1; | ||
1118 | if (!strcmp(param->value, YES)) | ||
1119 | acceptor_boolean_value = 1; | ||
1120 | if (acceptor_boolean_value || proposer_boolean_value) { | ||
1121 | if (iscsi_update_param_value(param, YES) < 0) | ||
1122 | return -1; | ||
1123 | if (proposer_boolean_value) | ||
1124 | SET_PSTATE_REPLY_OPTIONAL(param); | ||
1125 | } | ||
1126 | } else if (IS_TYPE_NUMBER(param)) { | ||
1127 | char *tmpptr, buf[10]; | ||
1128 | u32 acceptor_value = simple_strtoul(param->value, &tmpptr, 0); | ||
1129 | u32 proposer_value = simple_strtoul(value, &tmpptr, 0); | ||
1130 | |||
1131 | memset(buf, 0, 10); | ||
1132 | |||
1133 | if (!strcmp(param->name, MAXCONNECTIONS) || | ||
1134 | !strcmp(param->name, MAXBURSTLENGTH) || | ||
1135 | !strcmp(param->name, FIRSTBURSTLENGTH) || | ||
1136 | !strcmp(param->name, MAXOUTSTANDINGR2T) || | ||
1137 | !strcmp(param->name, DEFAULTTIME2RETAIN) || | ||
1138 | !strcmp(param->name, ERRORRECOVERYLEVEL)) { | ||
1139 | if (proposer_value > acceptor_value) { | ||
1140 | sprintf(buf, "%u", acceptor_value); | ||
1141 | if (iscsi_update_param_value(param, | ||
1142 | &buf[0]) < 0) | ||
1143 | return -1; | ||
1144 | } else { | ||
1145 | if (iscsi_update_param_value(param, value) < 0) | ||
1146 | return -1; | ||
1147 | } | ||
1148 | } else if (!strcmp(param->name, DEFAULTTIME2WAIT)) { | ||
1149 | if (acceptor_value > proposer_value) { | ||
1150 | sprintf(buf, "%u", acceptor_value); | ||
1151 | if (iscsi_update_param_value(param, | ||
1152 | &buf[0]) < 0) | ||
1153 | return -1; | ||
1154 | } else { | ||
1155 | if (iscsi_update_param_value(param, value) < 0) | ||
1156 | return -1; | ||
1157 | } | ||
1158 | } else { | ||
1159 | if (iscsi_update_param_value(param, value) < 0) | ||
1160 | return -1; | ||
1161 | } | ||
1162 | |||
1163 | if (!strcmp(param->name, MAXRECVDATASEGMENTLENGTH)) | ||
1164 | SET_PSTATE_REPLY_OPTIONAL(param); | ||
1165 | } else if (IS_TYPE_NUMBER_RANGE(param)) { | ||
1166 | negoitated_value = iscsi_get_value_from_number_range( | ||
1167 | param, value); | ||
1168 | if (!negoitated_value) | ||
1169 | return -1; | ||
1170 | if (iscsi_update_param_value(param, negoitated_value) < 0) | ||
1171 | return -1; | ||
1172 | } else if (IS_TYPE_VALUE_LIST(param)) { | ||
1173 | negoitated_value = iscsi_check_valuelist_for_support( | ||
1174 | param, value); | ||
1175 | if (!negoitated_value) { | ||
1176 | pr_err("Proposer's value list \"%s\" contains" | ||
1177 | " no valid values from Acceptor's value list" | ||
1178 | " \"%s\".\n", value, param->value); | ||
1179 | return -1; | ||
1180 | } | ||
1181 | if (iscsi_update_param_value(param, negoitated_value) < 0) | ||
1182 | return -1; | ||
1183 | } else if (IS_PHASE_DECLARATIVE(param)) { | ||
1184 | if (iscsi_update_param_value(param, value) < 0) | ||
1185 | return -1; | ||
1186 | SET_PSTATE_REPLY_OPTIONAL(param); | ||
1187 | } | ||
1188 | |||
1189 | return 0; | ||
1190 | } | ||
1191 | |||
1192 | static int iscsi_check_proposer_state(struct iscsi_param *param, char *value) | ||
1193 | { | ||
1194 | if (IS_PSTATE_RESPONSE_GOT(param)) { | ||
1195 | pr_err("Received key \"%s\" twice, protocol error.\n", | ||
1196 | param->name); | ||
1197 | return -1; | ||
1198 | } | ||
1199 | |||
1200 | if (IS_TYPE_NUMBER_RANGE(param)) { | ||
1201 | u32 left_val = 0, right_val = 0, recieved_value = 0; | ||
1202 | char *left_val_ptr = NULL, *right_val_ptr = NULL; | ||
1203 | char *tilde_ptr = NULL, *tmp_ptr = NULL; | ||
1204 | |||
1205 | if (!strcmp(value, IRRELEVANT) || !strcmp(value, REJECT)) { | ||
1206 | if (iscsi_update_param_value(param, value) < 0) | ||
1207 | return -1; | ||
1208 | return 0; | ||
1209 | } | ||
1210 | |||
1211 | tilde_ptr = strchr(value, '~'); | ||
1212 | if (tilde_ptr) { | ||
1213 | pr_err("Illegal \"~\" in response for \"%s\".\n", | ||
1214 | param->name); | ||
1215 | return -1; | ||
1216 | } | ||
1217 | tilde_ptr = strchr(param->value, '~'); | ||
1218 | if (!tilde_ptr) { | ||
1219 | pr_err("Unable to locate numerical range" | ||
1220 | " indicator \"~\" for \"%s\".\n", param->name); | ||
1221 | return -1; | ||
1222 | } | ||
1223 | *tilde_ptr = '\0'; | ||
1224 | |||
1225 | left_val_ptr = param->value; | ||
1226 | right_val_ptr = param->value + strlen(left_val_ptr) + 1; | ||
1227 | left_val = simple_strtoul(left_val_ptr, &tmp_ptr, 0); | ||
1228 | right_val = simple_strtoul(right_val_ptr, &tmp_ptr, 0); | ||
1229 | recieved_value = simple_strtoul(value, &tmp_ptr, 0); | ||
1230 | |||
1231 | *tilde_ptr = '~'; | ||
1232 | |||
1233 | if ((recieved_value < left_val) || | ||
1234 | (recieved_value > right_val)) { | ||
1235 | pr_err("Illegal response \"%s=%u\", value must" | ||
1236 | " be between %u and %u.\n", param->name, | ||
1237 | recieved_value, left_val, right_val); | ||
1238 | return -1; | ||
1239 | } | ||
1240 | } else if (IS_TYPE_VALUE_LIST(param)) { | ||
1241 | char *comma_ptr = NULL, *tmp_ptr = NULL; | ||
1242 | |||
1243 | comma_ptr = strchr(value, ','); | ||
1244 | if (comma_ptr) { | ||
1245 | pr_err("Illegal \",\" in response for \"%s\".\n", | ||
1246 | param->name); | ||
1247 | return -1; | ||
1248 | } | ||
1249 | |||
1250 | tmp_ptr = iscsi_check_valuelist_for_support(param, value); | ||
1251 | if (!tmp_ptr) | ||
1252 | return -1; | ||
1253 | } | ||
1254 | |||
1255 | if (iscsi_update_param_value(param, value) < 0) | ||
1256 | return -1; | ||
1257 | |||
1258 | return 0; | ||
1259 | } | ||
1260 | |||
1261 | static int iscsi_check_value(struct iscsi_param *param, char *value) | ||
1262 | { | ||
1263 | char *comma_ptr = NULL; | ||
1264 | |||
1265 | if (!strcmp(value, REJECT)) { | ||
1266 | if (!strcmp(param->name, IFMARKINT) || | ||
1267 | !strcmp(param->name, OFMARKINT)) { | ||
1268 | /* | ||
1269 | * Reject is not fatal for [I,O]FMarkInt, and causes | ||
1270 | * [I,O]FMarker to be reset to No. (See iSCSI v20 A.3.2) | ||
1271 | */ | ||
1272 | SET_PSTATE_REJECT(param); | ||
1273 | return 0; | ||
1274 | } | ||
1275 | pr_err("Received %s=%s\n", param->name, value); | ||
1276 | return -1; | ||
1277 | } | ||
1278 | if (!strcmp(value, IRRELEVANT)) { | ||
1279 | pr_debug("Received %s=%s\n", param->name, value); | ||
1280 | SET_PSTATE_IRRELEVANT(param); | ||
1281 | return 0; | ||
1282 | } | ||
1283 | if (!strcmp(value, NOTUNDERSTOOD)) { | ||
1284 | if (!IS_PSTATE_PROPOSER(param)) { | ||
1285 | pr_err("Received illegal offer %s=%s\n", | ||
1286 | param->name, value); | ||
1287 | return -1; | ||
1288 | } | ||
1289 | |||
1290 | /* #warning FIXME: Add check for X-ExtensionKey here */ | ||
1291 | pr_err("Standard iSCSI key \"%s\" cannot be answered" | ||
1292 | " with \"%s\", protocol error.\n", param->name, value); | ||
1293 | return -1; | ||
1294 | } | ||
1295 | |||
1296 | do { | ||
1297 | comma_ptr = NULL; | ||
1298 | comma_ptr = strchr(value, ','); | ||
1299 | |||
1300 | if (comma_ptr && !IS_TYPE_VALUE_LIST(param)) { | ||
1301 | pr_err("Detected value seperator \",\", but" | ||
1302 | " key \"%s\" does not allow a value list," | ||
1303 | " protocol error.\n", param->name); | ||
1304 | return -1; | ||
1305 | } | ||
1306 | if (comma_ptr) | ||
1307 | *comma_ptr = '\0'; | ||
1308 | |||
1309 | if (strlen(value) > VALUE_MAXLEN) { | ||
1310 | pr_err("Value for key \"%s\" exceeds %d," | ||
1311 | " protocol error.\n", param->name, | ||
1312 | VALUE_MAXLEN); | ||
1313 | return -1; | ||
1314 | } | ||
1315 | |||
1316 | if (IS_TYPE_BOOL_AND(param) || IS_TYPE_BOOL_OR(param)) { | ||
1317 | if (iscsi_check_boolean_value(param, value) < 0) | ||
1318 | return -1; | ||
1319 | } else if (IS_TYPE_NUMBER(param)) { | ||
1320 | if (iscsi_check_numerical_value(param, value) < 0) | ||
1321 | return -1; | ||
1322 | } else if (IS_TYPE_NUMBER_RANGE(param)) { | ||
1323 | if (iscsi_check_numerical_range_value(param, value) < 0) | ||
1324 | return -1; | ||
1325 | } else if (IS_TYPE_STRING(param) || IS_TYPE_VALUE_LIST(param)) { | ||
1326 | if (iscsi_check_string_or_list_value(param, value) < 0) | ||
1327 | return -1; | ||
1328 | } else { | ||
1329 | pr_err("Huh? 0x%02x\n", param->type); | ||
1330 | return -1; | ||
1331 | } | ||
1332 | |||
1333 | if (comma_ptr) | ||
1334 | *comma_ptr++ = ','; | ||
1335 | |||
1336 | value = comma_ptr; | ||
1337 | } while (value); | ||
1338 | |||
1339 | return 0; | ||
1340 | } | ||
1341 | |||
1342 | static struct iscsi_param *__iscsi_check_key( | ||
1343 | char *key, | ||
1344 | int sender, | ||
1345 | struct iscsi_param_list *param_list) | ||
1346 | { | ||
1347 | struct iscsi_param *param; | ||
1348 | |||
1349 | if (strlen(key) > KEY_MAXLEN) { | ||
1350 | pr_err("Length of key name \"%s\" exceeds %d.\n", | ||
1351 | key, KEY_MAXLEN); | ||
1352 | return NULL; | ||
1353 | } | ||
1354 | |||
1355 | param = iscsi_find_param_from_key(key, param_list); | ||
1356 | if (!param) | ||
1357 | return NULL; | ||
1358 | |||
1359 | if ((sender & SENDER_INITIATOR) && !IS_SENDER_INITIATOR(param)) { | ||
1360 | pr_err("Key \"%s\" may not be sent to %s," | ||
1361 | " protocol error.\n", param->name, | ||
1362 | (sender & SENDER_RECEIVER) ? "target" : "initiator"); | ||
1363 | return NULL; | ||
1364 | } | ||
1365 | |||
1366 | if ((sender & SENDER_TARGET) && !IS_SENDER_TARGET(param)) { | ||
1367 | pr_err("Key \"%s\" may not be sent to %s," | ||
1368 | " protocol error.\n", param->name, | ||
1369 | (sender & SENDER_RECEIVER) ? "initiator" : "target"); | ||
1370 | return NULL; | ||
1371 | } | ||
1372 | |||
1373 | return param; | ||
1374 | } | ||
1375 | |||
1376 | static struct iscsi_param *iscsi_check_key( | ||
1377 | char *key, | ||
1378 | int phase, | ||
1379 | int sender, | ||
1380 | struct iscsi_param_list *param_list) | ||
1381 | { | ||
1382 | struct iscsi_param *param; | ||
1383 | /* | ||
1384 | * Key name length must not exceed 63 bytes. (See iSCSI v20 5.1) | ||
1385 | */ | ||
1386 | if (strlen(key) > KEY_MAXLEN) { | ||
1387 | pr_err("Length of key name \"%s\" exceeds %d.\n", | ||
1388 | key, KEY_MAXLEN); | ||
1389 | return NULL; | ||
1390 | } | ||
1391 | |||
1392 | param = iscsi_find_param_from_key(key, param_list); | ||
1393 | if (!param) | ||
1394 | return NULL; | ||
1395 | |||
1396 | if ((sender & SENDER_INITIATOR) && !IS_SENDER_INITIATOR(param)) { | ||
1397 | pr_err("Key \"%s\" may not be sent to %s," | ||
1398 | " protocol error.\n", param->name, | ||
1399 | (sender & SENDER_RECEIVER) ? "target" : "initiator"); | ||
1400 | return NULL; | ||
1401 | } | ||
1402 | if ((sender & SENDER_TARGET) && !IS_SENDER_TARGET(param)) { | ||
1403 | pr_err("Key \"%s\" may not be sent to %s," | ||
1404 | " protocol error.\n", param->name, | ||
1405 | (sender & SENDER_RECEIVER) ? "initiator" : "target"); | ||
1406 | return NULL; | ||
1407 | } | ||
1408 | |||
1409 | if (IS_PSTATE_ACCEPTOR(param)) { | ||
1410 | pr_err("Key \"%s\" received twice, protocol error.\n", | ||
1411 | key); | ||
1412 | return NULL; | ||
1413 | } | ||
1414 | |||
1415 | if (!phase) | ||
1416 | return param; | ||
1417 | |||
1418 | if (!(param->phase & phase)) { | ||
1419 | pr_err("Key \"%s\" may not be negotiated during ", | ||
1420 | param->name); | ||
1421 | switch (phase) { | ||
1422 | case PHASE_SECURITY: | ||
1423 | pr_debug("Security phase.\n"); | ||
1424 | break; | ||
1425 | case PHASE_OPERATIONAL: | ||
1426 | pr_debug("Operational phase.\n"); | ||
1427 | default: | ||
1428 | pr_debug("Unknown phase.\n"); | ||
1429 | } | ||
1430 | return NULL; | ||
1431 | } | ||
1432 | |||
1433 | return param; | ||
1434 | } | ||
1435 | |||
1436 | static int iscsi_enforce_integrity_rules( | ||
1437 | u8 phase, | ||
1438 | struct iscsi_param_list *param_list) | ||
1439 | { | ||
1440 | char *tmpptr; | ||
1441 | u8 DataSequenceInOrder = 0; | ||
1442 | u8 ErrorRecoveryLevel = 0, SessionType = 0; | ||
1443 | u8 IFMarker = 0, OFMarker = 0; | ||
1444 | u8 IFMarkInt_Reject = 0, OFMarkInt_Reject = 0; | ||
1445 | u32 FirstBurstLength = 0, MaxBurstLength = 0; | ||
1446 | struct iscsi_param *param = NULL; | ||
1447 | |||
1448 | list_for_each_entry(param, ¶m_list->param_list, p_list) { | ||
1449 | if (!(param->phase & phase)) | ||
1450 | continue; | ||
1451 | if (!strcmp(param->name, SESSIONTYPE)) | ||
1452 | if (!strcmp(param->value, NORMAL)) | ||
1453 | SessionType = 1; | ||
1454 | if (!strcmp(param->name, ERRORRECOVERYLEVEL)) | ||
1455 | ErrorRecoveryLevel = simple_strtoul(param->value, | ||
1456 | &tmpptr, 0); | ||
1457 | if (!strcmp(param->name, DATASEQUENCEINORDER)) | ||
1458 | if (!strcmp(param->value, YES)) | ||
1459 | DataSequenceInOrder = 1; | ||
1460 | if (!strcmp(param->name, MAXBURSTLENGTH)) | ||
1461 | MaxBurstLength = simple_strtoul(param->value, | ||
1462 | &tmpptr, 0); | ||
1463 | if (!strcmp(param->name, IFMARKER)) | ||
1464 | if (!strcmp(param->value, YES)) | ||
1465 | IFMarker = 1; | ||
1466 | if (!strcmp(param->name, OFMARKER)) | ||
1467 | if (!strcmp(param->value, YES)) | ||
1468 | OFMarker = 1; | ||
1469 | if (!strcmp(param->name, IFMARKINT)) | ||
1470 | if (!strcmp(param->value, REJECT)) | ||
1471 | IFMarkInt_Reject = 1; | ||
1472 | if (!strcmp(param->name, OFMARKINT)) | ||
1473 | if (!strcmp(param->value, REJECT)) | ||
1474 | OFMarkInt_Reject = 1; | ||
1475 | } | ||
1476 | |||
1477 | list_for_each_entry(param, ¶m_list->param_list, p_list) { | ||
1478 | if (!(param->phase & phase)) | ||
1479 | continue; | ||
1480 | if (!SessionType && (!IS_PSTATE_ACCEPTOR(param) && | ||
1481 | (strcmp(param->name, IFMARKER) && | ||
1482 | strcmp(param->name, OFMARKER) && | ||
1483 | strcmp(param->name, IFMARKINT) && | ||
1484 | strcmp(param->name, OFMARKINT)))) | ||
1485 | continue; | ||
1486 | if (!strcmp(param->name, MAXOUTSTANDINGR2T) && | ||
1487 | DataSequenceInOrder && (ErrorRecoveryLevel > 0)) { | ||
1488 | if (strcmp(param->value, "1")) { | ||
1489 | if (iscsi_update_param_value(param, "1") < 0) | ||
1490 | return -1; | ||
1491 | pr_debug("Reset \"%s\" to \"%s\".\n", | ||
1492 | param->name, param->value); | ||
1493 | } | ||
1494 | } | ||
1495 | if (!strcmp(param->name, MAXCONNECTIONS) && !SessionType) { | ||
1496 | if (strcmp(param->value, "1")) { | ||
1497 | if (iscsi_update_param_value(param, "1") < 0) | ||
1498 | return -1; | ||
1499 | pr_debug("Reset \"%s\" to \"%s\".\n", | ||
1500 | param->name, param->value); | ||
1501 | } | ||
1502 | } | ||
1503 | if (!strcmp(param->name, FIRSTBURSTLENGTH)) { | ||
1504 | FirstBurstLength = simple_strtoul(param->value, | ||
1505 | &tmpptr, 0); | ||
1506 | if (FirstBurstLength > MaxBurstLength) { | ||
1507 | char tmpbuf[10]; | ||
1508 | memset(tmpbuf, 0, 10); | ||
1509 | sprintf(tmpbuf, "%u", MaxBurstLength); | ||
1510 | if (iscsi_update_param_value(param, tmpbuf)) | ||
1511 | return -1; | ||
1512 | pr_debug("Reset \"%s\" to \"%s\".\n", | ||
1513 | param->name, param->value); | ||
1514 | } | ||
1515 | } | ||
1516 | if (!strcmp(param->name, IFMARKER) && IFMarkInt_Reject) { | ||
1517 | if (iscsi_update_param_value(param, NO) < 0) | ||
1518 | return -1; | ||
1519 | IFMarker = 0; | ||
1520 | pr_debug("Reset \"%s\" to \"%s\".\n", | ||
1521 | param->name, param->value); | ||
1522 | } | ||
1523 | if (!strcmp(param->name, OFMARKER) && OFMarkInt_Reject) { | ||
1524 | if (iscsi_update_param_value(param, NO) < 0) | ||
1525 | return -1; | ||
1526 | OFMarker = 0; | ||
1527 | pr_debug("Reset \"%s\" to \"%s\".\n", | ||
1528 | param->name, param->value); | ||
1529 | } | ||
1530 | if (!strcmp(param->name, IFMARKINT) && !IFMarker) { | ||
1531 | if (!strcmp(param->value, REJECT)) | ||
1532 | continue; | ||
1533 | param->state &= ~PSTATE_NEGOTIATE; | ||
1534 | if (iscsi_update_param_value(param, IRRELEVANT) < 0) | ||
1535 | return -1; | ||
1536 | pr_debug("Reset \"%s\" to \"%s\".\n", | ||
1537 | param->name, param->value); | ||
1538 | } | ||
1539 | if (!strcmp(param->name, OFMARKINT) && !OFMarker) { | ||
1540 | if (!strcmp(param->value, REJECT)) | ||
1541 | continue; | ||
1542 | param->state &= ~PSTATE_NEGOTIATE; | ||
1543 | if (iscsi_update_param_value(param, IRRELEVANT) < 0) | ||
1544 | return -1; | ||
1545 | pr_debug("Reset \"%s\" to \"%s\".\n", | ||
1546 | param->name, param->value); | ||
1547 | } | ||
1548 | } | ||
1549 | |||
1550 | return 0; | ||
1551 | } | ||
1552 | |||
1553 | int iscsi_decode_text_input( | ||
1554 | u8 phase, | ||
1555 | u8 sender, | ||
1556 | char *textbuf, | ||
1557 | u32 length, | ||
1558 | struct iscsi_param_list *param_list) | ||
1559 | { | ||
1560 | char *tmpbuf, *start = NULL, *end = NULL; | ||
1561 | |||
1562 | tmpbuf = kzalloc(length + 1, GFP_KERNEL); | ||
1563 | if (!tmpbuf) { | ||
1564 | pr_err("Unable to allocate memory for tmpbuf.\n"); | ||
1565 | return -1; | ||
1566 | } | ||
1567 | |||
1568 | memcpy(tmpbuf, textbuf, length); | ||
1569 | tmpbuf[length] = '\0'; | ||
1570 | start = tmpbuf; | ||
1571 | end = (start + length); | ||
1572 | |||
1573 | while (start < end) { | ||
1574 | char *key, *value; | ||
1575 | struct iscsi_param *param; | ||
1576 | |||
1577 | if (iscsi_extract_key_value(start, &key, &value) < 0) { | ||
1578 | kfree(tmpbuf); | ||
1579 | return -1; | ||
1580 | } | ||
1581 | |||
1582 | pr_debug("Got key: %s=%s\n", key, value); | ||
1583 | |||
1584 | if (phase & PHASE_SECURITY) { | ||
1585 | if (iscsi_check_for_auth_key(key) > 0) { | ||
1586 | char *tmpptr = key + strlen(key); | ||
1587 | *tmpptr = '='; | ||
1588 | kfree(tmpbuf); | ||
1589 | return 1; | ||
1590 | } | ||
1591 | } | ||
1592 | |||
1593 | param = iscsi_check_key(key, phase, sender, param_list); | ||
1594 | if (!param) { | ||
1595 | if (iscsi_add_notunderstood_response(key, | ||
1596 | value, param_list) < 0) { | ||
1597 | kfree(tmpbuf); | ||
1598 | return -1; | ||
1599 | } | ||
1600 | start += strlen(key) + strlen(value) + 2; | ||
1601 | continue; | ||
1602 | } | ||
1603 | if (iscsi_check_value(param, value) < 0) { | ||
1604 | kfree(tmpbuf); | ||
1605 | return -1; | ||
1606 | } | ||
1607 | |||
1608 | start += strlen(key) + strlen(value) + 2; | ||
1609 | |||
1610 | if (IS_PSTATE_PROPOSER(param)) { | ||
1611 | if (iscsi_check_proposer_state(param, value) < 0) { | ||
1612 | kfree(tmpbuf); | ||
1613 | return -1; | ||
1614 | } | ||
1615 | SET_PSTATE_RESPONSE_GOT(param); | ||
1616 | } else { | ||
1617 | if (iscsi_check_acceptor_state(param, value) < 0) { | ||
1618 | kfree(tmpbuf); | ||
1619 | return -1; | ||
1620 | } | ||
1621 | SET_PSTATE_ACCEPTOR(param); | ||
1622 | } | ||
1623 | } | ||
1624 | |||
1625 | kfree(tmpbuf); | ||
1626 | return 0; | ||
1627 | } | ||
1628 | |||
1629 | int iscsi_encode_text_output( | ||
1630 | u8 phase, | ||
1631 | u8 sender, | ||
1632 | char *textbuf, | ||
1633 | u32 *length, | ||
1634 | struct iscsi_param_list *param_list) | ||
1635 | { | ||
1636 | char *output_buf = NULL; | ||
1637 | struct iscsi_extra_response *er; | ||
1638 | struct iscsi_param *param; | ||
1639 | |||
1640 | output_buf = textbuf + *length; | ||
1641 | |||
1642 | if (iscsi_enforce_integrity_rules(phase, param_list) < 0) | ||
1643 | return -1; | ||
1644 | |||
1645 | list_for_each_entry(param, ¶m_list->param_list, p_list) { | ||
1646 | if (!(param->sender & sender)) | ||
1647 | continue; | ||
1648 | if (IS_PSTATE_ACCEPTOR(param) && | ||
1649 | !IS_PSTATE_RESPONSE_SENT(param) && | ||
1650 | !IS_PSTATE_REPLY_OPTIONAL(param) && | ||
1651 | (param->phase & phase)) { | ||
1652 | *length += sprintf(output_buf, "%s=%s", | ||
1653 | param->name, param->value); | ||
1654 | *length += 1; | ||
1655 | output_buf = textbuf + *length; | ||
1656 | SET_PSTATE_RESPONSE_SENT(param); | ||
1657 | pr_debug("Sending key: %s=%s\n", | ||
1658 | param->name, param->value); | ||
1659 | continue; | ||
1660 | } | ||
1661 | if (IS_PSTATE_NEGOTIATE(param) && | ||
1662 | !IS_PSTATE_ACCEPTOR(param) && | ||
1663 | !IS_PSTATE_PROPOSER(param) && | ||
1664 | (param->phase & phase)) { | ||
1665 | *length += sprintf(output_buf, "%s=%s", | ||
1666 | param->name, param->value); | ||
1667 | *length += 1; | ||
1668 | output_buf = textbuf + *length; | ||
1669 | SET_PSTATE_PROPOSER(param); | ||
1670 | iscsi_check_proposer_for_optional_reply(param); | ||
1671 | pr_debug("Sending key: %s=%s\n", | ||
1672 | param->name, param->value); | ||
1673 | } | ||
1674 | } | ||
1675 | |||
1676 | list_for_each_entry(er, ¶m_list->extra_response_list, er_list) { | ||
1677 | *length += sprintf(output_buf, "%s=%s", er->key, er->value); | ||
1678 | *length += 1; | ||
1679 | output_buf = textbuf + *length; | ||
1680 | pr_debug("Sending key: %s=%s\n", er->key, er->value); | ||
1681 | } | ||
1682 | iscsi_release_extra_responses(param_list); | ||
1683 | |||
1684 | return 0; | ||
1685 | } | ||
1686 | |||
1687 | int iscsi_check_negotiated_keys(struct iscsi_param_list *param_list) | ||
1688 | { | ||
1689 | int ret = 0; | ||
1690 | struct iscsi_param *param; | ||
1691 | |||
1692 | list_for_each_entry(param, ¶m_list->param_list, p_list) { | ||
1693 | if (IS_PSTATE_NEGOTIATE(param) && | ||
1694 | IS_PSTATE_PROPOSER(param) && | ||
1695 | !IS_PSTATE_RESPONSE_GOT(param) && | ||
1696 | !IS_PSTATE_REPLY_OPTIONAL(param) && | ||
1697 | !IS_PHASE_DECLARATIVE(param)) { | ||
1698 | pr_err("No response for proposed key \"%s\".\n", | ||
1699 | param->name); | ||
1700 | ret = -1; | ||
1701 | } | ||
1702 | } | ||
1703 | |||
1704 | return ret; | ||
1705 | } | ||
1706 | |||
1707 | int iscsi_change_param_value( | ||
1708 | char *keyvalue, | ||
1709 | struct iscsi_param_list *param_list, | ||
1710 | int check_key) | ||
1711 | { | ||
1712 | char *key = NULL, *value = NULL; | ||
1713 | struct iscsi_param *param; | ||
1714 | int sender = 0; | ||
1715 | |||
1716 | if (iscsi_extract_key_value(keyvalue, &key, &value) < 0) | ||
1717 | return -1; | ||
1718 | |||
1719 | if (!check_key) { | ||
1720 | param = __iscsi_check_key(keyvalue, sender, param_list); | ||
1721 | if (!param) | ||
1722 | return -1; | ||
1723 | } else { | ||
1724 | param = iscsi_check_key(keyvalue, 0, sender, param_list); | ||
1725 | if (!param) | ||
1726 | return -1; | ||
1727 | |||
1728 | param->set_param = 1; | ||
1729 | if (iscsi_check_value(param, value) < 0) { | ||
1730 | param->set_param = 0; | ||
1731 | return -1; | ||
1732 | } | ||
1733 | param->set_param = 0; | ||
1734 | } | ||
1735 | |||
1736 | if (iscsi_update_param_value(param, value) < 0) | ||
1737 | return -1; | ||
1738 | |||
1739 | return 0; | ||
1740 | } | ||
1741 | |||
1742 | void iscsi_set_connection_parameters( | ||
1743 | struct iscsi_conn_ops *ops, | ||
1744 | struct iscsi_param_list *param_list) | ||
1745 | { | ||
1746 | char *tmpptr; | ||
1747 | struct iscsi_param *param; | ||
1748 | |||
1749 | pr_debug("---------------------------------------------------" | ||
1750 | "---------------\n"); | ||
1751 | list_for_each_entry(param, ¶m_list->param_list, p_list) { | ||
1752 | if (!IS_PSTATE_ACCEPTOR(param) && !IS_PSTATE_PROPOSER(param)) | ||
1753 | continue; | ||
1754 | if (!strcmp(param->name, AUTHMETHOD)) { | ||
1755 | pr_debug("AuthMethod: %s\n", | ||
1756 | param->value); | ||
1757 | } else if (!strcmp(param->name, HEADERDIGEST)) { | ||
1758 | ops->HeaderDigest = !strcmp(param->value, CRC32C); | ||
1759 | pr_debug("HeaderDigest: %s\n", | ||
1760 | param->value); | ||
1761 | } else if (!strcmp(param->name, DATADIGEST)) { | ||
1762 | ops->DataDigest = !strcmp(param->value, CRC32C); | ||
1763 | pr_debug("DataDigest: %s\n", | ||
1764 | param->value); | ||
1765 | } else if (!strcmp(param->name, MAXRECVDATASEGMENTLENGTH)) { | ||
1766 | ops->MaxRecvDataSegmentLength = | ||
1767 | simple_strtoul(param->value, &tmpptr, 0); | ||
1768 | pr_debug("MaxRecvDataSegmentLength: %s\n", | ||
1769 | param->value); | ||
1770 | } else if (!strcmp(param->name, OFMARKER)) { | ||
1771 | ops->OFMarker = !strcmp(param->value, YES); | ||
1772 | pr_debug("OFMarker: %s\n", | ||
1773 | param->value); | ||
1774 | } else if (!strcmp(param->name, IFMARKER)) { | ||
1775 | ops->IFMarker = !strcmp(param->value, YES); | ||
1776 | pr_debug("IFMarker: %s\n", | ||
1777 | param->value); | ||
1778 | } else if (!strcmp(param->name, OFMARKINT)) { | ||
1779 | ops->OFMarkInt = | ||
1780 | simple_strtoul(param->value, &tmpptr, 0); | ||
1781 | pr_debug("OFMarkInt: %s\n", | ||
1782 | param->value); | ||
1783 | } else if (!strcmp(param->name, IFMARKINT)) { | ||
1784 | ops->IFMarkInt = | ||
1785 | simple_strtoul(param->value, &tmpptr, 0); | ||
1786 | pr_debug("IFMarkInt: %s\n", | ||
1787 | param->value); | ||
1788 | } | ||
1789 | } | ||
1790 | pr_debug("----------------------------------------------------" | ||
1791 | "--------------\n"); | ||
1792 | } | ||
1793 | |||
1794 | void iscsi_set_session_parameters( | ||
1795 | struct iscsi_sess_ops *ops, | ||
1796 | struct iscsi_param_list *param_list, | ||
1797 | int leading) | ||
1798 | { | ||
1799 | char *tmpptr; | ||
1800 | struct iscsi_param *param; | ||
1801 | |||
1802 | pr_debug("----------------------------------------------------" | ||
1803 | "--------------\n"); | ||
1804 | list_for_each_entry(param, ¶m_list->param_list, p_list) { | ||
1805 | if (!IS_PSTATE_ACCEPTOR(param) && !IS_PSTATE_PROPOSER(param)) | ||
1806 | continue; | ||
1807 | if (!strcmp(param->name, INITIATORNAME)) { | ||
1808 | if (!param->value) | ||
1809 | continue; | ||
1810 | if (leading) | ||
1811 | snprintf(ops->InitiatorName, | ||
1812 | sizeof(ops->InitiatorName), | ||
1813 | "%s", param->value); | ||
1814 | pr_debug("InitiatorName: %s\n", | ||
1815 | param->value); | ||
1816 | } else if (!strcmp(param->name, INITIATORALIAS)) { | ||
1817 | if (!param->value) | ||
1818 | continue; | ||
1819 | snprintf(ops->InitiatorAlias, | ||
1820 | sizeof(ops->InitiatorAlias), | ||
1821 | "%s", param->value); | ||
1822 | pr_debug("InitiatorAlias: %s\n", | ||
1823 | param->value); | ||
1824 | } else if (!strcmp(param->name, TARGETNAME)) { | ||
1825 | if (!param->value) | ||
1826 | continue; | ||
1827 | if (leading) | ||
1828 | snprintf(ops->TargetName, | ||
1829 | sizeof(ops->TargetName), | ||
1830 | "%s", param->value); | ||
1831 | pr_debug("TargetName: %s\n", | ||
1832 | param->value); | ||
1833 | } else if (!strcmp(param->name, TARGETALIAS)) { | ||
1834 | if (!param->value) | ||
1835 | continue; | ||
1836 | snprintf(ops->TargetAlias, sizeof(ops->TargetAlias), | ||
1837 | "%s", param->value); | ||
1838 | pr_debug("TargetAlias: %s\n", | ||
1839 | param->value); | ||
1840 | } else if (!strcmp(param->name, TARGETPORTALGROUPTAG)) { | ||
1841 | ops->TargetPortalGroupTag = | ||
1842 | simple_strtoul(param->value, &tmpptr, 0); | ||
1843 | pr_debug("TargetPortalGroupTag: %s\n", | ||
1844 | param->value); | ||
1845 | } else if (!strcmp(param->name, MAXCONNECTIONS)) { | ||
1846 | ops->MaxConnections = | ||
1847 | simple_strtoul(param->value, &tmpptr, 0); | ||
1848 | pr_debug("MaxConnections: %s\n", | ||
1849 | param->value); | ||
1850 | } else if (!strcmp(param->name, INITIALR2T)) { | ||
1851 | ops->InitialR2T = !strcmp(param->value, YES); | ||
1852 | pr_debug("InitialR2T: %s\n", | ||
1853 | param->value); | ||
1854 | } else if (!strcmp(param->name, IMMEDIATEDATA)) { | ||
1855 | ops->ImmediateData = !strcmp(param->value, YES); | ||
1856 | pr_debug("ImmediateData: %s\n", | ||
1857 | param->value); | ||
1858 | } else if (!strcmp(param->name, MAXBURSTLENGTH)) { | ||
1859 | ops->MaxBurstLength = | ||
1860 | simple_strtoul(param->value, &tmpptr, 0); | ||
1861 | pr_debug("MaxBurstLength: %s\n", | ||
1862 | param->value); | ||
1863 | } else if (!strcmp(param->name, FIRSTBURSTLENGTH)) { | ||
1864 | ops->FirstBurstLength = | ||
1865 | simple_strtoul(param->value, &tmpptr, 0); | ||
1866 | pr_debug("FirstBurstLength: %s\n", | ||
1867 | param->value); | ||
1868 | } else if (!strcmp(param->name, DEFAULTTIME2WAIT)) { | ||
1869 | ops->DefaultTime2Wait = | ||
1870 | simple_strtoul(param->value, &tmpptr, 0); | ||
1871 | pr_debug("DefaultTime2Wait: %s\n", | ||
1872 | param->value); | ||
1873 | } else if (!strcmp(param->name, DEFAULTTIME2RETAIN)) { | ||
1874 | ops->DefaultTime2Retain = | ||
1875 | simple_strtoul(param->value, &tmpptr, 0); | ||
1876 | pr_debug("DefaultTime2Retain: %s\n", | ||
1877 | param->value); | ||
1878 | } else if (!strcmp(param->name, MAXOUTSTANDINGR2T)) { | ||
1879 | ops->MaxOutstandingR2T = | ||
1880 | simple_strtoul(param->value, &tmpptr, 0); | ||
1881 | pr_debug("MaxOutstandingR2T: %s\n", | ||
1882 | param->value); | ||
1883 | } else if (!strcmp(param->name, DATAPDUINORDER)) { | ||
1884 | ops->DataPDUInOrder = !strcmp(param->value, YES); | ||
1885 | pr_debug("DataPDUInOrder: %s\n", | ||
1886 | param->value); | ||
1887 | } else if (!strcmp(param->name, DATASEQUENCEINORDER)) { | ||
1888 | ops->DataSequenceInOrder = !strcmp(param->value, YES); | ||
1889 | pr_debug("DataSequenceInOrder: %s\n", | ||
1890 | param->value); | ||
1891 | } else if (!strcmp(param->name, ERRORRECOVERYLEVEL)) { | ||
1892 | ops->ErrorRecoveryLevel = | ||
1893 | simple_strtoul(param->value, &tmpptr, 0); | ||
1894 | pr_debug("ErrorRecoveryLevel: %s\n", | ||
1895 | param->value); | ||
1896 | } else if (!strcmp(param->name, SESSIONTYPE)) { | ||
1897 | ops->SessionType = !strcmp(param->value, DISCOVERY); | ||
1898 | pr_debug("SessionType: %s\n", | ||
1899 | param->value); | ||
1900 | } | ||
1901 | } | ||
1902 | pr_debug("----------------------------------------------------" | ||
1903 | "--------------\n"); | ||
1904 | |||
1905 | } | ||
diff --git a/drivers/target/iscsi/iscsi_target_parameters.h b/drivers/target/iscsi/iscsi_target_parameters.h new file mode 100644 index 000000000000..6a37fd6f1285 --- /dev/null +++ b/drivers/target/iscsi/iscsi_target_parameters.h | |||
@@ -0,0 +1,269 @@ | |||
1 | #ifndef ISCSI_PARAMETERS_H | ||
2 | #define ISCSI_PARAMETERS_H | ||
3 | |||
4 | struct iscsi_extra_response { | ||
5 | char key[64]; | ||
6 | char value[32]; | ||
7 | struct list_head er_list; | ||
8 | } ____cacheline_aligned; | ||
9 | |||
10 | struct iscsi_param { | ||
11 | char *name; | ||
12 | char *value; | ||
13 | u8 set_param; | ||
14 | u8 phase; | ||
15 | u8 scope; | ||
16 | u8 sender; | ||
17 | u8 type; | ||
18 | u8 use; | ||
19 | u16 type_range; | ||
20 | u32 state; | ||
21 | struct list_head p_list; | ||
22 | } ____cacheline_aligned; | ||
23 | |||
24 | extern int iscsi_login_rx_data(struct iscsi_conn *, char *, int); | ||
25 | extern int iscsi_login_tx_data(struct iscsi_conn *, char *, char *, int); | ||
26 | extern void iscsi_dump_conn_ops(struct iscsi_conn_ops *); | ||
27 | extern void iscsi_dump_sess_ops(struct iscsi_sess_ops *); | ||
28 | extern void iscsi_print_params(struct iscsi_param_list *); | ||
29 | extern int iscsi_create_default_params(struct iscsi_param_list **); | ||
30 | extern int iscsi_set_keys_to_negotiate(int, struct iscsi_param_list *); | ||
31 | extern int iscsi_set_keys_irrelevant_for_discovery(struct iscsi_param_list *); | ||
32 | extern int iscsi_copy_param_list(struct iscsi_param_list **, | ||
33 | struct iscsi_param_list *, int); | ||
34 | extern int iscsi_change_param_value(char *, struct iscsi_param_list *, int); | ||
35 | extern void iscsi_release_param_list(struct iscsi_param_list *); | ||
36 | extern struct iscsi_param *iscsi_find_param_from_key(char *, struct iscsi_param_list *); | ||
37 | extern int iscsi_extract_key_value(char *, char **, char **); | ||
38 | extern int iscsi_update_param_value(struct iscsi_param *, char *); | ||
39 | extern int iscsi_decode_text_input(u8, u8, char *, u32, struct iscsi_param_list *); | ||
40 | extern int iscsi_encode_text_output(u8, u8, char *, u32 *, | ||
41 | struct iscsi_param_list *); | ||
42 | extern int iscsi_check_negotiated_keys(struct iscsi_param_list *); | ||
43 | extern void iscsi_set_connection_parameters(struct iscsi_conn_ops *, | ||
44 | struct iscsi_param_list *); | ||
45 | extern void iscsi_set_session_parameters(struct iscsi_sess_ops *, | ||
46 | struct iscsi_param_list *, int); | ||
47 | |||
48 | #define YES "Yes" | ||
49 | #define NO "No" | ||
50 | #define ALL "All" | ||
51 | #define IRRELEVANT "Irrelevant" | ||
52 | #define NONE "None" | ||
53 | #define NOTUNDERSTOOD "NotUnderstood" | ||
54 | #define REJECT "Reject" | ||
55 | |||
56 | /* | ||
57 | * The Parameter Names. | ||
58 | */ | ||
59 | #define AUTHMETHOD "AuthMethod" | ||
60 | #define HEADERDIGEST "HeaderDigest" | ||
61 | #define DATADIGEST "DataDigest" | ||
62 | #define MAXCONNECTIONS "MaxConnections" | ||
63 | #define SENDTARGETS "SendTargets" | ||
64 | #define TARGETNAME "TargetName" | ||
65 | #define INITIATORNAME "InitiatorName" | ||
66 | #define TARGETALIAS "TargetAlias" | ||
67 | #define INITIATORALIAS "InitiatorAlias" | ||
68 | #define TARGETADDRESS "TargetAddress" | ||
69 | #define TARGETPORTALGROUPTAG "TargetPortalGroupTag" | ||
70 | #define INITIALR2T "InitialR2T" | ||
71 | #define IMMEDIATEDATA "ImmediateData" | ||
72 | #define MAXRECVDATASEGMENTLENGTH "MaxRecvDataSegmentLength" | ||
73 | #define MAXBURSTLENGTH "MaxBurstLength" | ||
74 | #define FIRSTBURSTLENGTH "FirstBurstLength" | ||
75 | #define DEFAULTTIME2WAIT "DefaultTime2Wait" | ||
76 | #define DEFAULTTIME2RETAIN "DefaultTime2Retain" | ||
77 | #define MAXOUTSTANDINGR2T "MaxOutstandingR2T" | ||
78 | #define DATAPDUINORDER "DataPDUInOrder" | ||
79 | #define DATASEQUENCEINORDER "DataSequenceInOrder" | ||
80 | #define ERRORRECOVERYLEVEL "ErrorRecoveryLevel" | ||
81 | #define SESSIONTYPE "SessionType" | ||
82 | #define IFMARKER "IFMarker" | ||
83 | #define OFMARKER "OFMarker" | ||
84 | #define IFMARKINT "IFMarkInt" | ||
85 | #define OFMARKINT "OFMarkInt" | ||
86 | #define X_EXTENSIONKEY "X-com.sbei.version" | ||
87 | #define X_EXTENSIONKEY_CISCO_NEW "X-com.cisco.protocol" | ||
88 | #define X_EXTENSIONKEY_CISCO_OLD "X-com.cisco.iscsi.draft" | ||
89 | |||
90 | /* | ||
91 | * For AuthMethod. | ||
92 | */ | ||
93 | #define KRB5 "KRB5" | ||
94 | #define SPKM1 "SPKM1" | ||
95 | #define SPKM2 "SPKM2" | ||
96 | #define SRP "SRP" | ||
97 | #define CHAP "CHAP" | ||
98 | |||
99 | /* | ||
100 | * Initial values for Parameter Negotiation. | ||
101 | */ | ||
102 | #define INITIAL_AUTHMETHOD CHAP | ||
103 | #define INITIAL_HEADERDIGEST "CRC32C,None" | ||
104 | #define INITIAL_DATADIGEST "CRC32C,None" | ||
105 | #define INITIAL_MAXCONNECTIONS "1" | ||
106 | #define INITIAL_SENDTARGETS ALL | ||
107 | #define INITIAL_TARGETNAME "LIO.Target" | ||
108 | #define INITIAL_INITIATORNAME "LIO.Initiator" | ||
109 | #define INITIAL_TARGETALIAS "LIO Target" | ||
110 | #define INITIAL_INITIATORALIAS "LIO Initiator" | ||
111 | #define INITIAL_TARGETADDRESS "0.0.0.0:0000,0" | ||
112 | #define INITIAL_TARGETPORTALGROUPTAG "1" | ||
113 | #define INITIAL_INITIALR2T YES | ||
114 | #define INITIAL_IMMEDIATEDATA YES | ||
115 | #define INITIAL_MAXRECVDATASEGMENTLENGTH "8192" | ||
116 | #define INITIAL_MAXBURSTLENGTH "262144" | ||
117 | #define INITIAL_FIRSTBURSTLENGTH "65536" | ||
118 | #define INITIAL_DEFAULTTIME2WAIT "2" | ||
119 | #define INITIAL_DEFAULTTIME2RETAIN "20" | ||
120 | #define INITIAL_MAXOUTSTANDINGR2T "1" | ||
121 | #define INITIAL_DATAPDUINORDER YES | ||
122 | #define INITIAL_DATASEQUENCEINORDER YES | ||
123 | #define INITIAL_ERRORRECOVERYLEVEL "0" | ||
124 | #define INITIAL_SESSIONTYPE NORMAL | ||
125 | #define INITIAL_IFMARKER NO | ||
126 | #define INITIAL_OFMARKER NO | ||
127 | #define INITIAL_IFMARKINT "2048~65535" | ||
128 | #define INITIAL_OFMARKINT "2048~65535" | ||
129 | |||
130 | /* | ||
131 | * For [Header,Data]Digests. | ||
132 | */ | ||
133 | #define CRC32C "CRC32C" | ||
134 | |||
135 | /* | ||
136 | * For SessionType. | ||
137 | */ | ||
138 | #define DISCOVERY "Discovery" | ||
139 | #define NORMAL "Normal" | ||
140 | |||
141 | /* | ||
142 | * struct iscsi_param->use | ||
143 | */ | ||
144 | #define USE_LEADING_ONLY 0x01 | ||
145 | #define USE_INITIAL_ONLY 0x02 | ||
146 | #define USE_ALL 0x04 | ||
147 | |||
148 | #define IS_USE_LEADING_ONLY(p) ((p)->use & USE_LEADING_ONLY) | ||
149 | #define IS_USE_INITIAL_ONLY(p) ((p)->use & USE_INITIAL_ONLY) | ||
150 | #define IS_USE_ALL(p) ((p)->use & USE_ALL) | ||
151 | |||
152 | #define SET_USE_INITIAL_ONLY(p) ((p)->use |= USE_INITIAL_ONLY) | ||
153 | |||
154 | /* | ||
155 | * struct iscsi_param->sender | ||
156 | */ | ||
157 | #define SENDER_INITIATOR 0x01 | ||
158 | #define SENDER_TARGET 0x02 | ||
159 | #define SENDER_BOTH 0x03 | ||
160 | /* Used in iscsi_check_key() */ | ||
161 | #define SENDER_RECEIVER 0x04 | ||
162 | |||
163 | #define IS_SENDER_INITIATOR(p) ((p)->sender & SENDER_INITIATOR) | ||
164 | #define IS_SENDER_TARGET(p) ((p)->sender & SENDER_TARGET) | ||
165 | #define IS_SENDER_BOTH(p) ((p)->sender & SENDER_BOTH) | ||
166 | |||
167 | /* | ||
168 | * struct iscsi_param->scope | ||
169 | */ | ||
170 | #define SCOPE_CONNECTION_ONLY 0x01 | ||
171 | #define SCOPE_SESSION_WIDE 0x02 | ||
172 | |||
173 | #define IS_SCOPE_CONNECTION_ONLY(p) ((p)->scope & SCOPE_CONNECTION_ONLY) | ||
174 | #define IS_SCOPE_SESSION_WIDE(p) ((p)->scope & SCOPE_SESSION_WIDE) | ||
175 | |||
176 | /* | ||
177 | * struct iscsi_param->phase | ||
178 | */ | ||
179 | #define PHASE_SECURITY 0x01 | ||
180 | #define PHASE_OPERATIONAL 0x02 | ||
181 | #define PHASE_DECLARATIVE 0x04 | ||
182 | #define PHASE_FFP0 0x08 | ||
183 | |||
184 | #define IS_PHASE_SECURITY(p) ((p)->phase & PHASE_SECURITY) | ||
185 | #define IS_PHASE_OPERATIONAL(p) ((p)->phase & PHASE_OPERATIONAL) | ||
186 | #define IS_PHASE_DECLARATIVE(p) ((p)->phase & PHASE_DECLARATIVE) | ||
187 | #define IS_PHASE_FFP0(p) ((p)->phase & PHASE_FFP0) | ||
188 | |||
189 | /* | ||
190 | * struct iscsi_param->type | ||
191 | */ | ||
192 | #define TYPE_BOOL_AND 0x01 | ||
193 | #define TYPE_BOOL_OR 0x02 | ||
194 | #define TYPE_NUMBER 0x04 | ||
195 | #define TYPE_NUMBER_RANGE 0x08 | ||
196 | #define TYPE_STRING 0x10 | ||
197 | #define TYPE_VALUE_LIST 0x20 | ||
198 | |||
199 | #define IS_TYPE_BOOL_AND(p) ((p)->type & TYPE_BOOL_AND) | ||
200 | #define IS_TYPE_BOOL_OR(p) ((p)->type & TYPE_BOOL_OR) | ||
201 | #define IS_TYPE_NUMBER(p) ((p)->type & TYPE_NUMBER) | ||
202 | #define IS_TYPE_NUMBER_RANGE(p) ((p)->type & TYPE_NUMBER_RANGE) | ||
203 | #define IS_TYPE_STRING(p) ((p)->type & TYPE_STRING) | ||
204 | #define IS_TYPE_VALUE_LIST(p) ((p)->type & TYPE_VALUE_LIST) | ||
205 | |||
206 | /* | ||
207 | * struct iscsi_param->type_range | ||
208 | */ | ||
209 | #define TYPERANGE_BOOL_AND 0x0001 | ||
210 | #define TYPERANGE_BOOL_OR 0x0002 | ||
211 | #define TYPERANGE_0_TO_2 0x0004 | ||
212 | #define TYPERANGE_0_TO_3600 0x0008 | ||
213 | #define TYPERANGE_0_TO_32767 0x0010 | ||
214 | #define TYPERANGE_0_TO_65535 0x0020 | ||
215 | #define TYPERANGE_1_TO_65535 0x0040 | ||
216 | #define TYPERANGE_2_TO_3600 0x0080 | ||
217 | #define TYPERANGE_512_TO_16777215 0x0100 | ||
218 | #define TYPERANGE_AUTH 0x0200 | ||
219 | #define TYPERANGE_DIGEST 0x0400 | ||
220 | #define TYPERANGE_ISCSINAME 0x0800 | ||
221 | #define TYPERANGE_MARKINT 0x1000 | ||
222 | #define TYPERANGE_SESSIONTYPE 0x2000 | ||
223 | #define TYPERANGE_TARGETADDRESS 0x4000 | ||
224 | #define TYPERANGE_UTF8 0x8000 | ||
225 | |||
226 | #define IS_TYPERANGE_0_TO_2(p) ((p)->type_range & TYPERANGE_0_TO_2) | ||
227 | #define IS_TYPERANGE_0_TO_3600(p) ((p)->type_range & TYPERANGE_0_TO_3600) | ||
228 | #define IS_TYPERANGE_0_TO_32767(p) ((p)->type_range & TYPERANGE_0_TO_32767) | ||
229 | #define IS_TYPERANGE_0_TO_65535(p) ((p)->type_range & TYPERANGE_0_TO_65535) | ||
230 | #define IS_TYPERANGE_1_TO_65535(p) ((p)->type_range & TYPERANGE_1_TO_65535) | ||
231 | #define IS_TYPERANGE_2_TO_3600(p) ((p)->type_range & TYPERANGE_2_TO_3600) | ||
232 | #define IS_TYPERANGE_512_TO_16777215(p) ((p)->type_range & \ | ||
233 | TYPERANGE_512_TO_16777215) | ||
234 | #define IS_TYPERANGE_AUTH_PARAM(p) ((p)->type_range & TYPERANGE_AUTH) | ||
235 | #define IS_TYPERANGE_DIGEST_PARAM(p) ((p)->type_range & TYPERANGE_DIGEST) | ||
236 | #define IS_TYPERANGE_SESSIONTYPE(p) ((p)->type_range & \ | ||
237 | TYPERANGE_SESSIONTYPE) | ||
238 | |||
239 | /* | ||
240 | * struct iscsi_param->state | ||
241 | */ | ||
242 | #define PSTATE_ACCEPTOR 0x01 | ||
243 | #define PSTATE_NEGOTIATE 0x02 | ||
244 | #define PSTATE_PROPOSER 0x04 | ||
245 | #define PSTATE_IRRELEVANT 0x08 | ||
246 | #define PSTATE_REJECT 0x10 | ||
247 | #define PSTATE_REPLY_OPTIONAL 0x20 | ||
248 | #define PSTATE_RESPONSE_GOT 0x40 | ||
249 | #define PSTATE_RESPONSE_SENT 0x80 | ||
250 | |||
251 | #define IS_PSTATE_ACCEPTOR(p) ((p)->state & PSTATE_ACCEPTOR) | ||
252 | #define IS_PSTATE_NEGOTIATE(p) ((p)->state & PSTATE_NEGOTIATE) | ||
253 | #define IS_PSTATE_PROPOSER(p) ((p)->state & PSTATE_PROPOSER) | ||
254 | #define IS_PSTATE_IRRELEVANT(p) ((p)->state & PSTATE_IRRELEVANT) | ||
255 | #define IS_PSTATE_REJECT(p) ((p)->state & PSTATE_REJECT) | ||
256 | #define IS_PSTATE_REPLY_OPTIONAL(p) ((p)->state & PSTATE_REPLY_OPTIONAL) | ||
257 | #define IS_PSTATE_RESPONSE_GOT(p) ((p)->state & PSTATE_RESPONSE_GOT) | ||
258 | #define IS_PSTATE_RESPONSE_SENT(p) ((p)->state & PSTATE_RESPONSE_SENT) | ||
259 | |||
260 | #define SET_PSTATE_ACCEPTOR(p) ((p)->state |= PSTATE_ACCEPTOR) | ||
261 | #define SET_PSTATE_NEGOTIATE(p) ((p)->state |= PSTATE_NEGOTIATE) | ||
262 | #define SET_PSTATE_PROPOSER(p) ((p)->state |= PSTATE_PROPOSER) | ||
263 | #define SET_PSTATE_IRRELEVANT(p) ((p)->state |= PSTATE_IRRELEVANT) | ||
264 | #define SET_PSTATE_REJECT(p) ((p)->state |= PSTATE_REJECT) | ||
265 | #define SET_PSTATE_REPLY_OPTIONAL(p) ((p)->state |= PSTATE_REPLY_OPTIONAL) | ||
266 | #define SET_PSTATE_RESPONSE_GOT(p) ((p)->state |= PSTATE_RESPONSE_GOT) | ||
267 | #define SET_PSTATE_RESPONSE_SENT(p) ((p)->state |= PSTATE_RESPONSE_SENT) | ||
268 | |||
269 | #endif /* ISCSI_PARAMETERS_H */ | ||
diff --git a/drivers/target/iscsi/iscsi_target_seq_pdu_list.c b/drivers/target/iscsi/iscsi_target_seq_pdu_list.c new file mode 100644 index 000000000000..fc694082bfc0 --- /dev/null +++ b/drivers/target/iscsi/iscsi_target_seq_pdu_list.c | |||
@@ -0,0 +1,664 @@ | |||
1 | /******************************************************************************* | ||
2 | * This file contains main functions related to iSCSI DataSequenceInOrder=No | ||
3 | * and DataPDUInOrder=No. | ||
4 | * | ||
5 | \u00a9 Copyright 2007-2011 RisingTide Systems LLC. | ||
6 | * | ||
7 | * Licensed to the Linux Foundation under the General Public License (GPL) version 2. | ||
8 | * | ||
9 | * Author: Nicholas A. Bellinger <nab@linux-iscsi.org> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or | ||
14 | * (at your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | * GNU General Public License for more details. | ||
20 | ******************************************************************************/ | ||
21 | |||
22 | #include <linux/slab.h> | ||
23 | #include <linux/random.h> | ||
24 | |||
25 | #include "iscsi_target_core.h" | ||
26 | #include "iscsi_target_util.h" | ||
27 | #include "iscsi_target_seq_pdu_list.h" | ||
28 | |||
29 | #define OFFLOAD_BUF_SIZE 32768 | ||
30 | |||
31 | void iscsit_dump_seq_list(struct iscsi_cmd *cmd) | ||
32 | { | ||
33 | int i; | ||
34 | struct iscsi_seq *seq; | ||
35 | |||
36 | pr_debug("Dumping Sequence List for ITT: 0x%08x:\n", | ||
37 | cmd->init_task_tag); | ||
38 | |||
39 | for (i = 0; i < cmd->seq_count; i++) { | ||
40 | seq = &cmd->seq_list[i]; | ||
41 | pr_debug("i: %d, pdu_start: %d, pdu_count: %d," | ||
42 | " offset: %d, xfer_len: %d, seq_send_order: %d," | ||
43 | " seq_no: %d\n", i, seq->pdu_start, seq->pdu_count, | ||
44 | seq->offset, seq->xfer_len, seq->seq_send_order, | ||
45 | seq->seq_no); | ||
46 | } | ||
47 | } | ||
48 | |||
49 | void iscsit_dump_pdu_list(struct iscsi_cmd *cmd) | ||
50 | { | ||
51 | int i; | ||
52 | struct iscsi_pdu *pdu; | ||
53 | |||
54 | pr_debug("Dumping PDU List for ITT: 0x%08x:\n", | ||
55 | cmd->init_task_tag); | ||
56 | |||
57 | for (i = 0; i < cmd->pdu_count; i++) { | ||
58 | pdu = &cmd->pdu_list[i]; | ||
59 | pr_debug("i: %d, offset: %d, length: %d," | ||
60 | " pdu_send_order: %d, seq_no: %d\n", i, pdu->offset, | ||
61 | pdu->length, pdu->pdu_send_order, pdu->seq_no); | ||
62 | } | ||
63 | } | ||
64 | |||
65 | static void iscsit_ordered_seq_lists( | ||
66 | struct iscsi_cmd *cmd, | ||
67 | u8 type) | ||
68 | { | ||
69 | u32 i, seq_count = 0; | ||
70 | |||
71 | for (i = 0; i < cmd->seq_count; i++) { | ||
72 | if (cmd->seq_list[i].type != SEQTYPE_NORMAL) | ||
73 | continue; | ||
74 | cmd->seq_list[i].seq_send_order = seq_count++; | ||
75 | } | ||
76 | } | ||
77 | |||
78 | static void iscsit_ordered_pdu_lists( | ||
79 | struct iscsi_cmd *cmd, | ||
80 | u8 type) | ||
81 | { | ||
82 | u32 i, pdu_send_order = 0, seq_no = 0; | ||
83 | |||
84 | for (i = 0; i < cmd->pdu_count; i++) { | ||
85 | redo: | ||
86 | if (cmd->pdu_list[i].seq_no == seq_no) { | ||
87 | cmd->pdu_list[i].pdu_send_order = pdu_send_order++; | ||
88 | continue; | ||
89 | } | ||
90 | seq_no++; | ||
91 | pdu_send_order = 0; | ||
92 | goto redo; | ||
93 | } | ||
94 | } | ||
95 | |||
96 | /* | ||
97 | * Generate count random values into array. | ||
98 | * Use 0x80000000 to mark generates valued in array[]. | ||
99 | */ | ||
100 | static void iscsit_create_random_array(u32 *array, u32 count) | ||
101 | { | ||
102 | int i, j, k; | ||
103 | |||
104 | if (count == 1) { | ||
105 | array[0] = 0; | ||
106 | return; | ||
107 | } | ||
108 | |||
109 | for (i = 0; i < count; i++) { | ||
110 | redo: | ||
111 | get_random_bytes(&j, sizeof(u32)); | ||
112 | j = (1 + (int) (9999 + 1) - j) % count; | ||
113 | for (k = 0; k < i + 1; k++) { | ||
114 | j |= 0x80000000; | ||
115 | if ((array[k] & 0x80000000) && (array[k] == j)) | ||
116 | goto redo; | ||
117 | } | ||
118 | array[i] = j; | ||
119 | } | ||
120 | |||
121 | for (i = 0; i < count; i++) | ||
122 | array[i] &= ~0x80000000; | ||
123 | } | ||
124 | |||
125 | static int iscsit_randomize_pdu_lists( | ||
126 | struct iscsi_cmd *cmd, | ||
127 | u8 type) | ||
128 | { | ||
129 | int i = 0; | ||
130 | u32 *array, pdu_count, seq_count = 0, seq_no = 0, seq_offset = 0; | ||
131 | |||
132 | for (pdu_count = 0; pdu_count < cmd->pdu_count; pdu_count++) { | ||
133 | redo: | ||
134 | if (cmd->pdu_list[pdu_count].seq_no == seq_no) { | ||
135 | seq_count++; | ||
136 | continue; | ||
137 | } | ||
138 | array = kzalloc(seq_count * sizeof(u32), GFP_KERNEL); | ||
139 | if (!array) { | ||
140 | pr_err("Unable to allocate memory" | ||
141 | " for random array.\n"); | ||
142 | return -1; | ||
143 | } | ||
144 | iscsit_create_random_array(array, seq_count); | ||
145 | |||
146 | for (i = 0; i < seq_count; i++) | ||
147 | cmd->pdu_list[seq_offset+i].pdu_send_order = array[i]; | ||
148 | |||
149 | kfree(array); | ||
150 | |||
151 | seq_offset += seq_count; | ||
152 | seq_count = 0; | ||
153 | seq_no++; | ||
154 | goto redo; | ||
155 | } | ||
156 | |||
157 | if (seq_count) { | ||
158 | array = kzalloc(seq_count * sizeof(u32), GFP_KERNEL); | ||
159 | if (!array) { | ||
160 | pr_err("Unable to allocate memory for" | ||
161 | " random array.\n"); | ||
162 | return -1; | ||
163 | } | ||
164 | iscsit_create_random_array(array, seq_count); | ||
165 | |||
166 | for (i = 0; i < seq_count; i++) | ||
167 | cmd->pdu_list[seq_offset+i].pdu_send_order = array[i]; | ||
168 | |||
169 | kfree(array); | ||
170 | } | ||
171 | |||
172 | return 0; | ||
173 | } | ||
174 | |||
175 | static int iscsit_randomize_seq_lists( | ||
176 | struct iscsi_cmd *cmd, | ||
177 | u8 type) | ||
178 | { | ||
179 | int i, j = 0; | ||
180 | u32 *array, seq_count = cmd->seq_count; | ||
181 | |||
182 | if ((type == PDULIST_IMMEDIATE) || (type == PDULIST_UNSOLICITED)) | ||
183 | seq_count--; | ||
184 | else if (type == PDULIST_IMMEDIATE_AND_UNSOLICITED) | ||
185 | seq_count -= 2; | ||
186 | |||
187 | if (!seq_count) | ||
188 | return 0; | ||
189 | |||
190 | array = kzalloc(seq_count * sizeof(u32), GFP_KERNEL); | ||
191 | if (!array) { | ||
192 | pr_err("Unable to allocate memory for random array.\n"); | ||
193 | return -1; | ||
194 | } | ||
195 | iscsit_create_random_array(array, seq_count); | ||
196 | |||
197 | for (i = 0; i < cmd->seq_count; i++) { | ||
198 | if (cmd->seq_list[i].type != SEQTYPE_NORMAL) | ||
199 | continue; | ||
200 | cmd->seq_list[i].seq_send_order = array[j++]; | ||
201 | } | ||
202 | |||
203 | kfree(array); | ||
204 | return 0; | ||
205 | } | ||
206 | |||
207 | static void iscsit_determine_counts_for_list( | ||
208 | struct iscsi_cmd *cmd, | ||
209 | struct iscsi_build_list *bl, | ||
210 | u32 *seq_count, | ||
211 | u32 *pdu_count) | ||
212 | { | ||
213 | int check_immediate = 0; | ||
214 | u32 burstlength = 0, offset = 0; | ||
215 | u32 unsolicited_data_length = 0; | ||
216 | struct iscsi_conn *conn = cmd->conn; | ||
217 | |||
218 | if ((bl->type == PDULIST_IMMEDIATE) || | ||
219 | (bl->type == PDULIST_IMMEDIATE_AND_UNSOLICITED)) | ||
220 | check_immediate = 1; | ||
221 | |||
222 | if ((bl->type == PDULIST_UNSOLICITED) || | ||
223 | (bl->type == PDULIST_IMMEDIATE_AND_UNSOLICITED)) | ||
224 | unsolicited_data_length = (cmd->data_length > | ||
225 | conn->sess->sess_ops->FirstBurstLength) ? | ||
226 | conn->sess->sess_ops->FirstBurstLength : cmd->data_length; | ||
227 | |||
228 | while (offset < cmd->data_length) { | ||
229 | *pdu_count += 1; | ||
230 | |||
231 | if (check_immediate) { | ||
232 | check_immediate = 0; | ||
233 | offset += bl->immediate_data_length; | ||
234 | *seq_count += 1; | ||
235 | if (unsolicited_data_length) | ||
236 | unsolicited_data_length -= | ||
237 | bl->immediate_data_length; | ||
238 | continue; | ||
239 | } | ||
240 | if (unsolicited_data_length > 0) { | ||
241 | if ((offset + conn->conn_ops->MaxRecvDataSegmentLength) | ||
242 | >= cmd->data_length) { | ||
243 | unsolicited_data_length -= | ||
244 | (cmd->data_length - offset); | ||
245 | offset += (cmd->data_length - offset); | ||
246 | continue; | ||
247 | } | ||
248 | if ((offset + conn->conn_ops->MaxRecvDataSegmentLength) | ||
249 | >= conn->sess->sess_ops->FirstBurstLength) { | ||
250 | unsolicited_data_length -= | ||
251 | (conn->sess->sess_ops->FirstBurstLength - | ||
252 | offset); | ||
253 | offset += (conn->sess->sess_ops->FirstBurstLength - | ||
254 | offset); | ||
255 | burstlength = 0; | ||
256 | *seq_count += 1; | ||
257 | continue; | ||
258 | } | ||
259 | |||
260 | offset += conn->conn_ops->MaxRecvDataSegmentLength; | ||
261 | unsolicited_data_length -= | ||
262 | conn->conn_ops->MaxRecvDataSegmentLength; | ||
263 | continue; | ||
264 | } | ||
265 | if ((offset + conn->conn_ops->MaxRecvDataSegmentLength) >= | ||
266 | cmd->data_length) { | ||
267 | offset += (cmd->data_length - offset); | ||
268 | continue; | ||
269 | } | ||
270 | if ((burstlength + conn->conn_ops->MaxRecvDataSegmentLength) >= | ||
271 | conn->sess->sess_ops->MaxBurstLength) { | ||
272 | offset += (conn->sess->sess_ops->MaxBurstLength - | ||
273 | burstlength); | ||
274 | burstlength = 0; | ||
275 | *seq_count += 1; | ||
276 | continue; | ||
277 | } | ||
278 | |||
279 | burstlength += conn->conn_ops->MaxRecvDataSegmentLength; | ||
280 | offset += conn->conn_ops->MaxRecvDataSegmentLength; | ||
281 | } | ||
282 | } | ||
283 | |||
284 | |||
285 | /* | ||
286 | * Builds PDU and/or Sequence list, called while DataSequenceInOrder=No | ||
287 | * and DataPDUInOrder=No. | ||
288 | */ | ||
289 | static int iscsit_build_pdu_and_seq_list( | ||
290 | struct iscsi_cmd *cmd, | ||
291 | struct iscsi_build_list *bl) | ||
292 | { | ||
293 | int check_immediate = 0, datapduinorder, datasequenceinorder; | ||
294 | u32 burstlength = 0, offset = 0, i = 0; | ||
295 | u32 pdu_count = 0, seq_no = 0, unsolicited_data_length = 0; | ||
296 | struct iscsi_conn *conn = cmd->conn; | ||
297 | struct iscsi_pdu *pdu = cmd->pdu_list; | ||
298 | struct iscsi_seq *seq = cmd->seq_list; | ||
299 | |||
300 | datapduinorder = conn->sess->sess_ops->DataPDUInOrder; | ||
301 | datasequenceinorder = conn->sess->sess_ops->DataSequenceInOrder; | ||
302 | |||
303 | if ((bl->type == PDULIST_IMMEDIATE) || | ||
304 | (bl->type == PDULIST_IMMEDIATE_AND_UNSOLICITED)) | ||
305 | check_immediate = 1; | ||
306 | |||
307 | if ((bl->type == PDULIST_UNSOLICITED) || | ||
308 | (bl->type == PDULIST_IMMEDIATE_AND_UNSOLICITED)) | ||
309 | unsolicited_data_length = (cmd->data_length > | ||
310 | conn->sess->sess_ops->FirstBurstLength) ? | ||
311 | conn->sess->sess_ops->FirstBurstLength : cmd->data_length; | ||
312 | |||
313 | while (offset < cmd->data_length) { | ||
314 | pdu_count++; | ||
315 | if (!datapduinorder) { | ||
316 | pdu[i].offset = offset; | ||
317 | pdu[i].seq_no = seq_no; | ||
318 | } | ||
319 | if (!datasequenceinorder && (pdu_count == 1)) { | ||
320 | seq[seq_no].pdu_start = i; | ||
321 | seq[seq_no].seq_no = seq_no; | ||
322 | seq[seq_no].offset = offset; | ||
323 | seq[seq_no].orig_offset = offset; | ||
324 | } | ||
325 | |||
326 | if (check_immediate) { | ||
327 | check_immediate = 0; | ||
328 | if (!datapduinorder) { | ||
329 | pdu[i].type = PDUTYPE_IMMEDIATE; | ||
330 | pdu[i++].length = bl->immediate_data_length; | ||
331 | } | ||
332 | if (!datasequenceinorder) { | ||
333 | seq[seq_no].type = SEQTYPE_IMMEDIATE; | ||
334 | seq[seq_no].pdu_count = 1; | ||
335 | seq[seq_no].xfer_len = | ||
336 | bl->immediate_data_length; | ||
337 | } | ||
338 | offset += bl->immediate_data_length; | ||
339 | pdu_count = 0; | ||
340 | seq_no++; | ||
341 | if (unsolicited_data_length) | ||
342 | unsolicited_data_length -= | ||
343 | bl->immediate_data_length; | ||
344 | continue; | ||
345 | } | ||
346 | if (unsolicited_data_length > 0) { | ||
347 | if ((offset + | ||
348 | conn->conn_ops->MaxRecvDataSegmentLength) >= | ||
349 | cmd->data_length) { | ||
350 | if (!datapduinorder) { | ||
351 | pdu[i].type = PDUTYPE_UNSOLICITED; | ||
352 | pdu[i].length = | ||
353 | (cmd->data_length - offset); | ||
354 | } | ||
355 | if (!datasequenceinorder) { | ||
356 | seq[seq_no].type = SEQTYPE_UNSOLICITED; | ||
357 | seq[seq_no].pdu_count = pdu_count; | ||
358 | seq[seq_no].xfer_len = (burstlength + | ||
359 | (cmd->data_length - offset)); | ||
360 | } | ||
361 | unsolicited_data_length -= | ||
362 | (cmd->data_length - offset); | ||
363 | offset += (cmd->data_length - offset); | ||
364 | continue; | ||
365 | } | ||
366 | if ((offset + | ||
367 | conn->conn_ops->MaxRecvDataSegmentLength) >= | ||
368 | conn->sess->sess_ops->FirstBurstLength) { | ||
369 | if (!datapduinorder) { | ||
370 | pdu[i].type = PDUTYPE_UNSOLICITED; | ||
371 | pdu[i++].length = | ||
372 | (conn->sess->sess_ops->FirstBurstLength - | ||
373 | offset); | ||
374 | } | ||
375 | if (!datasequenceinorder) { | ||
376 | seq[seq_no].type = SEQTYPE_UNSOLICITED; | ||
377 | seq[seq_no].pdu_count = pdu_count; | ||
378 | seq[seq_no].xfer_len = (burstlength + | ||
379 | (conn->sess->sess_ops->FirstBurstLength - | ||
380 | offset)); | ||
381 | } | ||
382 | unsolicited_data_length -= | ||
383 | (conn->sess->sess_ops->FirstBurstLength - | ||
384 | offset); | ||
385 | offset += (conn->sess->sess_ops->FirstBurstLength - | ||
386 | offset); | ||
387 | burstlength = 0; | ||
388 | pdu_count = 0; | ||
389 | seq_no++; | ||
390 | continue; | ||
391 | } | ||
392 | |||
393 | if (!datapduinorder) { | ||
394 | pdu[i].type = PDUTYPE_UNSOLICITED; | ||
395 | pdu[i++].length = | ||
396 | conn->conn_ops->MaxRecvDataSegmentLength; | ||
397 | } | ||
398 | burstlength += conn->conn_ops->MaxRecvDataSegmentLength; | ||
399 | offset += conn->conn_ops->MaxRecvDataSegmentLength; | ||
400 | unsolicited_data_length -= | ||
401 | conn->conn_ops->MaxRecvDataSegmentLength; | ||
402 | continue; | ||
403 | } | ||
404 | if ((offset + conn->conn_ops->MaxRecvDataSegmentLength) >= | ||
405 | cmd->data_length) { | ||
406 | if (!datapduinorder) { | ||
407 | pdu[i].type = PDUTYPE_NORMAL; | ||
408 | pdu[i].length = (cmd->data_length - offset); | ||
409 | } | ||
410 | if (!datasequenceinorder) { | ||
411 | seq[seq_no].type = SEQTYPE_NORMAL; | ||
412 | seq[seq_no].pdu_count = pdu_count; | ||
413 | seq[seq_no].xfer_len = (burstlength + | ||
414 | (cmd->data_length - offset)); | ||
415 | } | ||
416 | offset += (cmd->data_length - offset); | ||
417 | continue; | ||
418 | } | ||
419 | if ((burstlength + conn->conn_ops->MaxRecvDataSegmentLength) >= | ||
420 | conn->sess->sess_ops->MaxBurstLength) { | ||
421 | if (!datapduinorder) { | ||
422 | pdu[i].type = PDUTYPE_NORMAL; | ||
423 | pdu[i++].length = | ||
424 | (conn->sess->sess_ops->MaxBurstLength - | ||
425 | burstlength); | ||
426 | } | ||
427 | if (!datasequenceinorder) { | ||
428 | seq[seq_no].type = SEQTYPE_NORMAL; | ||
429 | seq[seq_no].pdu_count = pdu_count; | ||
430 | seq[seq_no].xfer_len = (burstlength + | ||
431 | (conn->sess->sess_ops->MaxBurstLength - | ||
432 | burstlength)); | ||
433 | } | ||
434 | offset += (conn->sess->sess_ops->MaxBurstLength - | ||
435 | burstlength); | ||
436 | burstlength = 0; | ||
437 | pdu_count = 0; | ||
438 | seq_no++; | ||
439 | continue; | ||
440 | } | ||
441 | |||
442 | if (!datapduinorder) { | ||
443 | pdu[i].type = PDUTYPE_NORMAL; | ||
444 | pdu[i++].length = | ||
445 | conn->conn_ops->MaxRecvDataSegmentLength; | ||
446 | } | ||
447 | burstlength += conn->conn_ops->MaxRecvDataSegmentLength; | ||
448 | offset += conn->conn_ops->MaxRecvDataSegmentLength; | ||
449 | } | ||
450 | |||
451 | if (!datasequenceinorder) { | ||
452 | if (bl->data_direction & ISCSI_PDU_WRITE) { | ||
453 | if (bl->randomize & RANDOM_R2T_OFFSETS) { | ||
454 | if (iscsit_randomize_seq_lists(cmd, bl->type) | ||
455 | < 0) | ||
456 | return -1; | ||
457 | } else | ||
458 | iscsit_ordered_seq_lists(cmd, bl->type); | ||
459 | } else if (bl->data_direction & ISCSI_PDU_READ) { | ||
460 | if (bl->randomize & RANDOM_DATAIN_SEQ_OFFSETS) { | ||
461 | if (iscsit_randomize_seq_lists(cmd, bl->type) | ||
462 | < 0) | ||
463 | return -1; | ||
464 | } else | ||
465 | iscsit_ordered_seq_lists(cmd, bl->type); | ||
466 | } | ||
467 | #if 0 | ||
468 | iscsit_dump_seq_list(cmd); | ||
469 | #endif | ||
470 | } | ||
471 | if (!datapduinorder) { | ||
472 | if (bl->data_direction & ISCSI_PDU_WRITE) { | ||
473 | if (bl->randomize & RANDOM_DATAOUT_PDU_OFFSETS) { | ||
474 | if (iscsit_randomize_pdu_lists(cmd, bl->type) | ||
475 | < 0) | ||
476 | return -1; | ||
477 | } else | ||
478 | iscsit_ordered_pdu_lists(cmd, bl->type); | ||
479 | } else if (bl->data_direction & ISCSI_PDU_READ) { | ||
480 | if (bl->randomize & RANDOM_DATAIN_PDU_OFFSETS) { | ||
481 | if (iscsit_randomize_pdu_lists(cmd, bl->type) | ||
482 | < 0) | ||
483 | return -1; | ||
484 | } else | ||
485 | iscsit_ordered_pdu_lists(cmd, bl->type); | ||
486 | } | ||
487 | #if 0 | ||
488 | iscsit_dump_pdu_list(cmd); | ||
489 | #endif | ||
490 | } | ||
491 | |||
492 | return 0; | ||
493 | } | ||
494 | |||
495 | /* | ||
496 | * Only called while DataSequenceInOrder=No or DataPDUInOrder=No. | ||
497 | */ | ||
498 | int iscsit_do_build_list( | ||
499 | struct iscsi_cmd *cmd, | ||
500 | struct iscsi_build_list *bl) | ||
501 | { | ||
502 | u32 pdu_count = 0, seq_count = 1; | ||
503 | struct iscsi_conn *conn = cmd->conn; | ||
504 | struct iscsi_pdu *pdu = NULL; | ||
505 | struct iscsi_seq *seq = NULL; | ||
506 | |||
507 | iscsit_determine_counts_for_list(cmd, bl, &seq_count, &pdu_count); | ||
508 | |||
509 | if (!conn->sess->sess_ops->DataSequenceInOrder) { | ||
510 | seq = kzalloc(seq_count * sizeof(struct iscsi_seq), GFP_ATOMIC); | ||
511 | if (!seq) { | ||
512 | pr_err("Unable to allocate struct iscsi_seq list\n"); | ||
513 | return -1; | ||
514 | } | ||
515 | cmd->seq_list = seq; | ||
516 | cmd->seq_count = seq_count; | ||
517 | } | ||
518 | |||
519 | if (!conn->sess->sess_ops->DataPDUInOrder) { | ||
520 | pdu = kzalloc(pdu_count * sizeof(struct iscsi_pdu), GFP_ATOMIC); | ||
521 | if (!pdu) { | ||
522 | pr_err("Unable to allocate struct iscsi_pdu list.\n"); | ||
523 | kfree(seq); | ||
524 | return -1; | ||
525 | } | ||
526 | cmd->pdu_list = pdu; | ||
527 | cmd->pdu_count = pdu_count; | ||
528 | } | ||
529 | |||
530 | return iscsit_build_pdu_and_seq_list(cmd, bl); | ||
531 | } | ||
532 | |||
533 | struct iscsi_pdu *iscsit_get_pdu_holder( | ||
534 | struct iscsi_cmd *cmd, | ||
535 | u32 offset, | ||
536 | u32 length) | ||
537 | { | ||
538 | u32 i; | ||
539 | struct iscsi_pdu *pdu = NULL; | ||
540 | |||
541 | if (!cmd->pdu_list) { | ||
542 | pr_err("struct iscsi_cmd->pdu_list is NULL!\n"); | ||
543 | return NULL; | ||
544 | } | ||
545 | |||
546 | pdu = &cmd->pdu_list[0]; | ||
547 | |||
548 | for (i = 0; i < cmd->pdu_count; i++) | ||
549 | if ((pdu[i].offset == offset) && (pdu[i].length == length)) | ||
550 | return &pdu[i]; | ||
551 | |||
552 | pr_err("Unable to locate PDU holder for ITT: 0x%08x, Offset:" | ||
553 | " %u, Length: %u\n", cmd->init_task_tag, offset, length); | ||
554 | return NULL; | ||
555 | } | ||
556 | |||
557 | struct iscsi_pdu *iscsit_get_pdu_holder_for_seq( | ||
558 | struct iscsi_cmd *cmd, | ||
559 | struct iscsi_seq *seq) | ||
560 | { | ||
561 | u32 i; | ||
562 | struct iscsi_conn *conn = cmd->conn; | ||
563 | struct iscsi_pdu *pdu = NULL; | ||
564 | |||
565 | if (!cmd->pdu_list) { | ||
566 | pr_err("struct iscsi_cmd->pdu_list is NULL!\n"); | ||
567 | return NULL; | ||
568 | } | ||
569 | |||
570 | if (conn->sess->sess_ops->DataSequenceInOrder) { | ||
571 | redo: | ||
572 | pdu = &cmd->pdu_list[cmd->pdu_start]; | ||
573 | |||
574 | for (i = 0; pdu[i].seq_no != cmd->seq_no; i++) { | ||
575 | #if 0 | ||
576 | pr_debug("pdu[i].seq_no: %d, pdu[i].pdu" | ||
577 | "_send_order: %d, pdu[i].offset: %d," | ||
578 | " pdu[i].length: %d\n", pdu[i].seq_no, | ||
579 | pdu[i].pdu_send_order, pdu[i].offset, | ||
580 | pdu[i].length); | ||
581 | #endif | ||
582 | if (pdu[i].pdu_send_order == cmd->pdu_send_order) { | ||
583 | cmd->pdu_send_order++; | ||
584 | return &pdu[i]; | ||
585 | } | ||
586 | } | ||
587 | |||
588 | cmd->pdu_start += cmd->pdu_send_order; | ||
589 | cmd->pdu_send_order = 0; | ||
590 | cmd->seq_no++; | ||
591 | |||
592 | if (cmd->pdu_start < cmd->pdu_count) | ||
593 | goto redo; | ||
594 | |||
595 | pr_err("Command ITT: 0x%08x unable to locate" | ||
596 | " struct iscsi_pdu for cmd->pdu_send_order: %u.\n", | ||
597 | cmd->init_task_tag, cmd->pdu_send_order); | ||
598 | return NULL; | ||
599 | } else { | ||
600 | if (!seq) { | ||
601 | pr_err("struct iscsi_seq is NULL!\n"); | ||
602 | return NULL; | ||
603 | } | ||
604 | #if 0 | ||
605 | pr_debug("seq->pdu_start: %d, seq->pdu_count: %d," | ||
606 | " seq->seq_no: %d\n", seq->pdu_start, seq->pdu_count, | ||
607 | seq->seq_no); | ||
608 | #endif | ||
609 | pdu = &cmd->pdu_list[seq->pdu_start]; | ||
610 | |||
611 | if (seq->pdu_send_order == seq->pdu_count) { | ||
612 | pr_err("Command ITT: 0x%08x seq->pdu_send" | ||
613 | "_order: %u equals seq->pdu_count: %u\n", | ||
614 | cmd->init_task_tag, seq->pdu_send_order, | ||
615 | seq->pdu_count); | ||
616 | return NULL; | ||
617 | } | ||
618 | |||
619 | for (i = 0; i < seq->pdu_count; i++) { | ||
620 | if (pdu[i].pdu_send_order == seq->pdu_send_order) { | ||
621 | seq->pdu_send_order++; | ||
622 | return &pdu[i]; | ||
623 | } | ||
624 | } | ||
625 | |||
626 | pr_err("Command ITT: 0x%08x unable to locate iscsi" | ||
627 | "_pdu_t for seq->pdu_send_order: %u.\n", | ||
628 | cmd->init_task_tag, seq->pdu_send_order); | ||
629 | return NULL; | ||
630 | } | ||
631 | |||
632 | return NULL; | ||
633 | } | ||
634 | |||
635 | struct iscsi_seq *iscsit_get_seq_holder( | ||
636 | struct iscsi_cmd *cmd, | ||
637 | u32 offset, | ||
638 | u32 length) | ||
639 | { | ||
640 | u32 i; | ||
641 | |||
642 | if (!cmd->seq_list) { | ||
643 | pr_err("struct iscsi_cmd->seq_list is NULL!\n"); | ||
644 | return NULL; | ||
645 | } | ||
646 | |||
647 | for (i = 0; i < cmd->seq_count; i++) { | ||
648 | #if 0 | ||
649 | pr_debug("seq_list[i].orig_offset: %d, seq_list[i]." | ||
650 | "xfer_len: %d, seq_list[i].seq_no %u\n", | ||
651 | cmd->seq_list[i].orig_offset, cmd->seq_list[i].xfer_len, | ||
652 | cmd->seq_list[i].seq_no); | ||
653 | #endif | ||
654 | if ((cmd->seq_list[i].orig_offset + | ||
655 | cmd->seq_list[i].xfer_len) >= | ||
656 | (offset + length)) | ||
657 | return &cmd->seq_list[i]; | ||
658 | } | ||
659 | |||
660 | pr_err("Unable to locate Sequence holder for ITT: 0x%08x," | ||
661 | " Offset: %u, Length: %u\n", cmd->init_task_tag, offset, | ||
662 | length); | ||
663 | return NULL; | ||
664 | } | ||
diff --git a/drivers/target/iscsi/iscsi_target_seq_pdu_list.h b/drivers/target/iscsi/iscsi_target_seq_pdu_list.h new file mode 100644 index 000000000000..0d52a10e3069 --- /dev/null +++ b/drivers/target/iscsi/iscsi_target_seq_pdu_list.h | |||
@@ -0,0 +1,86 @@ | |||
1 | #ifndef ISCSI_SEQ_AND_PDU_LIST_H | ||
2 | #define ISCSI_SEQ_AND_PDU_LIST_H | ||
3 | |||
4 | /* struct iscsi_pdu->status */ | ||
5 | #define DATAOUT_PDU_SENT 1 | ||
6 | |||
7 | /* struct iscsi_seq->type */ | ||
8 | #define SEQTYPE_IMMEDIATE 1 | ||
9 | #define SEQTYPE_UNSOLICITED 2 | ||
10 | #define SEQTYPE_NORMAL 3 | ||
11 | |||
12 | /* struct iscsi_seq->status */ | ||
13 | #define DATAOUT_SEQUENCE_GOT_R2T 1 | ||
14 | #define DATAOUT_SEQUENCE_WITHIN_COMMAND_RECOVERY 2 | ||
15 | #define DATAOUT_SEQUENCE_COMPLETE 3 | ||
16 | |||
17 | /* iscsi_determine_counts_for_list() type */ | ||
18 | #define PDULIST_NORMAL 1 | ||
19 | #define PDULIST_IMMEDIATE 2 | ||
20 | #define PDULIST_UNSOLICITED 3 | ||
21 | #define PDULIST_IMMEDIATE_AND_UNSOLICITED 4 | ||
22 | |||
23 | /* struct iscsi_pdu->type */ | ||
24 | #define PDUTYPE_IMMEDIATE 1 | ||
25 | #define PDUTYPE_UNSOLICITED 2 | ||
26 | #define PDUTYPE_NORMAL 3 | ||
27 | |||
28 | /* struct iscsi_pdu->status */ | ||
29 | #define ISCSI_PDU_NOT_RECEIVED 0 | ||
30 | #define ISCSI_PDU_RECEIVED_OK 1 | ||
31 | #define ISCSI_PDU_CRC_FAILED 2 | ||
32 | #define ISCSI_PDU_TIMED_OUT 3 | ||
33 | |||
34 | /* struct iscsi_build_list->randomize */ | ||
35 | #define RANDOM_DATAIN_PDU_OFFSETS 0x01 | ||
36 | #define RANDOM_DATAIN_SEQ_OFFSETS 0x02 | ||
37 | #define RANDOM_DATAOUT_PDU_OFFSETS 0x04 | ||
38 | #define RANDOM_R2T_OFFSETS 0x08 | ||
39 | |||
40 | /* struct iscsi_build_list->data_direction */ | ||
41 | #define ISCSI_PDU_READ 0x01 | ||
42 | #define ISCSI_PDU_WRITE 0x02 | ||
43 | |||
44 | struct iscsi_build_list { | ||
45 | int data_direction; | ||
46 | int randomize; | ||
47 | int type; | ||
48 | int immediate_data_length; | ||
49 | }; | ||
50 | |||
51 | struct iscsi_pdu { | ||
52 | int status; | ||
53 | int type; | ||
54 | u8 flags; | ||
55 | u32 data_sn; | ||
56 | u32 length; | ||
57 | u32 offset; | ||
58 | u32 pdu_send_order; | ||
59 | u32 seq_no; | ||
60 | } ____cacheline_aligned; | ||
61 | |||
62 | struct iscsi_seq { | ||
63 | int sent; | ||
64 | int status; | ||
65 | int type; | ||
66 | u32 data_sn; | ||
67 | u32 first_datasn; | ||
68 | u32 last_datasn; | ||
69 | u32 next_burst_len; | ||
70 | u32 pdu_start; | ||
71 | u32 pdu_count; | ||
72 | u32 offset; | ||
73 | u32 orig_offset; | ||
74 | u32 pdu_send_order; | ||
75 | u32 r2t_sn; | ||
76 | u32 seq_send_order; | ||
77 | u32 seq_no; | ||
78 | u32 xfer_len; | ||
79 | } ____cacheline_aligned; | ||
80 | |||
81 | extern int iscsit_do_build_list(struct iscsi_cmd *, struct iscsi_build_list *); | ||
82 | extern struct iscsi_pdu *iscsit_get_pdu_holder(struct iscsi_cmd *, u32, u32); | ||
83 | extern struct iscsi_pdu *iscsit_get_pdu_holder_for_seq(struct iscsi_cmd *, struct iscsi_seq *); | ||
84 | extern struct iscsi_seq *iscsit_get_seq_holder(struct iscsi_cmd *, u32, u32); | ||
85 | |||
86 | #endif /* ISCSI_SEQ_AND_PDU_LIST_H */ | ||
diff --git a/drivers/target/iscsi/iscsi_target_stat.c b/drivers/target/iscsi/iscsi_target_stat.c new file mode 100644 index 000000000000..bbdbe9301b27 --- /dev/null +++ b/drivers/target/iscsi/iscsi_target_stat.c | |||
@@ -0,0 +1,950 @@ | |||
1 | /******************************************************************************* | ||
2 | * Modern ConfigFS group context specific iSCSI statistics based on original | ||
3 | * iscsi_target_mib.c code | ||
4 | * | ||
5 | * Copyright (c) 2011 Rising Tide Systems | ||
6 | * | ||
7 | * Licensed to the Linux Foundation under the General Public License (GPL) version 2. | ||
8 | * | ||
9 | * Author: Nicholas A. Bellinger <nab@linux-iscsi.org> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or | ||
14 | * (at your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | * GNU General Public License for more details. | ||
20 | ******************************************************************************/ | ||
21 | |||
22 | #include <linux/configfs.h> | ||
23 | #include <scsi/iscsi_proto.h> | ||
24 | #include <target/target_core_base.h> | ||
25 | #include <target/target_core_transport.h> | ||
26 | #include <target/configfs_macros.h> | ||
27 | |||
28 | #include "iscsi_target_core.h" | ||
29 | #include "iscsi_target_parameters.h" | ||
30 | #include "iscsi_target_device.h" | ||
31 | #include "iscsi_target_tpg.h" | ||
32 | #include "iscsi_target_util.h" | ||
33 | #include "iscsi_target_stat.h" | ||
34 | |||
35 | #ifndef INITIAL_JIFFIES | ||
36 | #define INITIAL_JIFFIES ((unsigned long)(unsigned int) (-300*HZ)) | ||
37 | #endif | ||
38 | |||
39 | /* Instance Attributes Table */ | ||
40 | #define ISCSI_INST_NUM_NODES 1 | ||
41 | #define ISCSI_INST_DESCR "Storage Engine Target" | ||
42 | #define ISCSI_INST_LAST_FAILURE_TYPE 0 | ||
43 | #define ISCSI_DISCONTINUITY_TIME 0 | ||
44 | |||
45 | #define ISCSI_NODE_INDEX 1 | ||
46 | |||
47 | #define ISPRINT(a) ((a >= ' ') && (a <= '~')) | ||
48 | |||
49 | /**************************************************************************** | ||
50 | * iSCSI MIB Tables | ||
51 | ****************************************************************************/ | ||
52 | /* | ||
53 | * Instance Attributes Table | ||
54 | */ | ||
55 | CONFIGFS_EATTR_STRUCT(iscsi_stat_instance, iscsi_wwn_stat_grps); | ||
56 | #define ISCSI_STAT_INSTANCE_ATTR(_name, _mode) \ | ||
57 | static struct iscsi_stat_instance_attribute \ | ||
58 | iscsi_stat_instance_##_name = \ | ||
59 | __CONFIGFS_EATTR(_name, _mode, \ | ||
60 | iscsi_stat_instance_show_attr_##_name, \ | ||
61 | iscsi_stat_instance_store_attr_##_name); | ||
62 | |||
63 | #define ISCSI_STAT_INSTANCE_ATTR_RO(_name) \ | ||
64 | static struct iscsi_stat_instance_attribute \ | ||
65 | iscsi_stat_instance_##_name = \ | ||
66 | __CONFIGFS_EATTR_RO(_name, \ | ||
67 | iscsi_stat_instance_show_attr_##_name); | ||
68 | |||
69 | static ssize_t iscsi_stat_instance_show_attr_inst( | ||
70 | struct iscsi_wwn_stat_grps *igrps, char *page) | ||
71 | { | ||
72 | struct iscsi_tiqn *tiqn = container_of(igrps, | ||
73 | struct iscsi_tiqn, tiqn_stat_grps); | ||
74 | |||
75 | return snprintf(page, PAGE_SIZE, "%u\n", tiqn->tiqn_index); | ||
76 | } | ||
77 | ISCSI_STAT_INSTANCE_ATTR_RO(inst); | ||
78 | |||
79 | static ssize_t iscsi_stat_instance_show_attr_min_ver( | ||
80 | struct iscsi_wwn_stat_grps *igrps, char *page) | ||
81 | { | ||
82 | return snprintf(page, PAGE_SIZE, "%u\n", ISCSI_DRAFT20_VERSION); | ||
83 | } | ||
84 | ISCSI_STAT_INSTANCE_ATTR_RO(min_ver); | ||
85 | |||
86 | static ssize_t iscsi_stat_instance_show_attr_max_ver( | ||
87 | struct iscsi_wwn_stat_grps *igrps, char *page) | ||
88 | { | ||
89 | return snprintf(page, PAGE_SIZE, "%u\n", ISCSI_DRAFT20_VERSION); | ||
90 | } | ||
91 | ISCSI_STAT_INSTANCE_ATTR_RO(max_ver); | ||
92 | |||
93 | static ssize_t iscsi_stat_instance_show_attr_portals( | ||
94 | struct iscsi_wwn_stat_grps *igrps, char *page) | ||
95 | { | ||
96 | struct iscsi_tiqn *tiqn = container_of(igrps, | ||
97 | struct iscsi_tiqn, tiqn_stat_grps); | ||
98 | |||
99 | return snprintf(page, PAGE_SIZE, "%u\n", tiqn->tiqn_num_tpg_nps); | ||
100 | } | ||
101 | ISCSI_STAT_INSTANCE_ATTR_RO(portals); | ||
102 | |||
103 | static ssize_t iscsi_stat_instance_show_attr_nodes( | ||
104 | struct iscsi_wwn_stat_grps *igrps, char *page) | ||
105 | { | ||
106 | return snprintf(page, PAGE_SIZE, "%u\n", ISCSI_INST_NUM_NODES); | ||
107 | } | ||
108 | ISCSI_STAT_INSTANCE_ATTR_RO(nodes); | ||
109 | |||
110 | static ssize_t iscsi_stat_instance_show_attr_sessions( | ||
111 | struct iscsi_wwn_stat_grps *igrps, char *page) | ||
112 | { | ||
113 | struct iscsi_tiqn *tiqn = container_of(igrps, | ||
114 | struct iscsi_tiqn, tiqn_stat_grps); | ||
115 | |||
116 | return snprintf(page, PAGE_SIZE, "%u\n", tiqn->tiqn_nsessions); | ||
117 | } | ||
118 | ISCSI_STAT_INSTANCE_ATTR_RO(sessions); | ||
119 | |||
120 | static ssize_t iscsi_stat_instance_show_attr_fail_sess( | ||
121 | struct iscsi_wwn_stat_grps *igrps, char *page) | ||
122 | { | ||
123 | struct iscsi_tiqn *tiqn = container_of(igrps, | ||
124 | struct iscsi_tiqn, tiqn_stat_grps); | ||
125 | struct iscsi_sess_err_stats *sess_err = &tiqn->sess_err_stats; | ||
126 | u32 sess_err_count; | ||
127 | |||
128 | spin_lock_bh(&sess_err->lock); | ||
129 | sess_err_count = (sess_err->digest_errors + | ||
130 | sess_err->cxn_timeout_errors + | ||
131 | sess_err->pdu_format_errors); | ||
132 | spin_unlock_bh(&sess_err->lock); | ||
133 | |||
134 | return snprintf(page, PAGE_SIZE, "%u\n", sess_err_count); | ||
135 | } | ||
136 | ISCSI_STAT_INSTANCE_ATTR_RO(fail_sess); | ||
137 | |||
138 | static ssize_t iscsi_stat_instance_show_attr_fail_type( | ||
139 | struct iscsi_wwn_stat_grps *igrps, char *page) | ||
140 | { | ||
141 | struct iscsi_tiqn *tiqn = container_of(igrps, | ||
142 | struct iscsi_tiqn, tiqn_stat_grps); | ||
143 | struct iscsi_sess_err_stats *sess_err = &tiqn->sess_err_stats; | ||
144 | |||
145 | return snprintf(page, PAGE_SIZE, "%u\n", | ||
146 | sess_err->last_sess_failure_type); | ||
147 | } | ||
148 | ISCSI_STAT_INSTANCE_ATTR_RO(fail_type); | ||
149 | |||
150 | static ssize_t iscsi_stat_instance_show_attr_fail_rem_name( | ||
151 | struct iscsi_wwn_stat_grps *igrps, char *page) | ||
152 | { | ||
153 | struct iscsi_tiqn *tiqn = container_of(igrps, | ||
154 | struct iscsi_tiqn, tiqn_stat_grps); | ||
155 | struct iscsi_sess_err_stats *sess_err = &tiqn->sess_err_stats; | ||
156 | |||
157 | return snprintf(page, PAGE_SIZE, "%s\n", | ||
158 | sess_err->last_sess_fail_rem_name[0] ? | ||
159 | sess_err->last_sess_fail_rem_name : NONE); | ||
160 | } | ||
161 | ISCSI_STAT_INSTANCE_ATTR_RO(fail_rem_name); | ||
162 | |||
163 | static ssize_t iscsi_stat_instance_show_attr_disc_time( | ||
164 | struct iscsi_wwn_stat_grps *igrps, char *page) | ||
165 | { | ||
166 | return snprintf(page, PAGE_SIZE, "%u\n", ISCSI_DISCONTINUITY_TIME); | ||
167 | } | ||
168 | ISCSI_STAT_INSTANCE_ATTR_RO(disc_time); | ||
169 | |||
170 | static ssize_t iscsi_stat_instance_show_attr_description( | ||
171 | struct iscsi_wwn_stat_grps *igrps, char *page) | ||
172 | { | ||
173 | return snprintf(page, PAGE_SIZE, "%s\n", ISCSI_INST_DESCR); | ||
174 | } | ||
175 | ISCSI_STAT_INSTANCE_ATTR_RO(description); | ||
176 | |||
177 | static ssize_t iscsi_stat_instance_show_attr_vendor( | ||
178 | struct iscsi_wwn_stat_grps *igrps, char *page) | ||
179 | { | ||
180 | return snprintf(page, PAGE_SIZE, "RisingTide Systems iSCSI-Target\n"); | ||
181 | } | ||
182 | ISCSI_STAT_INSTANCE_ATTR_RO(vendor); | ||
183 | |||
184 | static ssize_t iscsi_stat_instance_show_attr_version( | ||
185 | struct iscsi_wwn_stat_grps *igrps, char *page) | ||
186 | { | ||
187 | return snprintf(page, PAGE_SIZE, "%s\n", ISCSIT_VERSION); | ||
188 | } | ||
189 | ISCSI_STAT_INSTANCE_ATTR_RO(version); | ||
190 | |||
191 | CONFIGFS_EATTR_OPS(iscsi_stat_instance, iscsi_wwn_stat_grps, | ||
192 | iscsi_instance_group); | ||
193 | |||
194 | static struct configfs_attribute *iscsi_stat_instance_attrs[] = { | ||
195 | &iscsi_stat_instance_inst.attr, | ||
196 | &iscsi_stat_instance_min_ver.attr, | ||
197 | &iscsi_stat_instance_max_ver.attr, | ||
198 | &iscsi_stat_instance_portals.attr, | ||
199 | &iscsi_stat_instance_nodes.attr, | ||
200 | &iscsi_stat_instance_sessions.attr, | ||
201 | &iscsi_stat_instance_fail_sess.attr, | ||
202 | &iscsi_stat_instance_fail_type.attr, | ||
203 | &iscsi_stat_instance_fail_rem_name.attr, | ||
204 | &iscsi_stat_instance_disc_time.attr, | ||
205 | &iscsi_stat_instance_description.attr, | ||
206 | &iscsi_stat_instance_vendor.attr, | ||
207 | &iscsi_stat_instance_version.attr, | ||
208 | NULL, | ||
209 | }; | ||
210 | |||
211 | static struct configfs_item_operations iscsi_stat_instance_item_ops = { | ||
212 | .show_attribute = iscsi_stat_instance_attr_show, | ||
213 | .store_attribute = iscsi_stat_instance_attr_store, | ||
214 | }; | ||
215 | |||
216 | struct config_item_type iscsi_stat_instance_cit = { | ||
217 | .ct_item_ops = &iscsi_stat_instance_item_ops, | ||
218 | .ct_attrs = iscsi_stat_instance_attrs, | ||
219 | .ct_owner = THIS_MODULE, | ||
220 | }; | ||
221 | |||
222 | /* | ||
223 | * Instance Session Failure Stats Table | ||
224 | */ | ||
225 | CONFIGFS_EATTR_STRUCT(iscsi_stat_sess_err, iscsi_wwn_stat_grps); | ||
226 | #define ISCSI_STAT_SESS_ERR_ATTR(_name, _mode) \ | ||
227 | static struct iscsi_stat_sess_err_attribute \ | ||
228 | iscsi_stat_sess_err_##_name = \ | ||
229 | __CONFIGFS_EATTR(_name, _mode, \ | ||
230 | iscsi_stat_sess_err_show_attr_##_name, \ | ||
231 | iscsi_stat_sess_err_store_attr_##_name); | ||
232 | |||
233 | #define ISCSI_STAT_SESS_ERR_ATTR_RO(_name) \ | ||
234 | static struct iscsi_stat_sess_err_attribute \ | ||
235 | iscsi_stat_sess_err_##_name = \ | ||
236 | __CONFIGFS_EATTR_RO(_name, \ | ||
237 | iscsi_stat_sess_err_show_attr_##_name); | ||
238 | |||
239 | static ssize_t iscsi_stat_sess_err_show_attr_inst( | ||
240 | struct iscsi_wwn_stat_grps *igrps, char *page) | ||
241 | { | ||
242 | struct iscsi_tiqn *tiqn = container_of(igrps, | ||
243 | struct iscsi_tiqn, tiqn_stat_grps); | ||
244 | |||
245 | return snprintf(page, PAGE_SIZE, "%u\n", tiqn->tiqn_index); | ||
246 | } | ||
247 | ISCSI_STAT_SESS_ERR_ATTR_RO(inst); | ||
248 | |||
249 | static ssize_t iscsi_stat_sess_err_show_attr_digest_errors( | ||
250 | struct iscsi_wwn_stat_grps *igrps, char *page) | ||
251 | { | ||
252 | struct iscsi_tiqn *tiqn = container_of(igrps, | ||
253 | struct iscsi_tiqn, tiqn_stat_grps); | ||
254 | struct iscsi_sess_err_stats *sess_err = &tiqn->sess_err_stats; | ||
255 | |||
256 | return snprintf(page, PAGE_SIZE, "%u\n", sess_err->digest_errors); | ||
257 | } | ||
258 | ISCSI_STAT_SESS_ERR_ATTR_RO(digest_errors); | ||
259 | |||
260 | static ssize_t iscsi_stat_sess_err_show_attr_cxn_errors( | ||
261 | struct iscsi_wwn_stat_grps *igrps, char *page) | ||
262 | { | ||
263 | struct iscsi_tiqn *tiqn = container_of(igrps, | ||
264 | struct iscsi_tiqn, tiqn_stat_grps); | ||
265 | struct iscsi_sess_err_stats *sess_err = &tiqn->sess_err_stats; | ||
266 | |||
267 | return snprintf(page, PAGE_SIZE, "%u\n", sess_err->cxn_timeout_errors); | ||
268 | } | ||
269 | ISCSI_STAT_SESS_ERR_ATTR_RO(cxn_errors); | ||
270 | |||
271 | static ssize_t iscsi_stat_sess_err_show_attr_format_errors( | ||
272 | struct iscsi_wwn_stat_grps *igrps, char *page) | ||
273 | { | ||
274 | struct iscsi_tiqn *tiqn = container_of(igrps, | ||
275 | struct iscsi_tiqn, tiqn_stat_grps); | ||
276 | struct iscsi_sess_err_stats *sess_err = &tiqn->sess_err_stats; | ||
277 | |||
278 | return snprintf(page, PAGE_SIZE, "%u\n", sess_err->pdu_format_errors); | ||
279 | } | ||
280 | ISCSI_STAT_SESS_ERR_ATTR_RO(format_errors); | ||
281 | |||
282 | CONFIGFS_EATTR_OPS(iscsi_stat_sess_err, iscsi_wwn_stat_grps, | ||
283 | iscsi_sess_err_group); | ||
284 | |||
285 | static struct configfs_attribute *iscsi_stat_sess_err_attrs[] = { | ||
286 | &iscsi_stat_sess_err_inst.attr, | ||
287 | &iscsi_stat_sess_err_digest_errors.attr, | ||
288 | &iscsi_stat_sess_err_cxn_errors.attr, | ||
289 | &iscsi_stat_sess_err_format_errors.attr, | ||
290 | NULL, | ||
291 | }; | ||
292 | |||
293 | static struct configfs_item_operations iscsi_stat_sess_err_item_ops = { | ||
294 | .show_attribute = iscsi_stat_sess_err_attr_show, | ||
295 | .store_attribute = iscsi_stat_sess_err_attr_store, | ||
296 | }; | ||
297 | |||
298 | struct config_item_type iscsi_stat_sess_err_cit = { | ||
299 | .ct_item_ops = &iscsi_stat_sess_err_item_ops, | ||
300 | .ct_attrs = iscsi_stat_sess_err_attrs, | ||
301 | .ct_owner = THIS_MODULE, | ||
302 | }; | ||
303 | |||
304 | /* | ||
305 | * Target Attributes Table | ||
306 | */ | ||
307 | CONFIGFS_EATTR_STRUCT(iscsi_stat_tgt_attr, iscsi_wwn_stat_grps); | ||
308 | #define ISCSI_STAT_TGT_ATTR(_name, _mode) \ | ||
309 | static struct iscsi_stat_tgt_attr_attribute \ | ||
310 | iscsi_stat_tgt_attr_##_name = \ | ||
311 | __CONFIGFS_EATTR(_name, _mode, \ | ||
312 | iscsi_stat_tgt-attr_show_attr_##_name, \ | ||
313 | iscsi_stat_tgt_attr_store_attr_##_name); | ||
314 | |||
315 | #define ISCSI_STAT_TGT_ATTR_RO(_name) \ | ||
316 | static struct iscsi_stat_tgt_attr_attribute \ | ||
317 | iscsi_stat_tgt_attr_##_name = \ | ||
318 | __CONFIGFS_EATTR_RO(_name, \ | ||
319 | iscsi_stat_tgt_attr_show_attr_##_name); | ||
320 | |||
321 | static ssize_t iscsi_stat_tgt_attr_show_attr_inst( | ||
322 | struct iscsi_wwn_stat_grps *igrps, char *page) | ||
323 | { | ||
324 | struct iscsi_tiqn *tiqn = container_of(igrps, | ||
325 | struct iscsi_tiqn, tiqn_stat_grps); | ||
326 | |||
327 | return snprintf(page, PAGE_SIZE, "%u\n", tiqn->tiqn_index); | ||
328 | } | ||
329 | ISCSI_STAT_TGT_ATTR_RO(inst); | ||
330 | |||
331 | static ssize_t iscsi_stat_tgt_attr_show_attr_indx( | ||
332 | struct iscsi_wwn_stat_grps *igrps, char *page) | ||
333 | { | ||
334 | return snprintf(page, PAGE_SIZE, "%u\n", ISCSI_NODE_INDEX); | ||
335 | } | ||
336 | ISCSI_STAT_TGT_ATTR_RO(indx); | ||
337 | |||
338 | static ssize_t iscsi_stat_tgt_attr_show_attr_login_fails( | ||
339 | struct iscsi_wwn_stat_grps *igrps, char *page) | ||
340 | { | ||
341 | struct iscsi_tiqn *tiqn = container_of(igrps, | ||
342 | struct iscsi_tiqn, tiqn_stat_grps); | ||
343 | struct iscsi_login_stats *lstat = &tiqn->login_stats; | ||
344 | u32 fail_count; | ||
345 | |||
346 | spin_lock(&lstat->lock); | ||
347 | fail_count = (lstat->redirects + lstat->authorize_fails + | ||
348 | lstat->authenticate_fails + lstat->negotiate_fails + | ||
349 | lstat->other_fails); | ||
350 | spin_unlock(&lstat->lock); | ||
351 | |||
352 | return snprintf(page, PAGE_SIZE, "%u\n", fail_count); | ||
353 | } | ||
354 | ISCSI_STAT_TGT_ATTR_RO(login_fails); | ||
355 | |||
356 | static ssize_t iscsi_stat_tgt_attr_show_attr_last_fail_time( | ||
357 | struct iscsi_wwn_stat_grps *igrps, char *page) | ||
358 | { | ||
359 | struct iscsi_tiqn *tiqn = container_of(igrps, | ||
360 | struct iscsi_tiqn, tiqn_stat_grps); | ||
361 | struct iscsi_login_stats *lstat = &tiqn->login_stats; | ||
362 | u32 last_fail_time; | ||
363 | |||
364 | spin_lock(&lstat->lock); | ||
365 | last_fail_time = lstat->last_fail_time ? | ||
366 | (u32)(((u32)lstat->last_fail_time - | ||
367 | INITIAL_JIFFIES) * 100 / HZ) : 0; | ||
368 | spin_unlock(&lstat->lock); | ||
369 | |||
370 | return snprintf(page, PAGE_SIZE, "%u\n", last_fail_time); | ||
371 | } | ||
372 | ISCSI_STAT_TGT_ATTR_RO(last_fail_time); | ||
373 | |||
374 | static ssize_t iscsi_stat_tgt_attr_show_attr_last_fail_type( | ||
375 | struct iscsi_wwn_stat_grps *igrps, char *page) | ||
376 | { | ||
377 | struct iscsi_tiqn *tiqn = container_of(igrps, | ||
378 | struct iscsi_tiqn, tiqn_stat_grps); | ||
379 | struct iscsi_login_stats *lstat = &tiqn->login_stats; | ||
380 | u32 last_fail_type; | ||
381 | |||
382 | spin_lock(&lstat->lock); | ||
383 | last_fail_type = lstat->last_fail_type; | ||
384 | spin_unlock(&lstat->lock); | ||
385 | |||
386 | return snprintf(page, PAGE_SIZE, "%u\n", last_fail_type); | ||
387 | } | ||
388 | ISCSI_STAT_TGT_ATTR_RO(last_fail_type); | ||
389 | |||
390 | static ssize_t iscsi_stat_tgt_attr_show_attr_fail_intr_name( | ||
391 | struct iscsi_wwn_stat_grps *igrps, char *page) | ||
392 | { | ||
393 | struct iscsi_tiqn *tiqn = container_of(igrps, | ||
394 | struct iscsi_tiqn, tiqn_stat_grps); | ||
395 | struct iscsi_login_stats *lstat = &tiqn->login_stats; | ||
396 | unsigned char buf[224]; | ||
397 | |||
398 | spin_lock(&lstat->lock); | ||
399 | snprintf(buf, 224, "%s", lstat->last_intr_fail_name[0] ? | ||
400 | lstat->last_intr_fail_name : NONE); | ||
401 | spin_unlock(&lstat->lock); | ||
402 | |||
403 | return snprintf(page, PAGE_SIZE, "%s\n", buf); | ||
404 | } | ||
405 | ISCSI_STAT_TGT_ATTR_RO(fail_intr_name); | ||
406 | |||
407 | static ssize_t iscsi_stat_tgt_attr_show_attr_fail_intr_addr_type( | ||
408 | struct iscsi_wwn_stat_grps *igrps, char *page) | ||
409 | { | ||
410 | struct iscsi_tiqn *tiqn = container_of(igrps, | ||
411 | struct iscsi_tiqn, tiqn_stat_grps); | ||
412 | struct iscsi_login_stats *lstat = &tiqn->login_stats; | ||
413 | unsigned char buf[8]; | ||
414 | |||
415 | spin_lock(&lstat->lock); | ||
416 | snprintf(buf, 8, "%s", (lstat->last_intr_fail_ip_addr != NULL) ? | ||
417 | "ipv6" : "ipv4"); | ||
418 | spin_unlock(&lstat->lock); | ||
419 | |||
420 | return snprintf(page, PAGE_SIZE, "%s\n", buf); | ||
421 | } | ||
422 | ISCSI_STAT_TGT_ATTR_RO(fail_intr_addr_type); | ||
423 | |||
424 | static ssize_t iscsi_stat_tgt_attr_show_attr_fail_intr_addr( | ||
425 | struct iscsi_wwn_stat_grps *igrps, char *page) | ||
426 | { | ||
427 | struct iscsi_tiqn *tiqn = container_of(igrps, | ||
428 | struct iscsi_tiqn, tiqn_stat_grps); | ||
429 | struct iscsi_login_stats *lstat = &tiqn->login_stats; | ||
430 | unsigned char buf[32]; | ||
431 | |||
432 | spin_lock(&lstat->lock); | ||
433 | if (lstat->last_intr_fail_ip_family == AF_INET6) | ||
434 | snprintf(buf, 32, "[%s]", lstat->last_intr_fail_ip_addr); | ||
435 | else | ||
436 | snprintf(buf, 32, "%s", lstat->last_intr_fail_ip_addr); | ||
437 | spin_unlock(&lstat->lock); | ||
438 | |||
439 | return snprintf(page, PAGE_SIZE, "%s\n", buf); | ||
440 | } | ||
441 | ISCSI_STAT_TGT_ATTR_RO(fail_intr_addr); | ||
442 | |||
443 | CONFIGFS_EATTR_OPS(iscsi_stat_tgt_attr, iscsi_wwn_stat_grps, | ||
444 | iscsi_tgt_attr_group); | ||
445 | |||
446 | static struct configfs_attribute *iscsi_stat_tgt_attr_attrs[] = { | ||
447 | &iscsi_stat_tgt_attr_inst.attr, | ||
448 | &iscsi_stat_tgt_attr_indx.attr, | ||
449 | &iscsi_stat_tgt_attr_login_fails.attr, | ||
450 | &iscsi_stat_tgt_attr_last_fail_time.attr, | ||
451 | &iscsi_stat_tgt_attr_last_fail_type.attr, | ||
452 | &iscsi_stat_tgt_attr_fail_intr_name.attr, | ||
453 | &iscsi_stat_tgt_attr_fail_intr_addr_type.attr, | ||
454 | &iscsi_stat_tgt_attr_fail_intr_addr.attr, | ||
455 | NULL, | ||
456 | }; | ||
457 | |||
458 | static struct configfs_item_operations iscsi_stat_tgt_attr_item_ops = { | ||
459 | .show_attribute = iscsi_stat_tgt_attr_attr_show, | ||
460 | .store_attribute = iscsi_stat_tgt_attr_attr_store, | ||
461 | }; | ||
462 | |||
463 | struct config_item_type iscsi_stat_tgt_attr_cit = { | ||
464 | .ct_item_ops = &iscsi_stat_tgt_attr_item_ops, | ||
465 | .ct_attrs = iscsi_stat_tgt_attr_attrs, | ||
466 | .ct_owner = THIS_MODULE, | ||
467 | }; | ||
468 | |||
469 | /* | ||
470 | * Target Login Stats Table | ||
471 | */ | ||
472 | CONFIGFS_EATTR_STRUCT(iscsi_stat_login, iscsi_wwn_stat_grps); | ||
473 | #define ISCSI_STAT_LOGIN(_name, _mode) \ | ||
474 | static struct iscsi_stat_login_attribute \ | ||
475 | iscsi_stat_login_##_name = \ | ||
476 | __CONFIGFS_EATTR(_name, _mode, \ | ||
477 | iscsi_stat_login_show_attr_##_name, \ | ||
478 | iscsi_stat_login_store_attr_##_name); | ||
479 | |||
480 | #define ISCSI_STAT_LOGIN_RO(_name) \ | ||
481 | static struct iscsi_stat_login_attribute \ | ||
482 | iscsi_stat_login_##_name = \ | ||
483 | __CONFIGFS_EATTR_RO(_name, \ | ||
484 | iscsi_stat_login_show_attr_##_name); | ||
485 | |||
486 | static ssize_t iscsi_stat_login_show_attr_inst( | ||
487 | struct iscsi_wwn_stat_grps *igrps, char *page) | ||
488 | { | ||
489 | struct iscsi_tiqn *tiqn = container_of(igrps, | ||
490 | struct iscsi_tiqn, tiqn_stat_grps); | ||
491 | |||
492 | return snprintf(page, PAGE_SIZE, "%u\n", tiqn->tiqn_index); | ||
493 | } | ||
494 | ISCSI_STAT_LOGIN_RO(inst); | ||
495 | |||
496 | static ssize_t iscsi_stat_login_show_attr_indx( | ||
497 | struct iscsi_wwn_stat_grps *igrps, char *page) | ||
498 | { | ||
499 | return snprintf(page, PAGE_SIZE, "%u\n", ISCSI_NODE_INDEX); | ||
500 | } | ||
501 | ISCSI_STAT_LOGIN_RO(indx); | ||
502 | |||
503 | static ssize_t iscsi_stat_login_show_attr_accepts( | ||
504 | struct iscsi_wwn_stat_grps *igrps, char *page) | ||
505 | { | ||
506 | struct iscsi_tiqn *tiqn = container_of(igrps, | ||
507 | struct iscsi_tiqn, tiqn_stat_grps); | ||
508 | struct iscsi_login_stats *lstat = &tiqn->login_stats; | ||
509 | ssize_t ret; | ||
510 | |||
511 | spin_lock(&lstat->lock); | ||
512 | ret = snprintf(page, PAGE_SIZE, "%u\n", lstat->accepts); | ||
513 | spin_unlock(&lstat->lock); | ||
514 | |||
515 | return ret; | ||
516 | } | ||
517 | ISCSI_STAT_LOGIN_RO(accepts); | ||
518 | |||
519 | static ssize_t iscsi_stat_login_show_attr_other_fails( | ||
520 | struct iscsi_wwn_stat_grps *igrps, char *page) | ||
521 | { | ||
522 | struct iscsi_tiqn *tiqn = container_of(igrps, | ||
523 | struct iscsi_tiqn, tiqn_stat_grps); | ||
524 | struct iscsi_login_stats *lstat = &tiqn->login_stats; | ||
525 | ssize_t ret; | ||
526 | |||
527 | spin_lock(&lstat->lock); | ||
528 | ret = snprintf(page, PAGE_SIZE, "%u\n", lstat->other_fails); | ||
529 | spin_unlock(&lstat->lock); | ||
530 | |||
531 | return ret; | ||
532 | } | ||
533 | ISCSI_STAT_LOGIN_RO(other_fails); | ||
534 | |||
535 | static ssize_t iscsi_stat_login_show_attr_redirects( | ||
536 | struct iscsi_wwn_stat_grps *igrps, char *page) | ||
537 | { | ||
538 | struct iscsi_tiqn *tiqn = container_of(igrps, | ||
539 | struct iscsi_tiqn, tiqn_stat_grps); | ||
540 | struct iscsi_login_stats *lstat = &tiqn->login_stats; | ||
541 | ssize_t ret; | ||
542 | |||
543 | spin_lock(&lstat->lock); | ||
544 | ret = snprintf(page, PAGE_SIZE, "%u\n", lstat->redirects); | ||
545 | spin_unlock(&lstat->lock); | ||
546 | |||
547 | return ret; | ||
548 | } | ||
549 | ISCSI_STAT_LOGIN_RO(redirects); | ||
550 | |||
551 | static ssize_t iscsi_stat_login_show_attr_authorize_fails( | ||
552 | struct iscsi_wwn_stat_grps *igrps, char *page) | ||
553 | { | ||
554 | struct iscsi_tiqn *tiqn = container_of(igrps, | ||
555 | struct iscsi_tiqn, tiqn_stat_grps); | ||
556 | struct iscsi_login_stats *lstat = &tiqn->login_stats; | ||
557 | ssize_t ret; | ||
558 | |||
559 | spin_lock(&lstat->lock); | ||
560 | ret = snprintf(page, PAGE_SIZE, "%u\n", lstat->authorize_fails); | ||
561 | spin_unlock(&lstat->lock); | ||
562 | |||
563 | return ret; | ||
564 | } | ||
565 | ISCSI_STAT_LOGIN_RO(authorize_fails); | ||
566 | |||
567 | static ssize_t iscsi_stat_login_show_attr_authenticate_fails( | ||
568 | struct iscsi_wwn_stat_grps *igrps, char *page) | ||
569 | { | ||
570 | struct iscsi_tiqn *tiqn = container_of(igrps, | ||
571 | struct iscsi_tiqn, tiqn_stat_grps); | ||
572 | struct iscsi_login_stats *lstat = &tiqn->login_stats; | ||
573 | ssize_t ret; | ||
574 | |||
575 | spin_lock(&lstat->lock); | ||
576 | ret = snprintf(page, PAGE_SIZE, "%u\n", lstat->authenticate_fails); | ||
577 | spin_unlock(&lstat->lock); | ||
578 | |||
579 | return ret; | ||
580 | } | ||
581 | ISCSI_STAT_LOGIN_RO(authenticate_fails); | ||
582 | |||
583 | static ssize_t iscsi_stat_login_show_attr_negotiate_fails( | ||
584 | struct iscsi_wwn_stat_grps *igrps, char *page) | ||
585 | { | ||
586 | struct iscsi_tiqn *tiqn = container_of(igrps, | ||
587 | struct iscsi_tiqn, tiqn_stat_grps); | ||
588 | struct iscsi_login_stats *lstat = &tiqn->login_stats; | ||
589 | ssize_t ret; | ||
590 | |||
591 | spin_lock(&lstat->lock); | ||
592 | ret = snprintf(page, PAGE_SIZE, "%u\n", lstat->negotiate_fails); | ||
593 | spin_unlock(&lstat->lock); | ||
594 | |||
595 | return ret; | ||
596 | } | ||
597 | ISCSI_STAT_LOGIN_RO(negotiate_fails); | ||
598 | |||
599 | CONFIGFS_EATTR_OPS(iscsi_stat_login, iscsi_wwn_stat_grps, | ||
600 | iscsi_login_stats_group); | ||
601 | |||
602 | static struct configfs_attribute *iscsi_stat_login_stats_attrs[] = { | ||
603 | &iscsi_stat_login_inst.attr, | ||
604 | &iscsi_stat_login_indx.attr, | ||
605 | &iscsi_stat_login_accepts.attr, | ||
606 | &iscsi_stat_login_other_fails.attr, | ||
607 | &iscsi_stat_login_redirects.attr, | ||
608 | &iscsi_stat_login_authorize_fails.attr, | ||
609 | &iscsi_stat_login_authenticate_fails.attr, | ||
610 | &iscsi_stat_login_negotiate_fails.attr, | ||
611 | NULL, | ||
612 | }; | ||
613 | |||
614 | static struct configfs_item_operations iscsi_stat_login_stats_item_ops = { | ||
615 | .show_attribute = iscsi_stat_login_attr_show, | ||
616 | .store_attribute = iscsi_stat_login_attr_store, | ||
617 | }; | ||
618 | |||
619 | struct config_item_type iscsi_stat_login_cit = { | ||
620 | .ct_item_ops = &iscsi_stat_login_stats_item_ops, | ||
621 | .ct_attrs = iscsi_stat_login_stats_attrs, | ||
622 | .ct_owner = THIS_MODULE, | ||
623 | }; | ||
624 | |||
625 | /* | ||
626 | * Target Logout Stats Table | ||
627 | */ | ||
628 | |||
629 | CONFIGFS_EATTR_STRUCT(iscsi_stat_logout, iscsi_wwn_stat_grps); | ||
630 | #define ISCSI_STAT_LOGOUT(_name, _mode) \ | ||
631 | static struct iscsi_stat_logout_attribute \ | ||
632 | iscsi_stat_logout_##_name = \ | ||
633 | __CONFIGFS_EATTR(_name, _mode, \ | ||
634 | iscsi_stat_logout_show_attr_##_name, \ | ||
635 | iscsi_stat_logout_store_attr_##_name); | ||
636 | |||
637 | #define ISCSI_STAT_LOGOUT_RO(_name) \ | ||
638 | static struct iscsi_stat_logout_attribute \ | ||
639 | iscsi_stat_logout_##_name = \ | ||
640 | __CONFIGFS_EATTR_RO(_name, \ | ||
641 | iscsi_stat_logout_show_attr_##_name); | ||
642 | |||
643 | static ssize_t iscsi_stat_logout_show_attr_inst( | ||
644 | struct iscsi_wwn_stat_grps *igrps, char *page) | ||
645 | { | ||
646 | struct iscsi_tiqn *tiqn = container_of(igrps, | ||
647 | struct iscsi_tiqn, tiqn_stat_grps); | ||
648 | |||
649 | return snprintf(page, PAGE_SIZE, "%u\n", tiqn->tiqn_index); | ||
650 | } | ||
651 | ISCSI_STAT_LOGOUT_RO(inst); | ||
652 | |||
653 | static ssize_t iscsi_stat_logout_show_attr_indx( | ||
654 | struct iscsi_wwn_stat_grps *igrps, char *page) | ||
655 | { | ||
656 | return snprintf(page, PAGE_SIZE, "%u\n", ISCSI_NODE_INDEX); | ||
657 | } | ||
658 | ISCSI_STAT_LOGOUT_RO(indx); | ||
659 | |||
660 | static ssize_t iscsi_stat_logout_show_attr_normal_logouts( | ||
661 | struct iscsi_wwn_stat_grps *igrps, char *page) | ||
662 | { | ||
663 | struct iscsi_tiqn *tiqn = container_of(igrps, | ||
664 | struct iscsi_tiqn, tiqn_stat_grps); | ||
665 | struct iscsi_logout_stats *lstats = &tiqn->logout_stats; | ||
666 | |||
667 | return snprintf(page, PAGE_SIZE, "%u\n", lstats->normal_logouts); | ||
668 | } | ||
669 | ISCSI_STAT_LOGOUT_RO(normal_logouts); | ||
670 | |||
671 | static ssize_t iscsi_stat_logout_show_attr_abnormal_logouts( | ||
672 | struct iscsi_wwn_stat_grps *igrps, char *page) | ||
673 | { | ||
674 | struct iscsi_tiqn *tiqn = container_of(igrps, | ||
675 | struct iscsi_tiqn, tiqn_stat_grps); | ||
676 | struct iscsi_logout_stats *lstats = &tiqn->logout_stats; | ||
677 | |||
678 | return snprintf(page, PAGE_SIZE, "%u\n", lstats->abnormal_logouts); | ||
679 | } | ||
680 | ISCSI_STAT_LOGOUT_RO(abnormal_logouts); | ||
681 | |||
682 | CONFIGFS_EATTR_OPS(iscsi_stat_logout, iscsi_wwn_stat_grps, | ||
683 | iscsi_logout_stats_group); | ||
684 | |||
685 | static struct configfs_attribute *iscsi_stat_logout_stats_attrs[] = { | ||
686 | &iscsi_stat_logout_inst.attr, | ||
687 | &iscsi_stat_logout_indx.attr, | ||
688 | &iscsi_stat_logout_normal_logouts.attr, | ||
689 | &iscsi_stat_logout_abnormal_logouts.attr, | ||
690 | NULL, | ||
691 | }; | ||
692 | |||
693 | static struct configfs_item_operations iscsi_stat_logout_stats_item_ops = { | ||
694 | .show_attribute = iscsi_stat_logout_attr_show, | ||
695 | .store_attribute = iscsi_stat_logout_attr_store, | ||
696 | }; | ||
697 | |||
698 | struct config_item_type iscsi_stat_logout_cit = { | ||
699 | .ct_item_ops = &iscsi_stat_logout_stats_item_ops, | ||
700 | .ct_attrs = iscsi_stat_logout_stats_attrs, | ||
701 | .ct_owner = THIS_MODULE, | ||
702 | }; | ||
703 | |||
704 | /* | ||
705 | * Session Stats Table | ||
706 | */ | ||
707 | |||
708 | CONFIGFS_EATTR_STRUCT(iscsi_stat_sess, iscsi_node_stat_grps); | ||
709 | #define ISCSI_STAT_SESS(_name, _mode) \ | ||
710 | static struct iscsi_stat_sess_attribute \ | ||
711 | iscsi_stat_sess_##_name = \ | ||
712 | __CONFIGFS_EATTR(_name, _mode, \ | ||
713 | iscsi_stat_sess_show_attr_##_name, \ | ||
714 | iscsi_stat_sess_store_attr_##_name); | ||
715 | |||
716 | #define ISCSI_STAT_SESS_RO(_name) \ | ||
717 | static struct iscsi_stat_sess_attribute \ | ||
718 | iscsi_stat_sess_##_name = \ | ||
719 | __CONFIGFS_EATTR_RO(_name, \ | ||
720 | iscsi_stat_sess_show_attr_##_name); | ||
721 | |||
722 | static ssize_t iscsi_stat_sess_show_attr_inst( | ||
723 | struct iscsi_node_stat_grps *igrps, char *page) | ||
724 | { | ||
725 | struct iscsi_node_acl *acl = container_of(igrps, | ||
726 | struct iscsi_node_acl, node_stat_grps); | ||
727 | struct se_wwn *wwn = acl->se_node_acl.se_tpg->se_tpg_wwn; | ||
728 | struct iscsi_tiqn *tiqn = container_of(wwn, | ||
729 | struct iscsi_tiqn, tiqn_wwn); | ||
730 | |||
731 | return snprintf(page, PAGE_SIZE, "%u\n", tiqn->tiqn_index); | ||
732 | } | ||
733 | ISCSI_STAT_SESS_RO(inst); | ||
734 | |||
735 | static ssize_t iscsi_stat_sess_show_attr_node( | ||
736 | struct iscsi_node_stat_grps *igrps, char *page) | ||
737 | { | ||
738 | struct iscsi_node_acl *acl = container_of(igrps, | ||
739 | struct iscsi_node_acl, node_stat_grps); | ||
740 | struct se_node_acl *se_nacl = &acl->se_node_acl; | ||
741 | struct iscsi_session *sess; | ||
742 | struct se_session *se_sess; | ||
743 | ssize_t ret = 0; | ||
744 | |||
745 | spin_lock_bh(&se_nacl->nacl_sess_lock); | ||
746 | se_sess = se_nacl->nacl_sess; | ||
747 | if (se_sess) { | ||
748 | sess = (struct iscsi_session *)se_sess->fabric_sess_ptr; | ||
749 | if (sess) | ||
750 | ret = snprintf(page, PAGE_SIZE, "%u\n", | ||
751 | sess->sess_ops->SessionType ? 0 : ISCSI_NODE_INDEX); | ||
752 | } | ||
753 | spin_unlock_bh(&se_nacl->nacl_sess_lock); | ||
754 | |||
755 | return ret; | ||
756 | } | ||
757 | ISCSI_STAT_SESS_RO(node); | ||
758 | |||
759 | static ssize_t iscsi_stat_sess_show_attr_indx( | ||
760 | struct iscsi_node_stat_grps *igrps, char *page) | ||
761 | { | ||
762 | struct iscsi_node_acl *acl = container_of(igrps, | ||
763 | struct iscsi_node_acl, node_stat_grps); | ||
764 | struct se_node_acl *se_nacl = &acl->se_node_acl; | ||
765 | struct iscsi_session *sess; | ||
766 | struct se_session *se_sess; | ||
767 | ssize_t ret = 0; | ||
768 | |||
769 | spin_lock_bh(&se_nacl->nacl_sess_lock); | ||
770 | se_sess = se_nacl->nacl_sess; | ||
771 | if (se_sess) { | ||
772 | sess = (struct iscsi_session *)se_sess->fabric_sess_ptr; | ||
773 | if (sess) | ||
774 | ret = snprintf(page, PAGE_SIZE, "%u\n", | ||
775 | sess->session_index); | ||
776 | } | ||
777 | spin_unlock_bh(&se_nacl->nacl_sess_lock); | ||
778 | |||
779 | return ret; | ||
780 | } | ||
781 | ISCSI_STAT_SESS_RO(indx); | ||
782 | |||
783 | static ssize_t iscsi_stat_sess_show_attr_cmd_pdus( | ||
784 | struct iscsi_node_stat_grps *igrps, char *page) | ||
785 | { | ||
786 | struct iscsi_node_acl *acl = container_of(igrps, | ||
787 | struct iscsi_node_acl, node_stat_grps); | ||
788 | struct se_node_acl *se_nacl = &acl->se_node_acl; | ||
789 | struct iscsi_session *sess; | ||
790 | struct se_session *se_sess; | ||
791 | ssize_t ret = 0; | ||
792 | |||
793 | spin_lock_bh(&se_nacl->nacl_sess_lock); | ||
794 | se_sess = se_nacl->nacl_sess; | ||
795 | if (se_sess) { | ||
796 | sess = (struct iscsi_session *)se_sess->fabric_sess_ptr; | ||
797 | if (sess) | ||
798 | ret = snprintf(page, PAGE_SIZE, "%u\n", sess->cmd_pdus); | ||
799 | } | ||
800 | spin_unlock_bh(&se_nacl->nacl_sess_lock); | ||
801 | |||
802 | return ret; | ||
803 | } | ||
804 | ISCSI_STAT_SESS_RO(cmd_pdus); | ||
805 | |||
806 | static ssize_t iscsi_stat_sess_show_attr_rsp_pdus( | ||
807 | struct iscsi_node_stat_grps *igrps, char *page) | ||
808 | { | ||
809 | struct iscsi_node_acl *acl = container_of(igrps, | ||
810 | struct iscsi_node_acl, node_stat_grps); | ||
811 | struct se_node_acl *se_nacl = &acl->se_node_acl; | ||
812 | struct iscsi_session *sess; | ||
813 | struct se_session *se_sess; | ||
814 | ssize_t ret = 0; | ||
815 | |||
816 | spin_lock_bh(&se_nacl->nacl_sess_lock); | ||
817 | se_sess = se_nacl->nacl_sess; | ||
818 | if (se_sess) { | ||
819 | sess = (struct iscsi_session *)se_sess->fabric_sess_ptr; | ||
820 | if (sess) | ||
821 | ret = snprintf(page, PAGE_SIZE, "%u\n", sess->rsp_pdus); | ||
822 | } | ||
823 | spin_unlock_bh(&se_nacl->nacl_sess_lock); | ||
824 | |||
825 | return ret; | ||
826 | } | ||
827 | ISCSI_STAT_SESS_RO(rsp_pdus); | ||
828 | |||
829 | static ssize_t iscsi_stat_sess_show_attr_txdata_octs( | ||
830 | struct iscsi_node_stat_grps *igrps, char *page) | ||
831 | { | ||
832 | struct iscsi_node_acl *acl = container_of(igrps, | ||
833 | struct iscsi_node_acl, node_stat_grps); | ||
834 | struct se_node_acl *se_nacl = &acl->se_node_acl; | ||
835 | struct iscsi_session *sess; | ||
836 | struct se_session *se_sess; | ||
837 | ssize_t ret = 0; | ||
838 | |||
839 | spin_lock_bh(&se_nacl->nacl_sess_lock); | ||
840 | se_sess = se_nacl->nacl_sess; | ||
841 | if (se_sess) { | ||
842 | sess = (struct iscsi_session *)se_sess->fabric_sess_ptr; | ||
843 | if (sess) | ||
844 | ret = snprintf(page, PAGE_SIZE, "%llu\n", | ||
845 | (unsigned long long)sess->tx_data_octets); | ||
846 | } | ||
847 | spin_unlock_bh(&se_nacl->nacl_sess_lock); | ||
848 | |||
849 | return ret; | ||
850 | } | ||
851 | ISCSI_STAT_SESS_RO(txdata_octs); | ||
852 | |||
853 | static ssize_t iscsi_stat_sess_show_attr_rxdata_octs( | ||
854 | struct iscsi_node_stat_grps *igrps, char *page) | ||
855 | { | ||
856 | struct iscsi_node_acl *acl = container_of(igrps, | ||
857 | struct iscsi_node_acl, node_stat_grps); | ||
858 | struct se_node_acl *se_nacl = &acl->se_node_acl; | ||
859 | struct iscsi_session *sess; | ||
860 | struct se_session *se_sess; | ||
861 | ssize_t ret = 0; | ||
862 | |||
863 | spin_lock_bh(&se_nacl->nacl_sess_lock); | ||
864 | se_sess = se_nacl->nacl_sess; | ||
865 | if (se_sess) { | ||
866 | sess = (struct iscsi_session *)se_sess->fabric_sess_ptr; | ||
867 | if (sess) | ||
868 | ret = snprintf(page, PAGE_SIZE, "%llu\n", | ||
869 | (unsigned long long)sess->rx_data_octets); | ||
870 | } | ||
871 | spin_unlock_bh(&se_nacl->nacl_sess_lock); | ||
872 | |||
873 | return ret; | ||
874 | } | ||
875 | ISCSI_STAT_SESS_RO(rxdata_octs); | ||
876 | |||
877 | static ssize_t iscsi_stat_sess_show_attr_conn_digest_errors( | ||
878 | struct iscsi_node_stat_grps *igrps, char *page) | ||
879 | { | ||
880 | struct iscsi_node_acl *acl = container_of(igrps, | ||
881 | struct iscsi_node_acl, node_stat_grps); | ||
882 | struct se_node_acl *se_nacl = &acl->se_node_acl; | ||
883 | struct iscsi_session *sess; | ||
884 | struct se_session *se_sess; | ||
885 | ssize_t ret = 0; | ||
886 | |||
887 | spin_lock_bh(&se_nacl->nacl_sess_lock); | ||
888 | se_sess = se_nacl->nacl_sess; | ||
889 | if (se_sess) { | ||
890 | sess = (struct iscsi_session *)se_sess->fabric_sess_ptr; | ||
891 | if (sess) | ||
892 | ret = snprintf(page, PAGE_SIZE, "%u\n", | ||
893 | sess->conn_digest_errors); | ||
894 | } | ||
895 | spin_unlock_bh(&se_nacl->nacl_sess_lock); | ||
896 | |||
897 | return ret; | ||
898 | } | ||
899 | ISCSI_STAT_SESS_RO(conn_digest_errors); | ||
900 | |||
901 | static ssize_t iscsi_stat_sess_show_attr_conn_timeout_errors( | ||
902 | struct iscsi_node_stat_grps *igrps, char *page) | ||
903 | { | ||
904 | struct iscsi_node_acl *acl = container_of(igrps, | ||
905 | struct iscsi_node_acl, node_stat_grps); | ||
906 | struct se_node_acl *se_nacl = &acl->se_node_acl; | ||
907 | struct iscsi_session *sess; | ||
908 | struct se_session *se_sess; | ||
909 | ssize_t ret = 0; | ||
910 | |||
911 | spin_lock_bh(&se_nacl->nacl_sess_lock); | ||
912 | se_sess = se_nacl->nacl_sess; | ||
913 | if (se_sess) { | ||
914 | sess = (struct iscsi_session *)se_sess->fabric_sess_ptr; | ||
915 | if (sess) | ||
916 | ret = snprintf(page, PAGE_SIZE, "%u\n", | ||
917 | sess->conn_timeout_errors); | ||
918 | } | ||
919 | spin_unlock_bh(&se_nacl->nacl_sess_lock); | ||
920 | |||
921 | return ret; | ||
922 | } | ||
923 | ISCSI_STAT_SESS_RO(conn_timeout_errors); | ||
924 | |||
925 | CONFIGFS_EATTR_OPS(iscsi_stat_sess, iscsi_node_stat_grps, | ||
926 | iscsi_sess_stats_group); | ||
927 | |||
928 | static struct configfs_attribute *iscsi_stat_sess_stats_attrs[] = { | ||
929 | &iscsi_stat_sess_inst.attr, | ||
930 | &iscsi_stat_sess_node.attr, | ||
931 | &iscsi_stat_sess_indx.attr, | ||
932 | &iscsi_stat_sess_cmd_pdus.attr, | ||
933 | &iscsi_stat_sess_rsp_pdus.attr, | ||
934 | &iscsi_stat_sess_txdata_octs.attr, | ||
935 | &iscsi_stat_sess_rxdata_octs.attr, | ||
936 | &iscsi_stat_sess_conn_digest_errors.attr, | ||
937 | &iscsi_stat_sess_conn_timeout_errors.attr, | ||
938 | NULL, | ||
939 | }; | ||
940 | |||
941 | static struct configfs_item_operations iscsi_stat_sess_stats_item_ops = { | ||
942 | .show_attribute = iscsi_stat_sess_attr_show, | ||
943 | .store_attribute = iscsi_stat_sess_attr_store, | ||
944 | }; | ||
945 | |||
946 | struct config_item_type iscsi_stat_sess_cit = { | ||
947 | .ct_item_ops = &iscsi_stat_sess_stats_item_ops, | ||
948 | .ct_attrs = iscsi_stat_sess_stats_attrs, | ||
949 | .ct_owner = THIS_MODULE, | ||
950 | }; | ||
diff --git a/drivers/target/iscsi/iscsi_target_stat.h b/drivers/target/iscsi/iscsi_target_stat.h new file mode 100644 index 000000000000..3ff76b4faad3 --- /dev/null +++ b/drivers/target/iscsi/iscsi_target_stat.h | |||
@@ -0,0 +1,64 @@ | |||
1 | #ifndef ISCSI_TARGET_STAT_H | ||
2 | #define ISCSI_TARGET_STAT_H | ||
3 | |||
4 | /* | ||
5 | * For struct iscsi_tiqn->tiqn_wwn default groups | ||
6 | */ | ||
7 | extern struct config_item_type iscsi_stat_instance_cit; | ||
8 | extern struct config_item_type iscsi_stat_sess_err_cit; | ||
9 | extern struct config_item_type iscsi_stat_tgt_attr_cit; | ||
10 | extern struct config_item_type iscsi_stat_login_cit; | ||
11 | extern struct config_item_type iscsi_stat_logout_cit; | ||
12 | |||
13 | /* | ||
14 | * For struct iscsi_session->se_sess default groups | ||
15 | */ | ||
16 | extern struct config_item_type iscsi_stat_sess_cit; | ||
17 | |||
18 | /* iSCSI session error types */ | ||
19 | #define ISCSI_SESS_ERR_UNKNOWN 0 | ||
20 | #define ISCSI_SESS_ERR_DIGEST 1 | ||
21 | #define ISCSI_SESS_ERR_CXN_TIMEOUT 2 | ||
22 | #define ISCSI_SESS_ERR_PDU_FORMAT 3 | ||
23 | |||
24 | /* iSCSI session error stats */ | ||
25 | struct iscsi_sess_err_stats { | ||
26 | spinlock_t lock; | ||
27 | u32 digest_errors; | ||
28 | u32 cxn_timeout_errors; | ||
29 | u32 pdu_format_errors; | ||
30 | u32 last_sess_failure_type; | ||
31 | char last_sess_fail_rem_name[224]; | ||
32 | } ____cacheline_aligned; | ||
33 | |||
34 | /* iSCSI login failure types (sub oids) */ | ||
35 | #define ISCSI_LOGIN_FAIL_OTHER 2 | ||
36 | #define ISCSI_LOGIN_FAIL_REDIRECT 3 | ||
37 | #define ISCSI_LOGIN_FAIL_AUTHORIZE 4 | ||
38 | #define ISCSI_LOGIN_FAIL_AUTHENTICATE 5 | ||
39 | #define ISCSI_LOGIN_FAIL_NEGOTIATE 6 | ||
40 | |||
41 | /* iSCSI login stats */ | ||
42 | struct iscsi_login_stats { | ||
43 | spinlock_t lock; | ||
44 | u32 accepts; | ||
45 | u32 other_fails; | ||
46 | u32 redirects; | ||
47 | u32 authorize_fails; | ||
48 | u32 authenticate_fails; | ||
49 | u32 negotiate_fails; /* used for notifications */ | ||
50 | u64 last_fail_time; /* time stamp (jiffies) */ | ||
51 | u32 last_fail_type; | ||
52 | int last_intr_fail_ip_family; | ||
53 | unsigned char last_intr_fail_ip_addr[IPV6_ADDRESS_SPACE]; | ||
54 | char last_intr_fail_name[224]; | ||
55 | } ____cacheline_aligned; | ||
56 | |||
57 | /* iSCSI logout stats */ | ||
58 | struct iscsi_logout_stats { | ||
59 | spinlock_t lock; | ||
60 | u32 normal_logouts; | ||
61 | u32 abnormal_logouts; | ||
62 | } ____cacheline_aligned; | ||
63 | |||
64 | #endif /*** ISCSI_TARGET_STAT_H ***/ | ||
diff --git a/drivers/target/iscsi/iscsi_target_tmr.c b/drivers/target/iscsi/iscsi_target_tmr.c new file mode 100644 index 000000000000..db1fe1ec84df --- /dev/null +++ b/drivers/target/iscsi/iscsi_target_tmr.c | |||
@@ -0,0 +1,849 @@ | |||
1 | /******************************************************************************* | ||
2 | * This file contains the iSCSI Target specific Task Management functions. | ||
3 | * | ||
4 | * \u00a9 Copyright 2007-2011 RisingTide Systems LLC. | ||
5 | * | ||
6 | * Licensed to the Linux Foundation under the General Public License (GPL) version 2. | ||
7 | * | ||
8 | * Author: Nicholas A. Bellinger <nab@linux-iscsi.org> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | ******************************************************************************/ | ||
20 | |||
21 | #include <asm/unaligned.h> | ||
22 | #include <scsi/iscsi_proto.h> | ||
23 | #include <target/target_core_base.h> | ||
24 | #include <target/target_core_transport.h> | ||
25 | |||
26 | #include "iscsi_target_core.h" | ||
27 | #include "iscsi_target_seq_pdu_list.h" | ||
28 | #include "iscsi_target_datain_values.h" | ||
29 | #include "iscsi_target_device.h" | ||
30 | #include "iscsi_target_erl0.h" | ||
31 | #include "iscsi_target_erl1.h" | ||
32 | #include "iscsi_target_erl2.h" | ||
33 | #include "iscsi_target_tmr.h" | ||
34 | #include "iscsi_target_tpg.h" | ||
35 | #include "iscsi_target_util.h" | ||
36 | #include "iscsi_target.h" | ||
37 | |||
38 | u8 iscsit_tmr_abort_task( | ||
39 | struct iscsi_cmd *cmd, | ||
40 | unsigned char *buf) | ||
41 | { | ||
42 | struct iscsi_cmd *ref_cmd; | ||
43 | struct iscsi_conn *conn = cmd->conn; | ||
44 | struct iscsi_tmr_req *tmr_req = cmd->tmr_req; | ||
45 | struct se_tmr_req *se_tmr = cmd->se_cmd.se_tmr_req; | ||
46 | struct iscsi_tm *hdr = (struct iscsi_tm *) buf; | ||
47 | |||
48 | ref_cmd = iscsit_find_cmd_from_itt(conn, hdr->rtt); | ||
49 | if (!ref_cmd) { | ||
50 | pr_err("Unable to locate RefTaskTag: 0x%08x on CID:" | ||
51 | " %hu.\n", hdr->rtt, conn->cid); | ||
52 | return ((hdr->refcmdsn >= conn->sess->exp_cmd_sn) && | ||
53 | (hdr->refcmdsn <= conn->sess->max_cmd_sn)) ? | ||
54 | ISCSI_TMF_RSP_COMPLETE : ISCSI_TMF_RSP_NO_TASK; | ||
55 | } | ||
56 | if (ref_cmd->cmd_sn != hdr->refcmdsn) { | ||
57 | pr_err("RefCmdSN 0x%08x does not equal" | ||
58 | " task's CmdSN 0x%08x. Rejecting ABORT_TASK.\n", | ||
59 | hdr->refcmdsn, ref_cmd->cmd_sn); | ||
60 | return ISCSI_TMF_RSP_REJECTED; | ||
61 | } | ||
62 | |||
63 | se_tmr->ref_task_tag = hdr->rtt; | ||
64 | se_tmr->ref_cmd = &ref_cmd->se_cmd; | ||
65 | tmr_req->ref_cmd_sn = hdr->refcmdsn; | ||
66 | tmr_req->exp_data_sn = hdr->exp_datasn; | ||
67 | |||
68 | return ISCSI_TMF_RSP_COMPLETE; | ||
69 | } | ||
70 | |||
71 | /* | ||
72 | * Called from iscsit_handle_task_mgt_cmd(). | ||
73 | */ | ||
74 | int iscsit_tmr_task_warm_reset( | ||
75 | struct iscsi_conn *conn, | ||
76 | struct iscsi_tmr_req *tmr_req, | ||
77 | unsigned char *buf) | ||
78 | { | ||
79 | struct iscsi_session *sess = conn->sess; | ||
80 | struct iscsi_node_attrib *na = iscsit_tpg_get_node_attrib(sess); | ||
81 | #if 0 | ||
82 | struct iscsi_init_task_mgt_cmnd *hdr = | ||
83 | (struct iscsi_init_task_mgt_cmnd *) buf; | ||
84 | #endif | ||
85 | if (!na->tmr_warm_reset) { | ||
86 | pr_err("TMR Opcode TARGET_WARM_RESET authorization" | ||
87 | " failed for Initiator Node: %s\n", | ||
88 | sess->se_sess->se_node_acl->initiatorname); | ||
89 | return -1; | ||
90 | } | ||
91 | /* | ||
92 | * Do the real work in transport_generic_do_tmr(). | ||
93 | */ | ||
94 | return 0; | ||
95 | } | ||
96 | |||
97 | int iscsit_tmr_task_cold_reset( | ||
98 | struct iscsi_conn *conn, | ||
99 | struct iscsi_tmr_req *tmr_req, | ||
100 | unsigned char *buf) | ||
101 | { | ||
102 | struct iscsi_session *sess = conn->sess; | ||
103 | struct iscsi_node_attrib *na = iscsit_tpg_get_node_attrib(sess); | ||
104 | |||
105 | if (!na->tmr_cold_reset) { | ||
106 | pr_err("TMR Opcode TARGET_COLD_RESET authorization" | ||
107 | " failed for Initiator Node: %s\n", | ||
108 | sess->se_sess->se_node_acl->initiatorname); | ||
109 | return -1; | ||
110 | } | ||
111 | /* | ||
112 | * Do the real work in transport_generic_do_tmr(). | ||
113 | */ | ||
114 | return 0; | ||
115 | } | ||
116 | |||
117 | u8 iscsit_tmr_task_reassign( | ||
118 | struct iscsi_cmd *cmd, | ||
119 | unsigned char *buf) | ||
120 | { | ||
121 | struct iscsi_cmd *ref_cmd = NULL; | ||
122 | struct iscsi_conn *conn = cmd->conn; | ||
123 | struct iscsi_conn_recovery *cr = NULL; | ||
124 | struct iscsi_tmr_req *tmr_req = cmd->tmr_req; | ||
125 | struct se_tmr_req *se_tmr = cmd->se_cmd.se_tmr_req; | ||
126 | struct iscsi_tm *hdr = (struct iscsi_tm *) buf; | ||
127 | int ret; | ||
128 | |||
129 | pr_debug("Got TASK_REASSIGN TMR ITT: 0x%08x," | ||
130 | " RefTaskTag: 0x%08x, ExpDataSN: 0x%08x, CID: %hu\n", | ||
131 | hdr->itt, hdr->rtt, hdr->exp_datasn, conn->cid); | ||
132 | |||
133 | if (conn->sess->sess_ops->ErrorRecoveryLevel != 2) { | ||
134 | pr_err("TMR TASK_REASSIGN not supported in ERL<2," | ||
135 | " ignoring request.\n"); | ||
136 | return ISCSI_TMF_RSP_NOT_SUPPORTED; | ||
137 | } | ||
138 | |||
139 | ret = iscsit_find_cmd_for_recovery(conn->sess, &ref_cmd, &cr, hdr->rtt); | ||
140 | if (ret == -2) { | ||
141 | pr_err("Command ITT: 0x%08x is still alligent to CID:" | ||
142 | " %hu\n", ref_cmd->init_task_tag, cr->cid); | ||
143 | return ISCSI_TMF_RSP_TASK_ALLEGIANT; | ||
144 | } else if (ret == -1) { | ||
145 | pr_err("Unable to locate RefTaskTag: 0x%08x in" | ||
146 | " connection recovery command list.\n", hdr->rtt); | ||
147 | return ISCSI_TMF_RSP_NO_TASK; | ||
148 | } | ||
149 | /* | ||
150 | * Temporary check to prevent connection recovery for | ||
151 | * connections with a differing MaxRecvDataSegmentLength. | ||
152 | */ | ||
153 | if (cr->maxrecvdatasegmentlength != | ||
154 | conn->conn_ops->MaxRecvDataSegmentLength) { | ||
155 | pr_err("Unable to perform connection recovery for" | ||
156 | " differing MaxRecvDataSegmentLength, rejecting" | ||
157 | " TMR TASK_REASSIGN.\n"); | ||
158 | return ISCSI_TMF_RSP_REJECTED; | ||
159 | } | ||
160 | |||
161 | se_tmr->ref_task_tag = hdr->rtt; | ||
162 | se_tmr->ref_cmd = &ref_cmd->se_cmd; | ||
163 | se_tmr->ref_task_lun = get_unaligned_le64(&hdr->lun); | ||
164 | tmr_req->ref_cmd_sn = hdr->refcmdsn; | ||
165 | tmr_req->exp_data_sn = hdr->exp_datasn; | ||
166 | tmr_req->conn_recovery = cr; | ||
167 | tmr_req->task_reassign = 1; | ||
168 | /* | ||
169 | * Command can now be reassigned to a new connection. | ||
170 | * The task management response must be sent before the | ||
171 | * reassignment actually happens. See iscsi_tmr_post_handler(). | ||
172 | */ | ||
173 | return ISCSI_TMF_RSP_COMPLETE; | ||
174 | } | ||
175 | |||
176 | static void iscsit_task_reassign_remove_cmd( | ||
177 | struct iscsi_cmd *cmd, | ||
178 | struct iscsi_conn_recovery *cr, | ||
179 | struct iscsi_session *sess) | ||
180 | { | ||
181 | int ret; | ||
182 | |||
183 | spin_lock(&cr->conn_recovery_cmd_lock); | ||
184 | ret = iscsit_remove_cmd_from_connection_recovery(cmd, sess); | ||
185 | spin_unlock(&cr->conn_recovery_cmd_lock); | ||
186 | if (!ret) { | ||
187 | pr_debug("iSCSI connection recovery successful for CID:" | ||
188 | " %hu on SID: %u\n", cr->cid, sess->sid); | ||
189 | iscsit_remove_active_connection_recovery_entry(cr, sess); | ||
190 | } | ||
191 | } | ||
192 | |||
193 | static int iscsit_task_reassign_complete_nop_out( | ||
194 | struct iscsi_tmr_req *tmr_req, | ||
195 | struct iscsi_conn *conn) | ||
196 | { | ||
197 | struct se_tmr_req *se_tmr = tmr_req->se_tmr_req; | ||
198 | struct se_cmd *se_cmd = se_tmr->ref_cmd; | ||
199 | struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd); | ||
200 | struct iscsi_conn_recovery *cr; | ||
201 | |||
202 | if (!cmd->cr) { | ||
203 | pr_err("struct iscsi_conn_recovery pointer for ITT: 0x%08x" | ||
204 | " is NULL!\n", cmd->init_task_tag); | ||
205 | return -1; | ||
206 | } | ||
207 | cr = cmd->cr; | ||
208 | |||
209 | /* | ||
210 | * Reset the StatSN so a new one for this commands new connection | ||
211 | * will be assigned. | ||
212 | * Reset the ExpStatSN as well so we may receive Status SNACKs. | ||
213 | */ | ||
214 | cmd->stat_sn = cmd->exp_stat_sn = 0; | ||
215 | |||
216 | iscsit_task_reassign_remove_cmd(cmd, cr, conn->sess); | ||
217 | |||
218 | spin_lock_bh(&conn->cmd_lock); | ||
219 | list_add_tail(&cmd->i_list, &conn->conn_cmd_list); | ||
220 | spin_unlock_bh(&conn->cmd_lock); | ||
221 | |||
222 | cmd->i_state = ISTATE_SEND_NOPIN; | ||
223 | iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state); | ||
224 | return 0; | ||
225 | } | ||
226 | |||
227 | static int iscsit_task_reassign_complete_write( | ||
228 | struct iscsi_cmd *cmd, | ||
229 | struct iscsi_tmr_req *tmr_req) | ||
230 | { | ||
231 | int no_build_r2ts = 0; | ||
232 | u32 length = 0, offset = 0; | ||
233 | struct iscsi_conn *conn = cmd->conn; | ||
234 | struct se_cmd *se_cmd = &cmd->se_cmd; | ||
235 | /* | ||
236 | * The Initiator must not send a R2T SNACK with a Begrun less than | ||
237 | * the TMR TASK_REASSIGN's ExpDataSN. | ||
238 | */ | ||
239 | if (!tmr_req->exp_data_sn) { | ||
240 | cmd->cmd_flags &= ~ICF_GOT_DATACK_SNACK; | ||
241 | cmd->acked_data_sn = 0; | ||
242 | } else { | ||
243 | cmd->cmd_flags |= ICF_GOT_DATACK_SNACK; | ||
244 | cmd->acked_data_sn = (tmr_req->exp_data_sn - 1); | ||
245 | } | ||
246 | |||
247 | /* | ||
248 | * The TMR TASK_REASSIGN's ExpDataSN contains the next R2TSN the | ||
249 | * Initiator is expecting. The Target controls all WRITE operations | ||
250 | * so if we have received all DataOUT we can safety ignore Initiator. | ||
251 | */ | ||
252 | if (cmd->cmd_flags & ICF_GOT_LAST_DATAOUT) { | ||
253 | if (!atomic_read(&cmd->transport_sent)) { | ||
254 | pr_debug("WRITE ITT: 0x%08x: t_state: %d" | ||
255 | " never sent to transport\n", | ||
256 | cmd->init_task_tag, cmd->se_cmd.t_state); | ||
257 | return transport_generic_handle_data(se_cmd); | ||
258 | } | ||
259 | |||
260 | cmd->i_state = ISTATE_SEND_STATUS; | ||
261 | iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state); | ||
262 | return 0; | ||
263 | } | ||
264 | |||
265 | /* | ||
266 | * Special case to deal with DataSequenceInOrder=No and Non-Immeidate | ||
267 | * Unsolicited DataOut. | ||
268 | */ | ||
269 | if (cmd->unsolicited_data) { | ||
270 | cmd->unsolicited_data = 0; | ||
271 | |||
272 | offset = cmd->next_burst_len = cmd->write_data_done; | ||
273 | |||
274 | if ((conn->sess->sess_ops->FirstBurstLength - offset) >= | ||
275 | cmd->data_length) { | ||
276 | no_build_r2ts = 1; | ||
277 | length = (cmd->data_length - offset); | ||
278 | } else | ||
279 | length = (conn->sess->sess_ops->FirstBurstLength - offset); | ||
280 | |||
281 | spin_lock_bh(&cmd->r2t_lock); | ||
282 | if (iscsit_add_r2t_to_list(cmd, offset, length, 0, 0) < 0) { | ||
283 | spin_unlock_bh(&cmd->r2t_lock); | ||
284 | return -1; | ||
285 | } | ||
286 | cmd->outstanding_r2ts++; | ||
287 | spin_unlock_bh(&cmd->r2t_lock); | ||
288 | |||
289 | if (no_build_r2ts) | ||
290 | return 0; | ||
291 | } | ||
292 | /* | ||
293 | * iscsit_build_r2ts_for_cmd() can handle the rest from here. | ||
294 | */ | ||
295 | return iscsit_build_r2ts_for_cmd(cmd, conn, 2); | ||
296 | } | ||
297 | |||
298 | static int iscsit_task_reassign_complete_read( | ||
299 | struct iscsi_cmd *cmd, | ||
300 | struct iscsi_tmr_req *tmr_req) | ||
301 | { | ||
302 | struct iscsi_conn *conn = cmd->conn; | ||
303 | struct iscsi_datain_req *dr; | ||
304 | struct se_cmd *se_cmd = &cmd->se_cmd; | ||
305 | /* | ||
306 | * The Initiator must not send a Data SNACK with a BegRun less than | ||
307 | * the TMR TASK_REASSIGN's ExpDataSN. | ||
308 | */ | ||
309 | if (!tmr_req->exp_data_sn) { | ||
310 | cmd->cmd_flags &= ~ICF_GOT_DATACK_SNACK; | ||
311 | cmd->acked_data_sn = 0; | ||
312 | } else { | ||
313 | cmd->cmd_flags |= ICF_GOT_DATACK_SNACK; | ||
314 | cmd->acked_data_sn = (tmr_req->exp_data_sn - 1); | ||
315 | } | ||
316 | |||
317 | if (!atomic_read(&cmd->transport_sent)) { | ||
318 | pr_debug("READ ITT: 0x%08x: t_state: %d never sent to" | ||
319 | " transport\n", cmd->init_task_tag, | ||
320 | cmd->se_cmd.t_state); | ||
321 | transport_generic_handle_cdb(se_cmd); | ||
322 | return 0; | ||
323 | } | ||
324 | |||
325 | if (!atomic_read(&se_cmd->t_transport_complete)) { | ||
326 | pr_err("READ ITT: 0x%08x: t_state: %d, never returned" | ||
327 | " from transport\n", cmd->init_task_tag, | ||
328 | cmd->se_cmd.t_state); | ||
329 | return -1; | ||
330 | } | ||
331 | |||
332 | dr = iscsit_allocate_datain_req(); | ||
333 | if (!dr) | ||
334 | return -1; | ||
335 | /* | ||
336 | * The TMR TASK_REASSIGN's ExpDataSN contains the next DataSN the | ||
337 | * Initiator is expecting. | ||
338 | */ | ||
339 | dr->data_sn = dr->begrun = tmr_req->exp_data_sn; | ||
340 | dr->runlength = 0; | ||
341 | dr->generate_recovery_values = 1; | ||
342 | dr->recovery = DATAIN_CONNECTION_RECOVERY; | ||
343 | |||
344 | iscsit_attach_datain_req(cmd, dr); | ||
345 | |||
346 | cmd->i_state = ISTATE_SEND_DATAIN; | ||
347 | iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state); | ||
348 | return 0; | ||
349 | } | ||
350 | |||
351 | static int iscsit_task_reassign_complete_none( | ||
352 | struct iscsi_cmd *cmd, | ||
353 | struct iscsi_tmr_req *tmr_req) | ||
354 | { | ||
355 | struct iscsi_conn *conn = cmd->conn; | ||
356 | |||
357 | cmd->i_state = ISTATE_SEND_STATUS; | ||
358 | iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state); | ||
359 | return 0; | ||
360 | } | ||
361 | |||
362 | static int iscsit_task_reassign_complete_scsi_cmnd( | ||
363 | struct iscsi_tmr_req *tmr_req, | ||
364 | struct iscsi_conn *conn) | ||
365 | { | ||
366 | struct se_tmr_req *se_tmr = tmr_req->se_tmr_req; | ||
367 | struct se_cmd *se_cmd = se_tmr->ref_cmd; | ||
368 | struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd); | ||
369 | struct iscsi_conn_recovery *cr; | ||
370 | |||
371 | if (!cmd->cr) { | ||
372 | pr_err("struct iscsi_conn_recovery pointer for ITT: 0x%08x" | ||
373 | " is NULL!\n", cmd->init_task_tag); | ||
374 | return -1; | ||
375 | } | ||
376 | cr = cmd->cr; | ||
377 | |||
378 | /* | ||
379 | * Reset the StatSN so a new one for this commands new connection | ||
380 | * will be assigned. | ||
381 | * Reset the ExpStatSN as well so we may receive Status SNACKs. | ||
382 | */ | ||
383 | cmd->stat_sn = cmd->exp_stat_sn = 0; | ||
384 | |||
385 | iscsit_task_reassign_remove_cmd(cmd, cr, conn->sess); | ||
386 | |||
387 | spin_lock_bh(&conn->cmd_lock); | ||
388 | list_add_tail(&cmd->i_list, &conn->conn_cmd_list); | ||
389 | spin_unlock_bh(&conn->cmd_lock); | ||
390 | |||
391 | if (se_cmd->se_cmd_flags & SCF_SENT_CHECK_CONDITION) { | ||
392 | cmd->i_state = ISTATE_SEND_STATUS; | ||
393 | iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state); | ||
394 | return 0; | ||
395 | } | ||
396 | |||
397 | switch (cmd->data_direction) { | ||
398 | case DMA_TO_DEVICE: | ||
399 | return iscsit_task_reassign_complete_write(cmd, tmr_req); | ||
400 | case DMA_FROM_DEVICE: | ||
401 | return iscsit_task_reassign_complete_read(cmd, tmr_req); | ||
402 | case DMA_NONE: | ||
403 | return iscsit_task_reassign_complete_none(cmd, tmr_req); | ||
404 | default: | ||
405 | pr_err("Unknown cmd->data_direction: 0x%02x\n", | ||
406 | cmd->data_direction); | ||
407 | return -1; | ||
408 | } | ||
409 | |||
410 | return 0; | ||
411 | } | ||
412 | |||
413 | static int iscsit_task_reassign_complete( | ||
414 | struct iscsi_tmr_req *tmr_req, | ||
415 | struct iscsi_conn *conn) | ||
416 | { | ||
417 | struct se_tmr_req *se_tmr = tmr_req->se_tmr_req; | ||
418 | struct se_cmd *se_cmd; | ||
419 | struct iscsi_cmd *cmd; | ||
420 | int ret = 0; | ||
421 | |||
422 | if (!se_tmr->ref_cmd) { | ||
423 | pr_err("TMR Request is missing a RefCmd struct iscsi_cmd.\n"); | ||
424 | return -1; | ||
425 | } | ||
426 | se_cmd = se_tmr->ref_cmd; | ||
427 | cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd); | ||
428 | |||
429 | cmd->conn = conn; | ||
430 | |||
431 | switch (cmd->iscsi_opcode) { | ||
432 | case ISCSI_OP_NOOP_OUT: | ||
433 | ret = iscsit_task_reassign_complete_nop_out(tmr_req, conn); | ||
434 | break; | ||
435 | case ISCSI_OP_SCSI_CMD: | ||
436 | ret = iscsit_task_reassign_complete_scsi_cmnd(tmr_req, conn); | ||
437 | break; | ||
438 | default: | ||
439 | pr_err("Illegal iSCSI Opcode 0x%02x during" | ||
440 | " command realligence\n", cmd->iscsi_opcode); | ||
441 | return -1; | ||
442 | } | ||
443 | |||
444 | if (ret != 0) | ||
445 | return ret; | ||
446 | |||
447 | pr_debug("Completed connection realligence for Opcode: 0x%02x," | ||
448 | " ITT: 0x%08x to CID: %hu.\n", cmd->iscsi_opcode, | ||
449 | cmd->init_task_tag, conn->cid); | ||
450 | |||
451 | return 0; | ||
452 | } | ||
453 | |||
454 | /* | ||
455 | * Handles special after-the-fact actions related to TMRs. | ||
456 | * Right now the only one that its really needed for is | ||
457 | * connection recovery releated TASK_REASSIGN. | ||
458 | */ | ||
459 | extern int iscsit_tmr_post_handler(struct iscsi_cmd *cmd, struct iscsi_conn *conn) | ||
460 | { | ||
461 | struct iscsi_tmr_req *tmr_req = cmd->tmr_req; | ||
462 | struct se_tmr_req *se_tmr = cmd->se_cmd.se_tmr_req; | ||
463 | |||
464 | if (tmr_req->task_reassign && | ||
465 | (se_tmr->response == ISCSI_TMF_RSP_COMPLETE)) | ||
466 | return iscsit_task_reassign_complete(tmr_req, conn); | ||
467 | |||
468 | return 0; | ||
469 | } | ||
470 | |||
471 | /* | ||
472 | * Nothing to do here, but leave it for good measure. :-) | ||
473 | */ | ||
474 | int iscsit_task_reassign_prepare_read( | ||
475 | struct iscsi_tmr_req *tmr_req, | ||
476 | struct iscsi_conn *conn) | ||
477 | { | ||
478 | return 0; | ||
479 | } | ||
480 | |||
481 | static void iscsit_task_reassign_prepare_unsolicited_dataout( | ||
482 | struct iscsi_cmd *cmd, | ||
483 | struct iscsi_conn *conn) | ||
484 | { | ||
485 | int i, j; | ||
486 | struct iscsi_pdu *pdu = NULL; | ||
487 | struct iscsi_seq *seq = NULL; | ||
488 | |||
489 | if (conn->sess->sess_ops->DataSequenceInOrder) { | ||
490 | cmd->data_sn = 0; | ||
491 | |||
492 | if (cmd->immediate_data) | ||
493 | cmd->r2t_offset += (cmd->first_burst_len - | ||
494 | cmd->seq_start_offset); | ||
495 | |||
496 | if (conn->sess->sess_ops->DataPDUInOrder) { | ||
497 | cmd->write_data_done -= (cmd->immediate_data) ? | ||
498 | (cmd->first_burst_len - | ||
499 | cmd->seq_start_offset) : | ||
500 | cmd->first_burst_len; | ||
501 | cmd->first_burst_len = 0; | ||
502 | return; | ||
503 | } | ||
504 | |||
505 | for (i = 0; i < cmd->pdu_count; i++) { | ||
506 | pdu = &cmd->pdu_list[i]; | ||
507 | |||
508 | if (pdu->status != ISCSI_PDU_RECEIVED_OK) | ||
509 | continue; | ||
510 | |||
511 | if ((pdu->offset >= cmd->seq_start_offset) && | ||
512 | ((pdu->offset + pdu->length) <= | ||
513 | cmd->seq_end_offset)) { | ||
514 | cmd->first_burst_len -= pdu->length; | ||
515 | cmd->write_data_done -= pdu->length; | ||
516 | pdu->status = ISCSI_PDU_NOT_RECEIVED; | ||
517 | } | ||
518 | } | ||
519 | } else { | ||
520 | for (i = 0; i < cmd->seq_count; i++) { | ||
521 | seq = &cmd->seq_list[i]; | ||
522 | |||
523 | if (seq->type != SEQTYPE_UNSOLICITED) | ||
524 | continue; | ||
525 | |||
526 | cmd->write_data_done -= | ||
527 | (seq->offset - seq->orig_offset); | ||
528 | cmd->first_burst_len = 0; | ||
529 | seq->data_sn = 0; | ||
530 | seq->offset = seq->orig_offset; | ||
531 | seq->next_burst_len = 0; | ||
532 | seq->status = DATAOUT_SEQUENCE_WITHIN_COMMAND_RECOVERY; | ||
533 | |||
534 | if (conn->sess->sess_ops->DataPDUInOrder) | ||
535 | continue; | ||
536 | |||
537 | for (j = 0; j < seq->pdu_count; j++) { | ||
538 | pdu = &cmd->pdu_list[j+seq->pdu_start]; | ||
539 | |||
540 | if (pdu->status != ISCSI_PDU_RECEIVED_OK) | ||
541 | continue; | ||
542 | |||
543 | pdu->status = ISCSI_PDU_NOT_RECEIVED; | ||
544 | } | ||
545 | } | ||
546 | } | ||
547 | } | ||
548 | |||
549 | int iscsit_task_reassign_prepare_write( | ||
550 | struct iscsi_tmr_req *tmr_req, | ||
551 | struct iscsi_conn *conn) | ||
552 | { | ||
553 | struct se_tmr_req *se_tmr = tmr_req->se_tmr_req; | ||
554 | struct se_cmd *se_cmd = se_tmr->ref_cmd; | ||
555 | struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd); | ||
556 | struct iscsi_pdu *pdu = NULL; | ||
557 | struct iscsi_r2t *r2t = NULL, *r2t_tmp; | ||
558 | int first_incomplete_r2t = 1, i = 0; | ||
559 | |||
560 | /* | ||
561 | * The command was in the process of receiving Unsolicited DataOUT when | ||
562 | * the connection failed. | ||
563 | */ | ||
564 | if (cmd->unsolicited_data) | ||
565 | iscsit_task_reassign_prepare_unsolicited_dataout(cmd, conn); | ||
566 | |||
567 | /* | ||
568 | * The Initiator is requesting R2Ts starting from zero, skip | ||
569 | * checking acknowledged R2Ts and start checking struct iscsi_r2ts | ||
570 | * greater than zero. | ||
571 | */ | ||
572 | if (!tmr_req->exp_data_sn) | ||
573 | goto drop_unacknowledged_r2ts; | ||
574 | |||
575 | /* | ||
576 | * We now check that the PDUs in DataOUT sequences below | ||
577 | * the TMR TASK_REASSIGN ExpDataSN (R2TSN the Initiator is | ||
578 | * expecting next) have all the DataOUT they require to complete | ||
579 | * the DataOUT sequence. First scan from R2TSN 0 to TMR | ||
580 | * TASK_REASSIGN ExpDataSN-1. | ||
581 | * | ||
582 | * If we have not received all DataOUT in question, we must | ||
583 | * make sure to make the appropriate changes to values in | ||
584 | * struct iscsi_cmd (and elsewhere depending on session parameters) | ||
585 | * so iscsit_build_r2ts_for_cmd() in iscsit_task_reassign_complete_write() | ||
586 | * will resend a new R2T for the DataOUT sequences in question. | ||
587 | */ | ||
588 | spin_lock_bh(&cmd->r2t_lock); | ||
589 | if (list_empty(&cmd->cmd_r2t_list)) { | ||
590 | spin_unlock_bh(&cmd->r2t_lock); | ||
591 | return -1; | ||
592 | } | ||
593 | |||
594 | list_for_each_entry(r2t, &cmd->cmd_r2t_list, r2t_list) { | ||
595 | |||
596 | if (r2t->r2t_sn >= tmr_req->exp_data_sn) | ||
597 | continue; | ||
598 | /* | ||
599 | * Safely ignore Recovery R2Ts and R2Ts that have completed | ||
600 | * DataOUT sequences. | ||
601 | */ | ||
602 | if (r2t->seq_complete) | ||
603 | continue; | ||
604 | |||
605 | if (r2t->recovery_r2t) | ||
606 | continue; | ||
607 | |||
608 | /* | ||
609 | * DataSequenceInOrder=Yes: | ||
610 | * | ||
611 | * Taking into account the iSCSI implementation requirement of | ||
612 | * MaxOutstandingR2T=1 while ErrorRecoveryLevel>0 and | ||
613 | * DataSequenceInOrder=Yes, we must take into consideration | ||
614 | * the following: | ||
615 | * | ||
616 | * DataSequenceInOrder=No: | ||
617 | * | ||
618 | * Taking into account that the Initiator controls the (possibly | ||
619 | * random) PDU Order in (possibly random) Sequence Order of | ||
620 | * DataOUT the target requests with R2Ts, we must take into | ||
621 | * consideration the following: | ||
622 | * | ||
623 | * DataPDUInOrder=Yes for DataSequenceInOrder=[Yes,No]: | ||
624 | * | ||
625 | * While processing non-complete R2T DataOUT sequence requests | ||
626 | * the Target will re-request only the total sequence length | ||
627 | * minus current received offset. This is because we must | ||
628 | * assume the initiator will continue sending DataOUT from the | ||
629 | * last PDU before the connection failed. | ||
630 | * | ||
631 | * DataPDUInOrder=No for DataSequenceInOrder=[Yes,No]: | ||
632 | * | ||
633 | * While processing non-complete R2T DataOUT sequence requests | ||
634 | * the Target will re-request the entire DataOUT sequence if | ||
635 | * any single PDU is missing from the sequence. This is because | ||
636 | * we have no logical method to determine the next PDU offset, | ||
637 | * and we must assume the Initiator will be sending any random | ||
638 | * PDU offset in the current sequence after TASK_REASSIGN | ||
639 | * has completed. | ||
640 | */ | ||
641 | if (conn->sess->sess_ops->DataSequenceInOrder) { | ||
642 | if (!first_incomplete_r2t) { | ||
643 | cmd->r2t_offset -= r2t->xfer_len; | ||
644 | goto next; | ||
645 | } | ||
646 | |||
647 | if (conn->sess->sess_ops->DataPDUInOrder) { | ||
648 | cmd->data_sn = 0; | ||
649 | cmd->r2t_offset -= (r2t->xfer_len - | ||
650 | cmd->next_burst_len); | ||
651 | first_incomplete_r2t = 0; | ||
652 | goto next; | ||
653 | } | ||
654 | |||
655 | cmd->data_sn = 0; | ||
656 | cmd->r2t_offset -= r2t->xfer_len; | ||
657 | |||
658 | for (i = 0; i < cmd->pdu_count; i++) { | ||
659 | pdu = &cmd->pdu_list[i]; | ||
660 | |||
661 | if (pdu->status != ISCSI_PDU_RECEIVED_OK) | ||
662 | continue; | ||
663 | |||
664 | if ((pdu->offset >= r2t->offset) && | ||
665 | (pdu->offset < (r2t->offset + | ||
666 | r2t->xfer_len))) { | ||
667 | cmd->next_burst_len -= pdu->length; | ||
668 | cmd->write_data_done -= pdu->length; | ||
669 | pdu->status = ISCSI_PDU_NOT_RECEIVED; | ||
670 | } | ||
671 | } | ||
672 | |||
673 | first_incomplete_r2t = 0; | ||
674 | } else { | ||
675 | struct iscsi_seq *seq; | ||
676 | |||
677 | seq = iscsit_get_seq_holder(cmd, r2t->offset, | ||
678 | r2t->xfer_len); | ||
679 | if (!seq) { | ||
680 | spin_unlock_bh(&cmd->r2t_lock); | ||
681 | return -1; | ||
682 | } | ||
683 | |||
684 | cmd->write_data_done -= | ||
685 | (seq->offset - seq->orig_offset); | ||
686 | seq->data_sn = 0; | ||
687 | seq->offset = seq->orig_offset; | ||
688 | seq->next_burst_len = 0; | ||
689 | seq->status = DATAOUT_SEQUENCE_WITHIN_COMMAND_RECOVERY; | ||
690 | |||
691 | cmd->seq_send_order--; | ||
692 | |||
693 | if (conn->sess->sess_ops->DataPDUInOrder) | ||
694 | goto next; | ||
695 | |||
696 | for (i = 0; i < seq->pdu_count; i++) { | ||
697 | pdu = &cmd->pdu_list[i+seq->pdu_start]; | ||
698 | |||
699 | if (pdu->status != ISCSI_PDU_RECEIVED_OK) | ||
700 | continue; | ||
701 | |||
702 | pdu->status = ISCSI_PDU_NOT_RECEIVED; | ||
703 | } | ||
704 | } | ||
705 | |||
706 | next: | ||
707 | cmd->outstanding_r2ts--; | ||
708 | } | ||
709 | spin_unlock_bh(&cmd->r2t_lock); | ||
710 | |||
711 | /* | ||
712 | * We now drop all unacknowledged R2Ts, ie: ExpDataSN from TMR | ||
713 | * TASK_REASSIGN to the last R2T in the list.. We are also careful | ||
714 | * to check that the Initiator is not requesting R2Ts for DataOUT | ||
715 | * sequences it has already completed. | ||
716 | * | ||
717 | * Free each R2T in question and adjust values in struct iscsi_cmd | ||
718 | * accordingly so iscsit_build_r2ts_for_cmd() do the rest of | ||
719 | * the work after the TMR TASK_REASSIGN Response is sent. | ||
720 | */ | ||
721 | drop_unacknowledged_r2ts: | ||
722 | |||
723 | cmd->cmd_flags &= ~ICF_SENT_LAST_R2T; | ||
724 | cmd->r2t_sn = tmr_req->exp_data_sn; | ||
725 | |||
726 | spin_lock_bh(&cmd->r2t_lock); | ||
727 | list_for_each_entry_safe(r2t, r2t_tmp, &cmd->cmd_r2t_list, r2t_list) { | ||
728 | /* | ||
729 | * Skip up to the R2T Sequence number provided by the | ||
730 | * iSCSI TASK_REASSIGN TMR | ||
731 | */ | ||
732 | if (r2t->r2t_sn < tmr_req->exp_data_sn) | ||
733 | continue; | ||
734 | |||
735 | if (r2t->seq_complete) { | ||
736 | pr_err("Initiator is requesting R2Ts from" | ||
737 | " R2TSN: 0x%08x, but R2TSN: 0x%08x, Offset: %u," | ||
738 | " Length: %u is already complete." | ||
739 | " BAD INITIATOR ERL=2 IMPLEMENTATION!\n", | ||
740 | tmr_req->exp_data_sn, r2t->r2t_sn, | ||
741 | r2t->offset, r2t->xfer_len); | ||
742 | spin_unlock_bh(&cmd->r2t_lock); | ||
743 | return -1; | ||
744 | } | ||
745 | |||
746 | if (r2t->recovery_r2t) { | ||
747 | iscsit_free_r2t(r2t, cmd); | ||
748 | continue; | ||
749 | } | ||
750 | |||
751 | /* DataSequenceInOrder=Yes: | ||
752 | * | ||
753 | * Taking into account the iSCSI implementation requirement of | ||
754 | * MaxOutstandingR2T=1 while ErrorRecoveryLevel>0 and | ||
755 | * DataSequenceInOrder=Yes, it's safe to subtract the R2Ts | ||
756 | * entire transfer length from the commands R2T offset marker. | ||
757 | * | ||
758 | * DataSequenceInOrder=No: | ||
759 | * | ||
760 | * We subtract the difference from struct iscsi_seq between the | ||
761 | * current offset and original offset from cmd->write_data_done | ||
762 | * for account for DataOUT PDUs already received. Then reset | ||
763 | * the current offset to the original and zero out the current | ||
764 | * burst length, to make sure we re-request the entire DataOUT | ||
765 | * sequence. | ||
766 | */ | ||
767 | if (conn->sess->sess_ops->DataSequenceInOrder) | ||
768 | cmd->r2t_offset -= r2t->xfer_len; | ||
769 | else | ||
770 | cmd->seq_send_order--; | ||
771 | |||
772 | cmd->outstanding_r2ts--; | ||
773 | iscsit_free_r2t(r2t, cmd); | ||
774 | } | ||
775 | spin_unlock_bh(&cmd->r2t_lock); | ||
776 | |||
777 | return 0; | ||
778 | } | ||
779 | |||
780 | /* | ||
781 | * Performs sanity checks TMR TASK_REASSIGN's ExpDataSN for | ||
782 | * a given struct iscsi_cmd. | ||
783 | */ | ||
784 | int iscsit_check_task_reassign_expdatasn( | ||
785 | struct iscsi_tmr_req *tmr_req, | ||
786 | struct iscsi_conn *conn) | ||
787 | { | ||
788 | struct se_tmr_req *se_tmr = tmr_req->se_tmr_req; | ||
789 | struct se_cmd *se_cmd = se_tmr->ref_cmd; | ||
790 | struct iscsi_cmd *ref_cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd); | ||
791 | |||
792 | if (ref_cmd->iscsi_opcode != ISCSI_OP_SCSI_CMD) | ||
793 | return 0; | ||
794 | |||
795 | if (se_cmd->se_cmd_flags & SCF_SENT_CHECK_CONDITION) | ||
796 | return 0; | ||
797 | |||
798 | if (ref_cmd->data_direction == DMA_NONE) | ||
799 | return 0; | ||
800 | |||
801 | /* | ||
802 | * For READs the TMR TASK_REASSIGNs ExpDataSN contains the next DataSN | ||
803 | * of DataIN the Initiator is expecting. | ||
804 | * | ||
805 | * Also check that the Initiator is not re-requesting DataIN that has | ||
806 | * already been acknowledged with a DataAck SNACK. | ||
807 | */ | ||
808 | if (ref_cmd->data_direction == DMA_FROM_DEVICE) { | ||
809 | if (tmr_req->exp_data_sn > ref_cmd->data_sn) { | ||
810 | pr_err("Received ExpDataSN: 0x%08x for READ" | ||
811 | " in TMR TASK_REASSIGN greater than command's" | ||
812 | " DataSN: 0x%08x.\n", tmr_req->exp_data_sn, | ||
813 | ref_cmd->data_sn); | ||
814 | return -1; | ||
815 | } | ||
816 | if ((ref_cmd->cmd_flags & ICF_GOT_DATACK_SNACK) && | ||
817 | (tmr_req->exp_data_sn <= ref_cmd->acked_data_sn)) { | ||
818 | pr_err("Received ExpDataSN: 0x%08x for READ" | ||
819 | " in TMR TASK_REASSIGN for previously" | ||
820 | " acknowledged DataIN: 0x%08x," | ||
821 | " protocol error\n", tmr_req->exp_data_sn, | ||
822 | ref_cmd->acked_data_sn); | ||
823 | return -1; | ||
824 | } | ||
825 | return iscsit_task_reassign_prepare_read(tmr_req, conn); | ||
826 | } | ||
827 | |||
828 | /* | ||
829 | * For WRITEs the TMR TASK_REASSIGNs ExpDataSN contains the next R2TSN | ||
830 | * for R2Ts the Initiator is expecting. | ||
831 | * | ||
832 | * Do the magic in iscsit_task_reassign_prepare_write(). | ||
833 | */ | ||
834 | if (ref_cmd->data_direction == DMA_TO_DEVICE) { | ||
835 | if (tmr_req->exp_data_sn > ref_cmd->r2t_sn) { | ||
836 | pr_err("Received ExpDataSN: 0x%08x for WRITE" | ||
837 | " in TMR TASK_REASSIGN greater than command's" | ||
838 | " R2TSN: 0x%08x.\n", tmr_req->exp_data_sn, | ||
839 | ref_cmd->r2t_sn); | ||
840 | return -1; | ||
841 | } | ||
842 | return iscsit_task_reassign_prepare_write(tmr_req, conn); | ||
843 | } | ||
844 | |||
845 | pr_err("Unknown iSCSI data_direction: 0x%02x\n", | ||
846 | ref_cmd->data_direction); | ||
847 | |||
848 | return -1; | ||
849 | } | ||
diff --git a/drivers/target/iscsi/iscsi_target_tmr.h b/drivers/target/iscsi/iscsi_target_tmr.h new file mode 100644 index 000000000000..142e992cb097 --- /dev/null +++ b/drivers/target/iscsi/iscsi_target_tmr.h | |||
@@ -0,0 +1,14 @@ | |||
1 | #ifndef ISCSI_TARGET_TMR_H | ||
2 | #define ISCSI_TARGET_TMR_H | ||
3 | |||
4 | extern u8 iscsit_tmr_abort_task(struct iscsi_cmd *, unsigned char *); | ||
5 | extern int iscsit_tmr_task_warm_reset(struct iscsi_conn *, struct iscsi_tmr_req *, | ||
6 | unsigned char *); | ||
7 | extern int iscsit_tmr_task_cold_reset(struct iscsi_conn *, struct iscsi_tmr_req *, | ||
8 | unsigned char *); | ||
9 | extern u8 iscsit_tmr_task_reassign(struct iscsi_cmd *, unsigned char *); | ||
10 | extern int iscsit_tmr_post_handler(struct iscsi_cmd *, struct iscsi_conn *); | ||
11 | extern int iscsit_check_task_reassign_expdatasn(struct iscsi_tmr_req *, | ||
12 | struct iscsi_conn *); | ||
13 | |||
14 | #endif /* ISCSI_TARGET_TMR_H */ | ||
diff --git a/drivers/target/iscsi/iscsi_target_tpg.c b/drivers/target/iscsi/iscsi_target_tpg.c new file mode 100644 index 000000000000..d4cf2cd25c44 --- /dev/null +++ b/drivers/target/iscsi/iscsi_target_tpg.c | |||
@@ -0,0 +1,759 @@ | |||
1 | /******************************************************************************* | ||
2 | * This file contains iSCSI Target Portal Group related functions. | ||
3 | * | ||
4 | * \u00a9 Copyright 2007-2011 RisingTide Systems LLC. | ||
5 | * | ||
6 | * Licensed to the Linux Foundation under the General Public License (GPL) version 2. | ||
7 | * | ||
8 | * Author: Nicholas A. Bellinger <nab@linux-iscsi.org> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | ******************************************************************************/ | ||
20 | |||
21 | #include <target/target_core_base.h> | ||
22 | #include <target/target_core_transport.h> | ||
23 | #include <target/target_core_fabric_ops.h> | ||
24 | #include <target/target_core_configfs.h> | ||
25 | #include <target/target_core_tpg.h> | ||
26 | |||
27 | #include "iscsi_target_core.h" | ||
28 | #include "iscsi_target_erl0.h" | ||
29 | #include "iscsi_target_login.h" | ||
30 | #include "iscsi_target_nodeattrib.h" | ||
31 | #include "iscsi_target_tpg.h" | ||
32 | #include "iscsi_target_util.h" | ||
33 | #include "iscsi_target.h" | ||
34 | #include "iscsi_target_parameters.h" | ||
35 | |||
36 | struct iscsi_portal_group *iscsit_alloc_portal_group(struct iscsi_tiqn *tiqn, u16 tpgt) | ||
37 | { | ||
38 | struct iscsi_portal_group *tpg; | ||
39 | |||
40 | tpg = kzalloc(sizeof(struct iscsi_portal_group), GFP_KERNEL); | ||
41 | if (!tpg) { | ||
42 | pr_err("Unable to allocate struct iscsi_portal_group\n"); | ||
43 | return NULL; | ||
44 | } | ||
45 | |||
46 | tpg->tpgt = tpgt; | ||
47 | tpg->tpg_state = TPG_STATE_FREE; | ||
48 | tpg->tpg_tiqn = tiqn; | ||
49 | INIT_LIST_HEAD(&tpg->tpg_gnp_list); | ||
50 | INIT_LIST_HEAD(&tpg->tpg_list); | ||
51 | mutex_init(&tpg->tpg_access_lock); | ||
52 | mutex_init(&tpg->np_login_lock); | ||
53 | spin_lock_init(&tpg->tpg_state_lock); | ||
54 | spin_lock_init(&tpg->tpg_np_lock); | ||
55 | |||
56 | return tpg; | ||
57 | } | ||
58 | |||
59 | static void iscsit_set_default_tpg_attribs(struct iscsi_portal_group *); | ||
60 | |||
61 | int iscsit_load_discovery_tpg(void) | ||
62 | { | ||
63 | struct iscsi_param *param; | ||
64 | struct iscsi_portal_group *tpg; | ||
65 | int ret; | ||
66 | |||
67 | tpg = iscsit_alloc_portal_group(NULL, 1); | ||
68 | if (!tpg) { | ||
69 | pr_err("Unable to allocate struct iscsi_portal_group\n"); | ||
70 | return -1; | ||
71 | } | ||
72 | |||
73 | ret = core_tpg_register( | ||
74 | &lio_target_fabric_configfs->tf_ops, | ||
75 | NULL, &tpg->tpg_se_tpg, (void *)tpg, | ||
76 | TRANSPORT_TPG_TYPE_DISCOVERY); | ||
77 | if (ret < 0) { | ||
78 | kfree(tpg); | ||
79 | return -1; | ||
80 | } | ||
81 | |||
82 | tpg->sid = 1; /* First Assigned LIO Session ID */ | ||
83 | iscsit_set_default_tpg_attribs(tpg); | ||
84 | |||
85 | if (iscsi_create_default_params(&tpg->param_list) < 0) | ||
86 | goto out; | ||
87 | /* | ||
88 | * By default we disable authentication for discovery sessions, | ||
89 | * this can be changed with: | ||
90 | * | ||
91 | * /sys/kernel/config/target/iscsi/discovery_auth/enforce_discovery_auth | ||
92 | */ | ||
93 | param = iscsi_find_param_from_key(AUTHMETHOD, tpg->param_list); | ||
94 | if (!param) | ||
95 | goto out; | ||
96 | |||
97 | if (iscsi_update_param_value(param, "CHAP,None") < 0) | ||
98 | goto out; | ||
99 | |||
100 | tpg->tpg_attrib.authentication = 0; | ||
101 | |||
102 | spin_lock(&tpg->tpg_state_lock); | ||
103 | tpg->tpg_state = TPG_STATE_ACTIVE; | ||
104 | spin_unlock(&tpg->tpg_state_lock); | ||
105 | |||
106 | iscsit_global->discovery_tpg = tpg; | ||
107 | pr_debug("CORE[0] - Allocated Discovery TPG\n"); | ||
108 | |||
109 | return 0; | ||
110 | out: | ||
111 | if (tpg->sid == 1) | ||
112 | core_tpg_deregister(&tpg->tpg_se_tpg); | ||
113 | kfree(tpg); | ||
114 | return -1; | ||
115 | } | ||
116 | |||
117 | void iscsit_release_discovery_tpg(void) | ||
118 | { | ||
119 | struct iscsi_portal_group *tpg = iscsit_global->discovery_tpg; | ||
120 | |||
121 | if (!tpg) | ||
122 | return; | ||
123 | |||
124 | core_tpg_deregister(&tpg->tpg_se_tpg); | ||
125 | |||
126 | kfree(tpg); | ||
127 | iscsit_global->discovery_tpg = NULL; | ||
128 | } | ||
129 | |||
130 | struct iscsi_portal_group *iscsit_get_tpg_from_np( | ||
131 | struct iscsi_tiqn *tiqn, | ||
132 | struct iscsi_np *np) | ||
133 | { | ||
134 | struct iscsi_portal_group *tpg = NULL; | ||
135 | struct iscsi_tpg_np *tpg_np; | ||
136 | |||
137 | spin_lock(&tiqn->tiqn_tpg_lock); | ||
138 | list_for_each_entry(tpg, &tiqn->tiqn_tpg_list, tpg_list) { | ||
139 | |||
140 | spin_lock(&tpg->tpg_state_lock); | ||
141 | if (tpg->tpg_state == TPG_STATE_FREE) { | ||
142 | spin_unlock(&tpg->tpg_state_lock); | ||
143 | continue; | ||
144 | } | ||
145 | spin_unlock(&tpg->tpg_state_lock); | ||
146 | |||
147 | spin_lock(&tpg->tpg_np_lock); | ||
148 | list_for_each_entry(tpg_np, &tpg->tpg_gnp_list, tpg_np_list) { | ||
149 | if (tpg_np->tpg_np == np) { | ||
150 | spin_unlock(&tpg->tpg_np_lock); | ||
151 | spin_unlock(&tiqn->tiqn_tpg_lock); | ||
152 | return tpg; | ||
153 | } | ||
154 | } | ||
155 | spin_unlock(&tpg->tpg_np_lock); | ||
156 | } | ||
157 | spin_unlock(&tiqn->tiqn_tpg_lock); | ||
158 | |||
159 | return NULL; | ||
160 | } | ||
161 | |||
162 | int iscsit_get_tpg( | ||
163 | struct iscsi_portal_group *tpg) | ||
164 | { | ||
165 | int ret; | ||
166 | |||
167 | ret = mutex_lock_interruptible(&tpg->tpg_access_lock); | ||
168 | return ((ret != 0) || signal_pending(current)) ? -1 : 0; | ||
169 | } | ||
170 | |||
171 | void iscsit_put_tpg(struct iscsi_portal_group *tpg) | ||
172 | { | ||
173 | mutex_unlock(&tpg->tpg_access_lock); | ||
174 | } | ||
175 | |||
176 | static void iscsit_clear_tpg_np_login_thread( | ||
177 | struct iscsi_tpg_np *tpg_np, | ||
178 | struct iscsi_portal_group *tpg) | ||
179 | { | ||
180 | if (!tpg_np->tpg_np) { | ||
181 | pr_err("struct iscsi_tpg_np->tpg_np is NULL!\n"); | ||
182 | return; | ||
183 | } | ||
184 | |||
185 | iscsit_reset_np_thread(tpg_np->tpg_np, tpg_np, tpg); | ||
186 | } | ||
187 | |||
188 | void iscsit_clear_tpg_np_login_threads( | ||
189 | struct iscsi_portal_group *tpg) | ||
190 | { | ||
191 | struct iscsi_tpg_np *tpg_np; | ||
192 | |||
193 | spin_lock(&tpg->tpg_np_lock); | ||
194 | list_for_each_entry(tpg_np, &tpg->tpg_gnp_list, tpg_np_list) { | ||
195 | if (!tpg_np->tpg_np) { | ||
196 | pr_err("struct iscsi_tpg_np->tpg_np is NULL!\n"); | ||
197 | continue; | ||
198 | } | ||
199 | spin_unlock(&tpg->tpg_np_lock); | ||
200 | iscsit_clear_tpg_np_login_thread(tpg_np, tpg); | ||
201 | spin_lock(&tpg->tpg_np_lock); | ||
202 | } | ||
203 | spin_unlock(&tpg->tpg_np_lock); | ||
204 | } | ||
205 | |||
206 | void iscsit_tpg_dump_params(struct iscsi_portal_group *tpg) | ||
207 | { | ||
208 | iscsi_print_params(tpg->param_list); | ||
209 | } | ||
210 | |||
211 | static void iscsit_set_default_tpg_attribs(struct iscsi_portal_group *tpg) | ||
212 | { | ||
213 | struct iscsi_tpg_attrib *a = &tpg->tpg_attrib; | ||
214 | |||
215 | a->authentication = TA_AUTHENTICATION; | ||
216 | a->login_timeout = TA_LOGIN_TIMEOUT; | ||
217 | a->netif_timeout = TA_NETIF_TIMEOUT; | ||
218 | a->default_cmdsn_depth = TA_DEFAULT_CMDSN_DEPTH; | ||
219 | a->generate_node_acls = TA_GENERATE_NODE_ACLS; | ||
220 | a->cache_dynamic_acls = TA_CACHE_DYNAMIC_ACLS; | ||
221 | a->demo_mode_write_protect = TA_DEMO_MODE_WRITE_PROTECT; | ||
222 | a->prod_mode_write_protect = TA_PROD_MODE_WRITE_PROTECT; | ||
223 | } | ||
224 | |||
225 | int iscsit_tpg_add_portal_group(struct iscsi_tiqn *tiqn, struct iscsi_portal_group *tpg) | ||
226 | { | ||
227 | if (tpg->tpg_state != TPG_STATE_FREE) { | ||
228 | pr_err("Unable to add iSCSI Target Portal Group: %d" | ||
229 | " while not in TPG_STATE_FREE state.\n", tpg->tpgt); | ||
230 | return -EEXIST; | ||
231 | } | ||
232 | iscsit_set_default_tpg_attribs(tpg); | ||
233 | |||
234 | if (iscsi_create_default_params(&tpg->param_list) < 0) | ||
235 | goto err_out; | ||
236 | |||
237 | ISCSI_TPG_ATTRIB(tpg)->tpg = tpg; | ||
238 | |||
239 | spin_lock(&tpg->tpg_state_lock); | ||
240 | tpg->tpg_state = TPG_STATE_INACTIVE; | ||
241 | spin_unlock(&tpg->tpg_state_lock); | ||
242 | |||
243 | spin_lock(&tiqn->tiqn_tpg_lock); | ||
244 | list_add_tail(&tpg->tpg_list, &tiqn->tiqn_tpg_list); | ||
245 | tiqn->tiqn_ntpgs++; | ||
246 | pr_debug("CORE[%s]_TPG[%hu] - Added iSCSI Target Portal Group\n", | ||
247 | tiqn->tiqn, tpg->tpgt); | ||
248 | spin_unlock(&tiqn->tiqn_tpg_lock); | ||
249 | |||
250 | return 0; | ||
251 | err_out: | ||
252 | if (tpg->param_list) { | ||
253 | iscsi_release_param_list(tpg->param_list); | ||
254 | tpg->param_list = NULL; | ||
255 | } | ||
256 | kfree(tpg); | ||
257 | return -ENOMEM; | ||
258 | } | ||
259 | |||
260 | int iscsit_tpg_del_portal_group( | ||
261 | struct iscsi_tiqn *tiqn, | ||
262 | struct iscsi_portal_group *tpg, | ||
263 | int force) | ||
264 | { | ||
265 | u8 old_state = tpg->tpg_state; | ||
266 | |||
267 | spin_lock(&tpg->tpg_state_lock); | ||
268 | tpg->tpg_state = TPG_STATE_INACTIVE; | ||
269 | spin_unlock(&tpg->tpg_state_lock); | ||
270 | |||
271 | if (iscsit_release_sessions_for_tpg(tpg, force) < 0) { | ||
272 | pr_err("Unable to delete iSCSI Target Portal Group:" | ||
273 | " %hu while active sessions exist, and force=0\n", | ||
274 | tpg->tpgt); | ||
275 | tpg->tpg_state = old_state; | ||
276 | return -EPERM; | ||
277 | } | ||
278 | |||
279 | core_tpg_clear_object_luns(&tpg->tpg_se_tpg); | ||
280 | |||
281 | if (tpg->param_list) { | ||
282 | iscsi_release_param_list(tpg->param_list); | ||
283 | tpg->param_list = NULL; | ||
284 | } | ||
285 | |||
286 | core_tpg_deregister(&tpg->tpg_se_tpg); | ||
287 | |||
288 | spin_lock(&tpg->tpg_state_lock); | ||
289 | tpg->tpg_state = TPG_STATE_FREE; | ||
290 | spin_unlock(&tpg->tpg_state_lock); | ||
291 | |||
292 | spin_lock(&tiqn->tiqn_tpg_lock); | ||
293 | tiqn->tiqn_ntpgs--; | ||
294 | list_del(&tpg->tpg_list); | ||
295 | spin_unlock(&tiqn->tiqn_tpg_lock); | ||
296 | |||
297 | pr_debug("CORE[%s]_TPG[%hu] - Deleted iSCSI Target Portal Group\n", | ||
298 | tiqn->tiqn, tpg->tpgt); | ||
299 | |||
300 | kfree(tpg); | ||
301 | return 0; | ||
302 | } | ||
303 | |||
304 | int iscsit_tpg_enable_portal_group(struct iscsi_portal_group *tpg) | ||
305 | { | ||
306 | struct iscsi_param *param; | ||
307 | struct iscsi_tiqn *tiqn = tpg->tpg_tiqn; | ||
308 | |||
309 | spin_lock(&tpg->tpg_state_lock); | ||
310 | if (tpg->tpg_state == TPG_STATE_ACTIVE) { | ||
311 | pr_err("iSCSI target portal group: %hu is already" | ||
312 | " active, ignoring request.\n", tpg->tpgt); | ||
313 | spin_unlock(&tpg->tpg_state_lock); | ||
314 | return -EINVAL; | ||
315 | } | ||
316 | /* | ||
317 | * Make sure that AuthMethod does not contain None as an option | ||
318 | * unless explictly disabled. Set the default to CHAP if authentication | ||
319 | * is enforced (as per default), and remove the NONE option. | ||
320 | */ | ||
321 | param = iscsi_find_param_from_key(AUTHMETHOD, tpg->param_list); | ||
322 | if (!param) { | ||
323 | spin_unlock(&tpg->tpg_state_lock); | ||
324 | return -ENOMEM; | ||
325 | } | ||
326 | |||
327 | if (ISCSI_TPG_ATTRIB(tpg)->authentication) { | ||
328 | if (!strcmp(param->value, NONE)) | ||
329 | if (iscsi_update_param_value(param, CHAP) < 0) { | ||
330 | spin_unlock(&tpg->tpg_state_lock); | ||
331 | return -ENOMEM; | ||
332 | } | ||
333 | if (iscsit_ta_authentication(tpg, 1) < 0) { | ||
334 | spin_unlock(&tpg->tpg_state_lock); | ||
335 | return -ENOMEM; | ||
336 | } | ||
337 | } | ||
338 | |||
339 | tpg->tpg_state = TPG_STATE_ACTIVE; | ||
340 | spin_unlock(&tpg->tpg_state_lock); | ||
341 | |||
342 | spin_lock(&tiqn->tiqn_tpg_lock); | ||
343 | tiqn->tiqn_active_tpgs++; | ||
344 | pr_debug("iSCSI_TPG[%hu] - Enabled iSCSI Target Portal Group\n", | ||
345 | tpg->tpgt); | ||
346 | spin_unlock(&tiqn->tiqn_tpg_lock); | ||
347 | |||
348 | return 0; | ||
349 | } | ||
350 | |||
351 | int iscsit_tpg_disable_portal_group(struct iscsi_portal_group *tpg, int force) | ||
352 | { | ||
353 | struct iscsi_tiqn *tiqn; | ||
354 | u8 old_state = tpg->tpg_state; | ||
355 | |||
356 | spin_lock(&tpg->tpg_state_lock); | ||
357 | if (tpg->tpg_state == TPG_STATE_INACTIVE) { | ||
358 | pr_err("iSCSI Target Portal Group: %hu is already" | ||
359 | " inactive, ignoring request.\n", tpg->tpgt); | ||
360 | spin_unlock(&tpg->tpg_state_lock); | ||
361 | return -EINVAL; | ||
362 | } | ||
363 | tpg->tpg_state = TPG_STATE_INACTIVE; | ||
364 | spin_unlock(&tpg->tpg_state_lock); | ||
365 | |||
366 | iscsit_clear_tpg_np_login_threads(tpg); | ||
367 | |||
368 | if (iscsit_release_sessions_for_tpg(tpg, force) < 0) { | ||
369 | spin_lock(&tpg->tpg_state_lock); | ||
370 | tpg->tpg_state = old_state; | ||
371 | spin_unlock(&tpg->tpg_state_lock); | ||
372 | pr_err("Unable to disable iSCSI Target Portal Group:" | ||
373 | " %hu while active sessions exist, and force=0\n", | ||
374 | tpg->tpgt); | ||
375 | return -EPERM; | ||
376 | } | ||
377 | |||
378 | tiqn = tpg->tpg_tiqn; | ||
379 | if (!tiqn || (tpg == iscsit_global->discovery_tpg)) | ||
380 | return 0; | ||
381 | |||
382 | spin_lock(&tiqn->tiqn_tpg_lock); | ||
383 | tiqn->tiqn_active_tpgs--; | ||
384 | pr_debug("iSCSI_TPG[%hu] - Disabled iSCSI Target Portal Group\n", | ||
385 | tpg->tpgt); | ||
386 | spin_unlock(&tiqn->tiqn_tpg_lock); | ||
387 | |||
388 | return 0; | ||
389 | } | ||
390 | |||
391 | struct iscsi_node_attrib *iscsit_tpg_get_node_attrib( | ||
392 | struct iscsi_session *sess) | ||
393 | { | ||
394 | struct se_session *se_sess = sess->se_sess; | ||
395 | struct se_node_acl *se_nacl = se_sess->se_node_acl; | ||
396 | struct iscsi_node_acl *acl = container_of(se_nacl, struct iscsi_node_acl, | ||
397 | se_node_acl); | ||
398 | |||
399 | return &acl->node_attrib; | ||
400 | } | ||
401 | |||
402 | struct iscsi_tpg_np *iscsit_tpg_locate_child_np( | ||
403 | struct iscsi_tpg_np *tpg_np, | ||
404 | int network_transport) | ||
405 | { | ||
406 | struct iscsi_tpg_np *tpg_np_child, *tpg_np_child_tmp; | ||
407 | |||
408 | spin_lock(&tpg_np->tpg_np_parent_lock); | ||
409 | list_for_each_entry_safe(tpg_np_child, tpg_np_child_tmp, | ||
410 | &tpg_np->tpg_np_parent_list, tpg_np_child_list) { | ||
411 | if (tpg_np_child->tpg_np->np_network_transport == | ||
412 | network_transport) { | ||
413 | spin_unlock(&tpg_np->tpg_np_parent_lock); | ||
414 | return tpg_np_child; | ||
415 | } | ||
416 | } | ||
417 | spin_unlock(&tpg_np->tpg_np_parent_lock); | ||
418 | |||
419 | return NULL; | ||
420 | } | ||
421 | |||
422 | struct iscsi_tpg_np *iscsit_tpg_add_network_portal( | ||
423 | struct iscsi_portal_group *tpg, | ||
424 | struct __kernel_sockaddr_storage *sockaddr, | ||
425 | char *ip_str, | ||
426 | struct iscsi_tpg_np *tpg_np_parent, | ||
427 | int network_transport) | ||
428 | { | ||
429 | struct iscsi_np *np; | ||
430 | struct iscsi_tpg_np *tpg_np; | ||
431 | |||
432 | tpg_np = kzalloc(sizeof(struct iscsi_tpg_np), GFP_KERNEL); | ||
433 | if (!tpg_np) { | ||
434 | pr_err("Unable to allocate memory for" | ||
435 | " struct iscsi_tpg_np.\n"); | ||
436 | return ERR_PTR(-ENOMEM); | ||
437 | } | ||
438 | |||
439 | np = iscsit_add_np(sockaddr, ip_str, network_transport); | ||
440 | if (IS_ERR(np)) { | ||
441 | kfree(tpg_np); | ||
442 | return ERR_CAST(np); | ||
443 | } | ||
444 | |||
445 | INIT_LIST_HEAD(&tpg_np->tpg_np_list); | ||
446 | INIT_LIST_HEAD(&tpg_np->tpg_np_child_list); | ||
447 | INIT_LIST_HEAD(&tpg_np->tpg_np_parent_list); | ||
448 | spin_lock_init(&tpg_np->tpg_np_parent_lock); | ||
449 | tpg_np->tpg_np = np; | ||
450 | tpg_np->tpg = tpg; | ||
451 | |||
452 | spin_lock(&tpg->tpg_np_lock); | ||
453 | list_add_tail(&tpg_np->tpg_np_list, &tpg->tpg_gnp_list); | ||
454 | tpg->num_tpg_nps++; | ||
455 | if (tpg->tpg_tiqn) | ||
456 | tpg->tpg_tiqn->tiqn_num_tpg_nps++; | ||
457 | spin_unlock(&tpg->tpg_np_lock); | ||
458 | |||
459 | if (tpg_np_parent) { | ||
460 | tpg_np->tpg_np_parent = tpg_np_parent; | ||
461 | spin_lock(&tpg_np_parent->tpg_np_parent_lock); | ||
462 | list_add_tail(&tpg_np->tpg_np_child_list, | ||
463 | &tpg_np_parent->tpg_np_parent_list); | ||
464 | spin_unlock(&tpg_np_parent->tpg_np_parent_lock); | ||
465 | } | ||
466 | |||
467 | pr_debug("CORE[%s] - Added Network Portal: %s:%hu,%hu on %s\n", | ||
468 | tpg->tpg_tiqn->tiqn, np->np_ip, np->np_port, tpg->tpgt, | ||
469 | (np->np_network_transport == ISCSI_TCP) ? "TCP" : "SCTP"); | ||
470 | |||
471 | return tpg_np; | ||
472 | } | ||
473 | |||
474 | static int iscsit_tpg_release_np( | ||
475 | struct iscsi_tpg_np *tpg_np, | ||
476 | struct iscsi_portal_group *tpg, | ||
477 | struct iscsi_np *np) | ||
478 | { | ||
479 | iscsit_clear_tpg_np_login_thread(tpg_np, tpg); | ||
480 | |||
481 | pr_debug("CORE[%s] - Removed Network Portal: %s:%hu,%hu on %s\n", | ||
482 | tpg->tpg_tiqn->tiqn, np->np_ip, np->np_port, tpg->tpgt, | ||
483 | (np->np_network_transport == ISCSI_TCP) ? "TCP" : "SCTP"); | ||
484 | |||
485 | tpg_np->tpg_np = NULL; | ||
486 | tpg_np->tpg = NULL; | ||
487 | kfree(tpg_np); | ||
488 | /* | ||
489 | * iscsit_del_np() will shutdown struct iscsi_np when last TPG reference is released. | ||
490 | */ | ||
491 | return iscsit_del_np(np); | ||
492 | } | ||
493 | |||
494 | int iscsit_tpg_del_network_portal( | ||
495 | struct iscsi_portal_group *tpg, | ||
496 | struct iscsi_tpg_np *tpg_np) | ||
497 | { | ||
498 | struct iscsi_np *np; | ||
499 | struct iscsi_tpg_np *tpg_np_child, *tpg_np_child_tmp; | ||
500 | int ret = 0; | ||
501 | |||
502 | np = tpg_np->tpg_np; | ||
503 | if (!np) { | ||
504 | pr_err("Unable to locate struct iscsi_np from" | ||
505 | " struct iscsi_tpg_np\n"); | ||
506 | return -EINVAL; | ||
507 | } | ||
508 | |||
509 | if (!tpg_np->tpg_np_parent) { | ||
510 | /* | ||
511 | * We are the parent tpg network portal. Release all of the | ||
512 | * child tpg_np's (eg: the non ISCSI_TCP ones) on our parent | ||
513 | * list first. | ||
514 | */ | ||
515 | list_for_each_entry_safe(tpg_np_child, tpg_np_child_tmp, | ||
516 | &tpg_np->tpg_np_parent_list, | ||
517 | tpg_np_child_list) { | ||
518 | ret = iscsit_tpg_del_network_portal(tpg, tpg_np_child); | ||
519 | if (ret < 0) | ||
520 | pr_err("iscsit_tpg_del_network_portal()" | ||
521 | " failed: %d\n", ret); | ||
522 | } | ||
523 | } else { | ||
524 | /* | ||
525 | * We are not the parent ISCSI_TCP tpg network portal. Release | ||
526 | * our own network portals from the child list. | ||
527 | */ | ||
528 | spin_lock(&tpg_np->tpg_np_parent->tpg_np_parent_lock); | ||
529 | list_del(&tpg_np->tpg_np_child_list); | ||
530 | spin_unlock(&tpg_np->tpg_np_parent->tpg_np_parent_lock); | ||
531 | } | ||
532 | |||
533 | spin_lock(&tpg->tpg_np_lock); | ||
534 | list_del(&tpg_np->tpg_np_list); | ||
535 | tpg->num_tpg_nps--; | ||
536 | if (tpg->tpg_tiqn) | ||
537 | tpg->tpg_tiqn->tiqn_num_tpg_nps--; | ||
538 | spin_unlock(&tpg->tpg_np_lock); | ||
539 | |||
540 | return iscsit_tpg_release_np(tpg_np, tpg, np); | ||
541 | } | ||
542 | |||
543 | int iscsit_tpg_set_initiator_node_queue_depth( | ||
544 | struct iscsi_portal_group *tpg, | ||
545 | unsigned char *initiatorname, | ||
546 | u32 queue_depth, | ||
547 | int force) | ||
548 | { | ||
549 | return core_tpg_set_initiator_node_queue_depth(&tpg->tpg_se_tpg, | ||
550 | initiatorname, queue_depth, force); | ||
551 | } | ||
552 | |||
553 | int iscsit_ta_authentication(struct iscsi_portal_group *tpg, u32 authentication) | ||
554 | { | ||
555 | unsigned char buf1[256], buf2[256], *none = NULL; | ||
556 | int len; | ||
557 | struct iscsi_param *param; | ||
558 | struct iscsi_tpg_attrib *a = &tpg->tpg_attrib; | ||
559 | |||
560 | if ((authentication != 1) && (authentication != 0)) { | ||
561 | pr_err("Illegal value for authentication parameter:" | ||
562 | " %u, ignoring request.\n", authentication); | ||
563 | return -1; | ||
564 | } | ||
565 | |||
566 | memset(buf1, 0, sizeof(buf1)); | ||
567 | memset(buf2, 0, sizeof(buf2)); | ||
568 | |||
569 | param = iscsi_find_param_from_key(AUTHMETHOD, tpg->param_list); | ||
570 | if (!param) | ||
571 | return -EINVAL; | ||
572 | |||
573 | if (authentication) { | ||
574 | snprintf(buf1, sizeof(buf1), "%s", param->value); | ||
575 | none = strstr(buf1, NONE); | ||
576 | if (!none) | ||
577 | goto out; | ||
578 | if (!strncmp(none + 4, ",", 1)) { | ||
579 | if (!strcmp(buf1, none)) | ||
580 | sprintf(buf2, "%s", none+5); | ||
581 | else { | ||
582 | none--; | ||
583 | *none = '\0'; | ||
584 | len = sprintf(buf2, "%s", buf1); | ||
585 | none += 5; | ||
586 | sprintf(buf2 + len, "%s", none); | ||
587 | } | ||
588 | } else { | ||
589 | none--; | ||
590 | *none = '\0'; | ||
591 | sprintf(buf2, "%s", buf1); | ||
592 | } | ||
593 | if (iscsi_update_param_value(param, buf2) < 0) | ||
594 | return -EINVAL; | ||
595 | } else { | ||
596 | snprintf(buf1, sizeof(buf1), "%s", param->value); | ||
597 | none = strstr(buf1, NONE); | ||
598 | if ((none)) | ||
599 | goto out; | ||
600 | strncat(buf1, ",", strlen(",")); | ||
601 | strncat(buf1, NONE, strlen(NONE)); | ||
602 | if (iscsi_update_param_value(param, buf1) < 0) | ||
603 | return -EINVAL; | ||
604 | } | ||
605 | |||
606 | out: | ||
607 | a->authentication = authentication; | ||
608 | pr_debug("%s iSCSI Authentication Methods for TPG: %hu.\n", | ||
609 | a->authentication ? "Enforcing" : "Disabling", tpg->tpgt); | ||
610 | |||
611 | return 0; | ||
612 | } | ||
613 | |||
614 | int iscsit_ta_login_timeout( | ||
615 | struct iscsi_portal_group *tpg, | ||
616 | u32 login_timeout) | ||
617 | { | ||
618 | struct iscsi_tpg_attrib *a = &tpg->tpg_attrib; | ||
619 | |||
620 | if (login_timeout > TA_LOGIN_TIMEOUT_MAX) { | ||
621 | pr_err("Requested Login Timeout %u larger than maximum" | ||
622 | " %u\n", login_timeout, TA_LOGIN_TIMEOUT_MAX); | ||
623 | return -EINVAL; | ||
624 | } else if (login_timeout < TA_LOGIN_TIMEOUT_MIN) { | ||
625 | pr_err("Requested Logout Timeout %u smaller than" | ||
626 | " minimum %u\n", login_timeout, TA_LOGIN_TIMEOUT_MIN); | ||
627 | return -EINVAL; | ||
628 | } | ||
629 | |||
630 | a->login_timeout = login_timeout; | ||
631 | pr_debug("Set Logout Timeout to %u for Target Portal Group" | ||
632 | " %hu\n", a->login_timeout, tpg->tpgt); | ||
633 | |||
634 | return 0; | ||
635 | } | ||
636 | |||
637 | int iscsit_ta_netif_timeout( | ||
638 | struct iscsi_portal_group *tpg, | ||
639 | u32 netif_timeout) | ||
640 | { | ||
641 | struct iscsi_tpg_attrib *a = &tpg->tpg_attrib; | ||
642 | |||
643 | if (netif_timeout > TA_NETIF_TIMEOUT_MAX) { | ||
644 | pr_err("Requested Network Interface Timeout %u larger" | ||
645 | " than maximum %u\n", netif_timeout, | ||
646 | TA_NETIF_TIMEOUT_MAX); | ||
647 | return -EINVAL; | ||
648 | } else if (netif_timeout < TA_NETIF_TIMEOUT_MIN) { | ||
649 | pr_err("Requested Network Interface Timeout %u smaller" | ||
650 | " than minimum %u\n", netif_timeout, | ||
651 | TA_NETIF_TIMEOUT_MIN); | ||
652 | return -EINVAL; | ||
653 | } | ||
654 | |||
655 | a->netif_timeout = netif_timeout; | ||
656 | pr_debug("Set Network Interface Timeout to %u for" | ||
657 | " Target Portal Group %hu\n", a->netif_timeout, tpg->tpgt); | ||
658 | |||
659 | return 0; | ||
660 | } | ||
661 | |||
662 | int iscsit_ta_generate_node_acls( | ||
663 | struct iscsi_portal_group *tpg, | ||
664 | u32 flag) | ||
665 | { | ||
666 | struct iscsi_tpg_attrib *a = &tpg->tpg_attrib; | ||
667 | |||
668 | if ((flag != 0) && (flag != 1)) { | ||
669 | pr_err("Illegal value %d\n", flag); | ||
670 | return -EINVAL; | ||
671 | } | ||
672 | |||
673 | a->generate_node_acls = flag; | ||
674 | pr_debug("iSCSI_TPG[%hu] - Generate Initiator Portal Group ACLs: %s\n", | ||
675 | tpg->tpgt, (a->generate_node_acls) ? "Enabled" : "Disabled"); | ||
676 | |||
677 | return 0; | ||
678 | } | ||
679 | |||
680 | int iscsit_ta_default_cmdsn_depth( | ||
681 | struct iscsi_portal_group *tpg, | ||
682 | u32 tcq_depth) | ||
683 | { | ||
684 | struct iscsi_tpg_attrib *a = &tpg->tpg_attrib; | ||
685 | |||
686 | if (tcq_depth > TA_DEFAULT_CMDSN_DEPTH_MAX) { | ||
687 | pr_err("Requested Default Queue Depth: %u larger" | ||
688 | " than maximum %u\n", tcq_depth, | ||
689 | TA_DEFAULT_CMDSN_DEPTH_MAX); | ||
690 | return -EINVAL; | ||
691 | } else if (tcq_depth < TA_DEFAULT_CMDSN_DEPTH_MIN) { | ||
692 | pr_err("Requested Default Queue Depth: %u smaller" | ||
693 | " than minimum %u\n", tcq_depth, | ||
694 | TA_DEFAULT_CMDSN_DEPTH_MIN); | ||
695 | return -EINVAL; | ||
696 | } | ||
697 | |||
698 | a->default_cmdsn_depth = tcq_depth; | ||
699 | pr_debug("iSCSI_TPG[%hu] - Set Default CmdSN TCQ Depth to %u\n", | ||
700 | tpg->tpgt, a->default_cmdsn_depth); | ||
701 | |||
702 | return 0; | ||
703 | } | ||
704 | |||
705 | int iscsit_ta_cache_dynamic_acls( | ||
706 | struct iscsi_portal_group *tpg, | ||
707 | u32 flag) | ||
708 | { | ||
709 | struct iscsi_tpg_attrib *a = &tpg->tpg_attrib; | ||
710 | |||
711 | if ((flag != 0) && (flag != 1)) { | ||
712 | pr_err("Illegal value %d\n", flag); | ||
713 | return -EINVAL; | ||
714 | } | ||
715 | |||
716 | a->cache_dynamic_acls = flag; | ||
717 | pr_debug("iSCSI_TPG[%hu] - Cache Dynamic Initiator Portal Group" | ||
718 | " ACLs %s\n", tpg->tpgt, (a->cache_dynamic_acls) ? | ||
719 | "Enabled" : "Disabled"); | ||
720 | |||
721 | return 0; | ||
722 | } | ||
723 | |||
724 | int iscsit_ta_demo_mode_write_protect( | ||
725 | struct iscsi_portal_group *tpg, | ||
726 | u32 flag) | ||
727 | { | ||
728 | struct iscsi_tpg_attrib *a = &tpg->tpg_attrib; | ||
729 | |||
730 | if ((flag != 0) && (flag != 1)) { | ||
731 | pr_err("Illegal value %d\n", flag); | ||
732 | return -EINVAL; | ||
733 | } | ||
734 | |||
735 | a->demo_mode_write_protect = flag; | ||
736 | pr_debug("iSCSI_TPG[%hu] - Demo Mode Write Protect bit: %s\n", | ||
737 | tpg->tpgt, (a->demo_mode_write_protect) ? "ON" : "OFF"); | ||
738 | |||
739 | return 0; | ||
740 | } | ||
741 | |||
742 | int iscsit_ta_prod_mode_write_protect( | ||
743 | struct iscsi_portal_group *tpg, | ||
744 | u32 flag) | ||
745 | { | ||
746 | struct iscsi_tpg_attrib *a = &tpg->tpg_attrib; | ||
747 | |||
748 | if ((flag != 0) && (flag != 1)) { | ||
749 | pr_err("Illegal value %d\n", flag); | ||
750 | return -EINVAL; | ||
751 | } | ||
752 | |||
753 | a->prod_mode_write_protect = flag; | ||
754 | pr_debug("iSCSI_TPG[%hu] - Production Mode Write Protect bit:" | ||
755 | " %s\n", tpg->tpgt, (a->prod_mode_write_protect) ? | ||
756 | "ON" : "OFF"); | ||
757 | |||
758 | return 0; | ||
759 | } | ||
diff --git a/drivers/target/iscsi/iscsi_target_tpg.h b/drivers/target/iscsi/iscsi_target_tpg.h new file mode 100644 index 000000000000..dda48c141a8c --- /dev/null +++ b/drivers/target/iscsi/iscsi_target_tpg.h | |||
@@ -0,0 +1,41 @@ | |||
1 | #ifndef ISCSI_TARGET_TPG_H | ||
2 | #define ISCSI_TARGET_TPG_H | ||
3 | |||
4 | extern struct iscsi_portal_group *iscsit_alloc_portal_group(struct iscsi_tiqn *, u16); | ||
5 | extern int iscsit_load_discovery_tpg(void); | ||
6 | extern void iscsit_release_discovery_tpg(void); | ||
7 | extern struct iscsi_portal_group *iscsit_get_tpg_from_np(struct iscsi_tiqn *, | ||
8 | struct iscsi_np *); | ||
9 | extern int iscsit_get_tpg(struct iscsi_portal_group *); | ||
10 | extern void iscsit_put_tpg(struct iscsi_portal_group *); | ||
11 | extern void iscsit_clear_tpg_np_login_threads(struct iscsi_portal_group *); | ||
12 | extern void iscsit_tpg_dump_params(struct iscsi_portal_group *); | ||
13 | extern int iscsit_tpg_add_portal_group(struct iscsi_tiqn *, struct iscsi_portal_group *); | ||
14 | extern int iscsit_tpg_del_portal_group(struct iscsi_tiqn *, struct iscsi_portal_group *, | ||
15 | int); | ||
16 | extern int iscsit_tpg_enable_portal_group(struct iscsi_portal_group *); | ||
17 | extern int iscsit_tpg_disable_portal_group(struct iscsi_portal_group *, int); | ||
18 | extern struct iscsi_node_acl *iscsit_tpg_add_initiator_node_acl( | ||
19 | struct iscsi_portal_group *, const char *, u32); | ||
20 | extern void iscsit_tpg_del_initiator_node_acl(struct iscsi_portal_group *, | ||
21 | struct se_node_acl *); | ||
22 | extern struct iscsi_node_attrib *iscsit_tpg_get_node_attrib(struct iscsi_session *); | ||
23 | extern void iscsit_tpg_del_external_nps(struct iscsi_tpg_np *); | ||
24 | extern struct iscsi_tpg_np *iscsit_tpg_locate_child_np(struct iscsi_tpg_np *, int); | ||
25 | extern struct iscsi_tpg_np *iscsit_tpg_add_network_portal(struct iscsi_portal_group *, | ||
26 | struct __kernel_sockaddr_storage *, char *, struct iscsi_tpg_np *, | ||
27 | int); | ||
28 | extern int iscsit_tpg_del_network_portal(struct iscsi_portal_group *, | ||
29 | struct iscsi_tpg_np *); | ||
30 | extern int iscsit_tpg_set_initiator_node_queue_depth(struct iscsi_portal_group *, | ||
31 | unsigned char *, u32, int); | ||
32 | extern int iscsit_ta_authentication(struct iscsi_portal_group *, u32); | ||
33 | extern int iscsit_ta_login_timeout(struct iscsi_portal_group *, u32); | ||
34 | extern int iscsit_ta_netif_timeout(struct iscsi_portal_group *, u32); | ||
35 | extern int iscsit_ta_generate_node_acls(struct iscsi_portal_group *, u32); | ||
36 | extern int iscsit_ta_default_cmdsn_depth(struct iscsi_portal_group *, u32); | ||
37 | extern int iscsit_ta_cache_dynamic_acls(struct iscsi_portal_group *, u32); | ||
38 | extern int iscsit_ta_demo_mode_write_protect(struct iscsi_portal_group *, u32); | ||
39 | extern int iscsit_ta_prod_mode_write_protect(struct iscsi_portal_group *, u32); | ||
40 | |||
41 | #endif /* ISCSI_TARGET_TPG_H */ | ||
diff --git a/drivers/target/iscsi/iscsi_target_tq.c b/drivers/target/iscsi/iscsi_target_tq.c new file mode 100644 index 000000000000..0baac5bcebd4 --- /dev/null +++ b/drivers/target/iscsi/iscsi_target_tq.c | |||
@@ -0,0 +1,551 @@ | |||
1 | /******************************************************************************* | ||
2 | * This file contains the iSCSI Login Thread and Thread Queue functions. | ||
3 | * | ||
4 | * \u00a9 Copyright 2007-2011 RisingTide Systems LLC. | ||
5 | * | ||
6 | * Licensed to the Linux Foundation under the General Public License (GPL) version 2. | ||
7 | * | ||
8 | * Author: Nicholas A. Bellinger <nab@linux-iscsi.org> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | ******************************************************************************/ | ||
20 | |||
21 | #include <linux/kthread.h> | ||
22 | #include <linux/list.h> | ||
23 | #include <linux/bitmap.h> | ||
24 | |||
25 | #include "iscsi_target_core.h" | ||
26 | #include "iscsi_target_tq.h" | ||
27 | #include "iscsi_target.h" | ||
28 | |||
29 | static LIST_HEAD(active_ts_list); | ||
30 | static LIST_HEAD(inactive_ts_list); | ||
31 | static DEFINE_SPINLOCK(active_ts_lock); | ||
32 | static DEFINE_SPINLOCK(inactive_ts_lock); | ||
33 | static DEFINE_SPINLOCK(ts_bitmap_lock); | ||
34 | |||
35 | static void iscsi_add_ts_to_active_list(struct iscsi_thread_set *ts) | ||
36 | { | ||
37 | spin_lock(&active_ts_lock); | ||
38 | list_add_tail(&ts->ts_list, &active_ts_list); | ||
39 | iscsit_global->active_ts++; | ||
40 | spin_unlock(&active_ts_lock); | ||
41 | } | ||
42 | |||
43 | extern void iscsi_add_ts_to_inactive_list(struct iscsi_thread_set *ts) | ||
44 | { | ||
45 | spin_lock(&inactive_ts_lock); | ||
46 | list_add_tail(&ts->ts_list, &inactive_ts_list); | ||
47 | iscsit_global->inactive_ts++; | ||
48 | spin_unlock(&inactive_ts_lock); | ||
49 | } | ||
50 | |||
51 | static void iscsi_del_ts_from_active_list(struct iscsi_thread_set *ts) | ||
52 | { | ||
53 | spin_lock(&active_ts_lock); | ||
54 | list_del(&ts->ts_list); | ||
55 | iscsit_global->active_ts--; | ||
56 | spin_unlock(&active_ts_lock); | ||
57 | } | ||
58 | |||
59 | static struct iscsi_thread_set *iscsi_get_ts_from_inactive_list(void) | ||
60 | { | ||
61 | struct iscsi_thread_set *ts; | ||
62 | |||
63 | spin_lock(&inactive_ts_lock); | ||
64 | if (list_empty(&inactive_ts_list)) { | ||
65 | spin_unlock(&inactive_ts_lock); | ||
66 | return NULL; | ||
67 | } | ||
68 | |||
69 | list_for_each_entry(ts, &inactive_ts_list, ts_list) | ||
70 | break; | ||
71 | |||
72 | list_del(&ts->ts_list); | ||
73 | iscsit_global->inactive_ts--; | ||
74 | spin_unlock(&inactive_ts_lock); | ||
75 | |||
76 | return ts; | ||
77 | } | ||
78 | |||
79 | extern int iscsi_allocate_thread_sets(u32 thread_pair_count) | ||
80 | { | ||
81 | int allocated_thread_pair_count = 0, i, thread_id; | ||
82 | struct iscsi_thread_set *ts = NULL; | ||
83 | |||
84 | for (i = 0; i < thread_pair_count; i++) { | ||
85 | ts = kzalloc(sizeof(struct iscsi_thread_set), GFP_KERNEL); | ||
86 | if (!ts) { | ||
87 | pr_err("Unable to allocate memory for" | ||
88 | " thread set.\n"); | ||
89 | return allocated_thread_pair_count; | ||
90 | } | ||
91 | /* | ||
92 | * Locate the next available regision in the thread_set_bitmap | ||
93 | */ | ||
94 | spin_lock(&ts_bitmap_lock); | ||
95 | thread_id = bitmap_find_free_region(iscsit_global->ts_bitmap, | ||
96 | iscsit_global->ts_bitmap_count, get_order(1)); | ||
97 | spin_unlock(&ts_bitmap_lock); | ||
98 | if (thread_id < 0) { | ||
99 | pr_err("bitmap_find_free_region() failed for" | ||
100 | " thread_set_bitmap\n"); | ||
101 | kfree(ts); | ||
102 | return allocated_thread_pair_count; | ||
103 | } | ||
104 | |||
105 | ts->thread_id = thread_id; | ||
106 | ts->status = ISCSI_THREAD_SET_FREE; | ||
107 | INIT_LIST_HEAD(&ts->ts_list); | ||
108 | spin_lock_init(&ts->ts_state_lock); | ||
109 | init_completion(&ts->rx_post_start_comp); | ||
110 | init_completion(&ts->tx_post_start_comp); | ||
111 | init_completion(&ts->rx_restart_comp); | ||
112 | init_completion(&ts->tx_restart_comp); | ||
113 | init_completion(&ts->rx_start_comp); | ||
114 | init_completion(&ts->tx_start_comp); | ||
115 | |||
116 | ts->create_threads = 1; | ||
117 | ts->tx_thread = kthread_run(iscsi_target_tx_thread, ts, "%s", | ||
118 | ISCSI_TX_THREAD_NAME); | ||
119 | if (IS_ERR(ts->tx_thread)) { | ||
120 | dump_stack(); | ||
121 | pr_err("Unable to start iscsi_target_tx_thread\n"); | ||
122 | break; | ||
123 | } | ||
124 | |||
125 | ts->rx_thread = kthread_run(iscsi_target_rx_thread, ts, "%s", | ||
126 | ISCSI_RX_THREAD_NAME); | ||
127 | if (IS_ERR(ts->rx_thread)) { | ||
128 | kthread_stop(ts->tx_thread); | ||
129 | pr_err("Unable to start iscsi_target_rx_thread\n"); | ||
130 | break; | ||
131 | } | ||
132 | ts->create_threads = 0; | ||
133 | |||
134 | iscsi_add_ts_to_inactive_list(ts); | ||
135 | allocated_thread_pair_count++; | ||
136 | } | ||
137 | |||
138 | pr_debug("Spawned %d thread set(s) (%d total threads).\n", | ||
139 | allocated_thread_pair_count, allocated_thread_pair_count * 2); | ||
140 | return allocated_thread_pair_count; | ||
141 | } | ||
142 | |||
143 | extern void iscsi_deallocate_thread_sets(void) | ||
144 | { | ||
145 | u32 released_count = 0; | ||
146 | struct iscsi_thread_set *ts = NULL; | ||
147 | |||
148 | while ((ts = iscsi_get_ts_from_inactive_list())) { | ||
149 | |||
150 | spin_lock_bh(&ts->ts_state_lock); | ||
151 | ts->status = ISCSI_THREAD_SET_DIE; | ||
152 | spin_unlock_bh(&ts->ts_state_lock); | ||
153 | |||
154 | if (ts->rx_thread) { | ||
155 | send_sig(SIGINT, ts->rx_thread, 1); | ||
156 | kthread_stop(ts->rx_thread); | ||
157 | } | ||
158 | if (ts->tx_thread) { | ||
159 | send_sig(SIGINT, ts->tx_thread, 1); | ||
160 | kthread_stop(ts->tx_thread); | ||
161 | } | ||
162 | /* | ||
163 | * Release this thread_id in the thread_set_bitmap | ||
164 | */ | ||
165 | spin_lock(&ts_bitmap_lock); | ||
166 | bitmap_release_region(iscsit_global->ts_bitmap, | ||
167 | ts->thread_id, get_order(1)); | ||
168 | spin_unlock(&ts_bitmap_lock); | ||
169 | |||
170 | released_count++; | ||
171 | kfree(ts); | ||
172 | } | ||
173 | |||
174 | if (released_count) | ||
175 | pr_debug("Stopped %d thread set(s) (%d total threads)." | ||
176 | "\n", released_count, released_count * 2); | ||
177 | } | ||
178 | |||
179 | static void iscsi_deallocate_extra_thread_sets(void) | ||
180 | { | ||
181 | u32 orig_count, released_count = 0; | ||
182 | struct iscsi_thread_set *ts = NULL; | ||
183 | |||
184 | orig_count = TARGET_THREAD_SET_COUNT; | ||
185 | |||
186 | while ((iscsit_global->inactive_ts + 1) > orig_count) { | ||
187 | ts = iscsi_get_ts_from_inactive_list(); | ||
188 | if (!ts) | ||
189 | break; | ||
190 | |||
191 | spin_lock_bh(&ts->ts_state_lock); | ||
192 | ts->status = ISCSI_THREAD_SET_DIE; | ||
193 | spin_unlock_bh(&ts->ts_state_lock); | ||
194 | |||
195 | if (ts->rx_thread) { | ||
196 | send_sig(SIGINT, ts->rx_thread, 1); | ||
197 | kthread_stop(ts->rx_thread); | ||
198 | } | ||
199 | if (ts->tx_thread) { | ||
200 | send_sig(SIGINT, ts->tx_thread, 1); | ||
201 | kthread_stop(ts->tx_thread); | ||
202 | } | ||
203 | /* | ||
204 | * Release this thread_id in the thread_set_bitmap | ||
205 | */ | ||
206 | spin_lock(&ts_bitmap_lock); | ||
207 | bitmap_release_region(iscsit_global->ts_bitmap, | ||
208 | ts->thread_id, get_order(1)); | ||
209 | spin_unlock(&ts_bitmap_lock); | ||
210 | |||
211 | released_count++; | ||
212 | kfree(ts); | ||
213 | } | ||
214 | |||
215 | if (released_count) { | ||
216 | pr_debug("Stopped %d thread set(s) (%d total threads)." | ||
217 | "\n", released_count, released_count * 2); | ||
218 | } | ||
219 | } | ||
220 | |||
221 | void iscsi_activate_thread_set(struct iscsi_conn *conn, struct iscsi_thread_set *ts) | ||
222 | { | ||
223 | iscsi_add_ts_to_active_list(ts); | ||
224 | |||
225 | spin_lock_bh(&ts->ts_state_lock); | ||
226 | conn->thread_set = ts; | ||
227 | ts->conn = conn; | ||
228 | spin_unlock_bh(&ts->ts_state_lock); | ||
229 | /* | ||
230 | * Start up the RX thread and wait on rx_post_start_comp. The RX | ||
231 | * Thread will then do the same for the TX Thread in | ||
232 | * iscsi_rx_thread_pre_handler(). | ||
233 | */ | ||
234 | complete(&ts->rx_start_comp); | ||
235 | wait_for_completion(&ts->rx_post_start_comp); | ||
236 | } | ||
237 | |||
238 | struct iscsi_thread_set *iscsi_get_thread_set(void) | ||
239 | { | ||
240 | int allocate_ts = 0; | ||
241 | struct completion comp; | ||
242 | struct iscsi_thread_set *ts = NULL; | ||
243 | /* | ||
244 | * If no inactive thread set is available on the first call to | ||
245 | * iscsi_get_ts_from_inactive_list(), sleep for a second and | ||
246 | * try again. If still none are available after two attempts, | ||
247 | * allocate a set ourselves. | ||
248 | */ | ||
249 | get_set: | ||
250 | ts = iscsi_get_ts_from_inactive_list(); | ||
251 | if (!ts) { | ||
252 | if (allocate_ts == 2) | ||
253 | iscsi_allocate_thread_sets(1); | ||
254 | |||
255 | init_completion(&comp); | ||
256 | wait_for_completion_timeout(&comp, 1 * HZ); | ||
257 | |||
258 | allocate_ts++; | ||
259 | goto get_set; | ||
260 | } | ||
261 | |||
262 | ts->delay_inactive = 1; | ||
263 | ts->signal_sent = 0; | ||
264 | ts->thread_count = 2; | ||
265 | init_completion(&ts->rx_restart_comp); | ||
266 | init_completion(&ts->tx_restart_comp); | ||
267 | |||
268 | return ts; | ||
269 | } | ||
270 | |||
271 | void iscsi_set_thread_clear(struct iscsi_conn *conn, u8 thread_clear) | ||
272 | { | ||
273 | struct iscsi_thread_set *ts = NULL; | ||
274 | |||
275 | if (!conn->thread_set) { | ||
276 | pr_err("struct iscsi_conn->thread_set is NULL\n"); | ||
277 | return; | ||
278 | } | ||
279 | ts = conn->thread_set; | ||
280 | |||
281 | spin_lock_bh(&ts->ts_state_lock); | ||
282 | ts->thread_clear &= ~thread_clear; | ||
283 | |||
284 | if ((thread_clear & ISCSI_CLEAR_RX_THREAD) && | ||
285 | (ts->blocked_threads & ISCSI_BLOCK_RX_THREAD)) | ||
286 | complete(&ts->rx_restart_comp); | ||
287 | else if ((thread_clear & ISCSI_CLEAR_TX_THREAD) && | ||
288 | (ts->blocked_threads & ISCSI_BLOCK_TX_THREAD)) | ||
289 | complete(&ts->tx_restart_comp); | ||
290 | spin_unlock_bh(&ts->ts_state_lock); | ||
291 | } | ||
292 | |||
293 | void iscsi_set_thread_set_signal(struct iscsi_conn *conn, u8 signal_sent) | ||
294 | { | ||
295 | struct iscsi_thread_set *ts = NULL; | ||
296 | |||
297 | if (!conn->thread_set) { | ||
298 | pr_err("struct iscsi_conn->thread_set is NULL\n"); | ||
299 | return; | ||
300 | } | ||
301 | ts = conn->thread_set; | ||
302 | |||
303 | spin_lock_bh(&ts->ts_state_lock); | ||
304 | ts->signal_sent |= signal_sent; | ||
305 | spin_unlock_bh(&ts->ts_state_lock); | ||
306 | } | ||
307 | |||
308 | int iscsi_release_thread_set(struct iscsi_conn *conn) | ||
309 | { | ||
310 | int thread_called = 0; | ||
311 | struct iscsi_thread_set *ts = NULL; | ||
312 | |||
313 | if (!conn || !conn->thread_set) { | ||
314 | pr_err("connection or thread set pointer is NULL\n"); | ||
315 | BUG(); | ||
316 | } | ||
317 | ts = conn->thread_set; | ||
318 | |||
319 | spin_lock_bh(&ts->ts_state_lock); | ||
320 | ts->status = ISCSI_THREAD_SET_RESET; | ||
321 | |||
322 | if (!strncmp(current->comm, ISCSI_RX_THREAD_NAME, | ||
323 | strlen(ISCSI_RX_THREAD_NAME))) | ||
324 | thread_called = ISCSI_RX_THREAD; | ||
325 | else if (!strncmp(current->comm, ISCSI_TX_THREAD_NAME, | ||
326 | strlen(ISCSI_TX_THREAD_NAME))) | ||
327 | thread_called = ISCSI_TX_THREAD; | ||
328 | |||
329 | if (ts->rx_thread && (thread_called == ISCSI_TX_THREAD) && | ||
330 | (ts->thread_clear & ISCSI_CLEAR_RX_THREAD)) { | ||
331 | |||
332 | if (!(ts->signal_sent & ISCSI_SIGNAL_RX_THREAD)) { | ||
333 | send_sig(SIGINT, ts->rx_thread, 1); | ||
334 | ts->signal_sent |= ISCSI_SIGNAL_RX_THREAD; | ||
335 | } | ||
336 | ts->blocked_threads |= ISCSI_BLOCK_RX_THREAD; | ||
337 | spin_unlock_bh(&ts->ts_state_lock); | ||
338 | wait_for_completion(&ts->rx_restart_comp); | ||
339 | spin_lock_bh(&ts->ts_state_lock); | ||
340 | ts->blocked_threads &= ~ISCSI_BLOCK_RX_THREAD; | ||
341 | } | ||
342 | if (ts->tx_thread && (thread_called == ISCSI_RX_THREAD) && | ||
343 | (ts->thread_clear & ISCSI_CLEAR_TX_THREAD)) { | ||
344 | |||
345 | if (!(ts->signal_sent & ISCSI_SIGNAL_TX_THREAD)) { | ||
346 | send_sig(SIGINT, ts->tx_thread, 1); | ||
347 | ts->signal_sent |= ISCSI_SIGNAL_TX_THREAD; | ||
348 | } | ||
349 | ts->blocked_threads |= ISCSI_BLOCK_TX_THREAD; | ||
350 | spin_unlock_bh(&ts->ts_state_lock); | ||
351 | wait_for_completion(&ts->tx_restart_comp); | ||
352 | spin_lock_bh(&ts->ts_state_lock); | ||
353 | ts->blocked_threads &= ~ISCSI_BLOCK_TX_THREAD; | ||
354 | } | ||
355 | |||
356 | ts->conn = NULL; | ||
357 | ts->status = ISCSI_THREAD_SET_FREE; | ||
358 | spin_unlock_bh(&ts->ts_state_lock); | ||
359 | |||
360 | return 0; | ||
361 | } | ||
362 | |||
363 | int iscsi_thread_set_force_reinstatement(struct iscsi_conn *conn) | ||
364 | { | ||
365 | struct iscsi_thread_set *ts; | ||
366 | |||
367 | if (!conn->thread_set) | ||
368 | return -1; | ||
369 | ts = conn->thread_set; | ||
370 | |||
371 | spin_lock_bh(&ts->ts_state_lock); | ||
372 | if (ts->status != ISCSI_THREAD_SET_ACTIVE) { | ||
373 | spin_unlock_bh(&ts->ts_state_lock); | ||
374 | return -1; | ||
375 | } | ||
376 | |||
377 | if (ts->tx_thread && (!(ts->signal_sent & ISCSI_SIGNAL_TX_THREAD))) { | ||
378 | send_sig(SIGINT, ts->tx_thread, 1); | ||
379 | ts->signal_sent |= ISCSI_SIGNAL_TX_THREAD; | ||
380 | } | ||
381 | if (ts->rx_thread && (!(ts->signal_sent & ISCSI_SIGNAL_RX_THREAD))) { | ||
382 | send_sig(SIGINT, ts->rx_thread, 1); | ||
383 | ts->signal_sent |= ISCSI_SIGNAL_RX_THREAD; | ||
384 | } | ||
385 | spin_unlock_bh(&ts->ts_state_lock); | ||
386 | |||
387 | return 0; | ||
388 | } | ||
389 | |||
390 | static void iscsi_check_to_add_additional_sets(void) | ||
391 | { | ||
392 | int thread_sets_add; | ||
393 | |||
394 | spin_lock(&inactive_ts_lock); | ||
395 | thread_sets_add = iscsit_global->inactive_ts; | ||
396 | spin_unlock(&inactive_ts_lock); | ||
397 | if (thread_sets_add == 1) | ||
398 | iscsi_allocate_thread_sets(1); | ||
399 | } | ||
400 | |||
401 | static int iscsi_signal_thread_pre_handler(struct iscsi_thread_set *ts) | ||
402 | { | ||
403 | spin_lock_bh(&ts->ts_state_lock); | ||
404 | if ((ts->status == ISCSI_THREAD_SET_DIE) || signal_pending(current)) { | ||
405 | spin_unlock_bh(&ts->ts_state_lock); | ||
406 | return -1; | ||
407 | } | ||
408 | spin_unlock_bh(&ts->ts_state_lock); | ||
409 | |||
410 | return 0; | ||
411 | } | ||
412 | |||
413 | struct iscsi_conn *iscsi_rx_thread_pre_handler(struct iscsi_thread_set *ts) | ||
414 | { | ||
415 | int ret; | ||
416 | |||
417 | spin_lock_bh(&ts->ts_state_lock); | ||
418 | if (ts->create_threads) { | ||
419 | spin_unlock_bh(&ts->ts_state_lock); | ||
420 | goto sleep; | ||
421 | } | ||
422 | |||
423 | flush_signals(current); | ||
424 | |||
425 | if (ts->delay_inactive && (--ts->thread_count == 0)) { | ||
426 | spin_unlock_bh(&ts->ts_state_lock); | ||
427 | iscsi_del_ts_from_active_list(ts); | ||
428 | |||
429 | if (!iscsit_global->in_shutdown) | ||
430 | iscsi_deallocate_extra_thread_sets(); | ||
431 | |||
432 | iscsi_add_ts_to_inactive_list(ts); | ||
433 | spin_lock_bh(&ts->ts_state_lock); | ||
434 | } | ||
435 | |||
436 | if ((ts->status == ISCSI_THREAD_SET_RESET) && | ||
437 | (ts->thread_clear & ISCSI_CLEAR_RX_THREAD)) | ||
438 | complete(&ts->rx_restart_comp); | ||
439 | |||
440 | ts->thread_clear &= ~ISCSI_CLEAR_RX_THREAD; | ||
441 | spin_unlock_bh(&ts->ts_state_lock); | ||
442 | sleep: | ||
443 | ret = wait_for_completion_interruptible(&ts->rx_start_comp); | ||
444 | if (ret != 0) | ||
445 | return NULL; | ||
446 | |||
447 | if (iscsi_signal_thread_pre_handler(ts) < 0) | ||
448 | return NULL; | ||
449 | |||
450 | if (!ts->conn) { | ||
451 | pr_err("struct iscsi_thread_set->conn is NULL for" | ||
452 | " thread_id: %d, going back to sleep\n", ts->thread_id); | ||
453 | goto sleep; | ||
454 | } | ||
455 | iscsi_check_to_add_additional_sets(); | ||
456 | /* | ||
457 | * The RX Thread starts up the TX Thread and sleeps. | ||
458 | */ | ||
459 | ts->thread_clear |= ISCSI_CLEAR_RX_THREAD; | ||
460 | complete(&ts->tx_start_comp); | ||
461 | wait_for_completion(&ts->tx_post_start_comp); | ||
462 | |||
463 | return ts->conn; | ||
464 | } | ||
465 | |||
466 | struct iscsi_conn *iscsi_tx_thread_pre_handler(struct iscsi_thread_set *ts) | ||
467 | { | ||
468 | int ret; | ||
469 | |||
470 | spin_lock_bh(&ts->ts_state_lock); | ||
471 | if (ts->create_threads) { | ||
472 | spin_unlock_bh(&ts->ts_state_lock); | ||
473 | goto sleep; | ||
474 | } | ||
475 | |||
476 | flush_signals(current); | ||
477 | |||
478 | if (ts->delay_inactive && (--ts->thread_count == 0)) { | ||
479 | spin_unlock_bh(&ts->ts_state_lock); | ||
480 | iscsi_del_ts_from_active_list(ts); | ||
481 | |||
482 | if (!iscsit_global->in_shutdown) | ||
483 | iscsi_deallocate_extra_thread_sets(); | ||
484 | |||
485 | iscsi_add_ts_to_inactive_list(ts); | ||
486 | spin_lock_bh(&ts->ts_state_lock); | ||
487 | } | ||
488 | if ((ts->status == ISCSI_THREAD_SET_RESET) && | ||
489 | (ts->thread_clear & ISCSI_CLEAR_TX_THREAD)) | ||
490 | complete(&ts->tx_restart_comp); | ||
491 | |||
492 | ts->thread_clear &= ~ISCSI_CLEAR_TX_THREAD; | ||
493 | spin_unlock_bh(&ts->ts_state_lock); | ||
494 | sleep: | ||
495 | ret = wait_for_completion_interruptible(&ts->tx_start_comp); | ||
496 | if (ret != 0) | ||
497 | return NULL; | ||
498 | |||
499 | if (iscsi_signal_thread_pre_handler(ts) < 0) | ||
500 | return NULL; | ||
501 | |||
502 | if (!ts->conn) { | ||
503 | pr_err("struct iscsi_thread_set->conn is NULL for " | ||
504 | " thread_id: %d, going back to sleep\n", | ||
505 | ts->thread_id); | ||
506 | goto sleep; | ||
507 | } | ||
508 | |||
509 | iscsi_check_to_add_additional_sets(); | ||
510 | /* | ||
511 | * From the TX thread, up the tx_post_start_comp that the RX Thread is | ||
512 | * sleeping on in iscsi_rx_thread_pre_handler(), then up the | ||
513 | * rx_post_start_comp that iscsi_activate_thread_set() is sleeping on. | ||
514 | */ | ||
515 | ts->thread_clear |= ISCSI_CLEAR_TX_THREAD; | ||
516 | complete(&ts->tx_post_start_comp); | ||
517 | complete(&ts->rx_post_start_comp); | ||
518 | |||
519 | spin_lock_bh(&ts->ts_state_lock); | ||
520 | ts->status = ISCSI_THREAD_SET_ACTIVE; | ||
521 | spin_unlock_bh(&ts->ts_state_lock); | ||
522 | |||
523 | return ts->conn; | ||
524 | } | ||
525 | |||
526 | int iscsi_thread_set_init(void) | ||
527 | { | ||
528 | int size; | ||
529 | |||
530 | iscsit_global->ts_bitmap_count = ISCSI_TS_BITMAP_BITS; | ||
531 | |||
532 | size = BITS_TO_LONGS(iscsit_global->ts_bitmap_count) * sizeof(long); | ||
533 | iscsit_global->ts_bitmap = kzalloc(size, GFP_KERNEL); | ||
534 | if (!iscsit_global->ts_bitmap) { | ||
535 | pr_err("Unable to allocate iscsit_global->ts_bitmap\n"); | ||
536 | return -ENOMEM; | ||
537 | } | ||
538 | |||
539 | spin_lock_init(&active_ts_lock); | ||
540 | spin_lock_init(&inactive_ts_lock); | ||
541 | spin_lock_init(&ts_bitmap_lock); | ||
542 | INIT_LIST_HEAD(&active_ts_list); | ||
543 | INIT_LIST_HEAD(&inactive_ts_list); | ||
544 | |||
545 | return 0; | ||
546 | } | ||
547 | |||
548 | void iscsi_thread_set_free(void) | ||
549 | { | ||
550 | kfree(iscsit_global->ts_bitmap); | ||
551 | } | ||
diff --git a/drivers/target/iscsi/iscsi_target_tq.h b/drivers/target/iscsi/iscsi_target_tq.h new file mode 100644 index 000000000000..26e6a95ec203 --- /dev/null +++ b/drivers/target/iscsi/iscsi_target_tq.h | |||
@@ -0,0 +1,88 @@ | |||
1 | #ifndef ISCSI_THREAD_QUEUE_H | ||
2 | #define ISCSI_THREAD_QUEUE_H | ||
3 | |||
4 | /* | ||
5 | * Defines for thread sets. | ||
6 | */ | ||
7 | extern int iscsi_thread_set_force_reinstatement(struct iscsi_conn *); | ||
8 | extern void iscsi_add_ts_to_inactive_list(struct iscsi_thread_set *); | ||
9 | extern int iscsi_allocate_thread_sets(u32); | ||
10 | extern void iscsi_deallocate_thread_sets(void); | ||
11 | extern void iscsi_activate_thread_set(struct iscsi_conn *, struct iscsi_thread_set *); | ||
12 | extern struct iscsi_thread_set *iscsi_get_thread_set(void); | ||
13 | extern void iscsi_set_thread_clear(struct iscsi_conn *, u8); | ||
14 | extern void iscsi_set_thread_set_signal(struct iscsi_conn *, u8); | ||
15 | extern int iscsi_release_thread_set(struct iscsi_conn *); | ||
16 | extern struct iscsi_conn *iscsi_rx_thread_pre_handler(struct iscsi_thread_set *); | ||
17 | extern struct iscsi_conn *iscsi_tx_thread_pre_handler(struct iscsi_thread_set *); | ||
18 | extern int iscsi_thread_set_init(void); | ||
19 | extern void iscsi_thread_set_free(void); | ||
20 | |||
21 | extern int iscsi_target_tx_thread(void *); | ||
22 | extern int iscsi_target_rx_thread(void *); | ||
23 | |||
24 | #define TARGET_THREAD_SET_COUNT 4 | ||
25 | |||
26 | #define ISCSI_RX_THREAD 1 | ||
27 | #define ISCSI_TX_THREAD 2 | ||
28 | #define ISCSI_RX_THREAD_NAME "iscsi_trx" | ||
29 | #define ISCSI_TX_THREAD_NAME "iscsi_ttx" | ||
30 | #define ISCSI_BLOCK_RX_THREAD 0x1 | ||
31 | #define ISCSI_BLOCK_TX_THREAD 0x2 | ||
32 | #define ISCSI_CLEAR_RX_THREAD 0x1 | ||
33 | #define ISCSI_CLEAR_TX_THREAD 0x2 | ||
34 | #define ISCSI_SIGNAL_RX_THREAD 0x1 | ||
35 | #define ISCSI_SIGNAL_TX_THREAD 0x2 | ||
36 | |||
37 | /* struct iscsi_thread_set->status */ | ||
38 | #define ISCSI_THREAD_SET_FREE 1 | ||
39 | #define ISCSI_THREAD_SET_ACTIVE 2 | ||
40 | #define ISCSI_THREAD_SET_DIE 3 | ||
41 | #define ISCSI_THREAD_SET_RESET 4 | ||
42 | #define ISCSI_THREAD_SET_DEALLOCATE_THREADS 5 | ||
43 | |||
44 | /* By default allow a maximum of 32K iSCSI connections */ | ||
45 | #define ISCSI_TS_BITMAP_BITS 32768 | ||
46 | |||
47 | struct iscsi_thread_set { | ||
48 | /* flags used for blocking and restarting sets */ | ||
49 | int blocked_threads; | ||
50 | /* flag for creating threads */ | ||
51 | int create_threads; | ||
52 | /* flag for delaying readding to inactive list */ | ||
53 | int delay_inactive; | ||
54 | /* status for thread set */ | ||
55 | int status; | ||
56 | /* which threads have had signals sent */ | ||
57 | int signal_sent; | ||
58 | /* flag for which threads exited first */ | ||
59 | int thread_clear; | ||
60 | /* Active threads in the thread set */ | ||
61 | int thread_count; | ||
62 | /* Unique thread ID */ | ||
63 | u32 thread_id; | ||
64 | /* pointer to connection if set is active */ | ||
65 | struct iscsi_conn *conn; | ||
66 | /* used for controlling ts state accesses */ | ||
67 | spinlock_t ts_state_lock; | ||
68 | /* Used for rx side post startup */ | ||
69 | struct completion rx_post_start_comp; | ||
70 | /* Used for tx side post startup */ | ||
71 | struct completion tx_post_start_comp; | ||
72 | /* used for restarting thread queue */ | ||
73 | struct completion rx_restart_comp; | ||
74 | /* used for restarting thread queue */ | ||
75 | struct completion tx_restart_comp; | ||
76 | /* used for normal unused blocking */ | ||
77 | struct completion rx_start_comp; | ||
78 | /* used for normal unused blocking */ | ||
79 | struct completion tx_start_comp; | ||
80 | /* OS descriptor for rx thread */ | ||
81 | struct task_struct *rx_thread; | ||
82 | /* OS descriptor for tx thread */ | ||
83 | struct task_struct *tx_thread; | ||
84 | /* struct iscsi_thread_set in list list head*/ | ||
85 | struct list_head ts_list; | ||
86 | }; | ||
87 | |||
88 | #endif /*** ISCSI_THREAD_QUEUE_H ***/ | ||
diff --git a/drivers/target/iscsi/iscsi_target_util.c b/drivers/target/iscsi/iscsi_target_util.c new file mode 100644 index 000000000000..a1acb0167902 --- /dev/null +++ b/drivers/target/iscsi/iscsi_target_util.c | |||
@@ -0,0 +1,1819 @@ | |||
1 | /******************************************************************************* | ||
2 | * This file contains the iSCSI Target specific utility functions. | ||
3 | * | ||
4 | * \u00a9 Copyright 2007-2011 RisingTide Systems LLC. | ||
5 | * | ||
6 | * Licensed to the Linux Foundation under the General Public License (GPL) version 2. | ||
7 | * | ||
8 | * Author: Nicholas A. Bellinger <nab@linux-iscsi.org> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | ******************************************************************************/ | ||
20 | |||
21 | #include <linux/list.h> | ||
22 | #include <scsi/scsi_tcq.h> | ||
23 | #include <scsi/iscsi_proto.h> | ||
24 | #include <target/target_core_base.h> | ||
25 | #include <target/target_core_transport.h> | ||
26 | #include <target/target_core_tmr.h> | ||
27 | #include <target/target_core_fabric_ops.h> | ||
28 | #include <target/target_core_configfs.h> | ||
29 | |||
30 | #include "iscsi_target_core.h" | ||
31 | #include "iscsi_target_parameters.h" | ||
32 | #include "iscsi_target_seq_pdu_list.h" | ||
33 | #include "iscsi_target_datain_values.h" | ||
34 | #include "iscsi_target_erl0.h" | ||
35 | #include "iscsi_target_erl1.h" | ||
36 | #include "iscsi_target_erl2.h" | ||
37 | #include "iscsi_target_tpg.h" | ||
38 | #include "iscsi_target_tq.h" | ||
39 | #include "iscsi_target_util.h" | ||
40 | #include "iscsi_target.h" | ||
41 | |||
42 | #define PRINT_BUFF(buff, len) \ | ||
43 | { \ | ||
44 | int zzz; \ | ||
45 | \ | ||
46 | pr_debug("%d:\n", __LINE__); \ | ||
47 | for (zzz = 0; zzz < len; zzz++) { \ | ||
48 | if (zzz % 16 == 0) { \ | ||
49 | if (zzz) \ | ||
50 | pr_debug("\n"); \ | ||
51 | pr_debug("%4i: ", zzz); \ | ||
52 | } \ | ||
53 | pr_debug("%02x ", (unsigned char) (buff)[zzz]); \ | ||
54 | } \ | ||
55 | if ((len + 1) % 16) \ | ||
56 | pr_debug("\n"); \ | ||
57 | } | ||
58 | |||
59 | extern struct list_head g_tiqn_list; | ||
60 | extern spinlock_t tiqn_lock; | ||
61 | |||
62 | /* | ||
63 | * Called with cmd->r2t_lock held. | ||
64 | */ | ||
65 | int iscsit_add_r2t_to_list( | ||
66 | struct iscsi_cmd *cmd, | ||
67 | u32 offset, | ||
68 | u32 xfer_len, | ||
69 | int recovery, | ||
70 | u32 r2t_sn) | ||
71 | { | ||
72 | struct iscsi_r2t *r2t; | ||
73 | |||
74 | r2t = kmem_cache_zalloc(lio_r2t_cache, GFP_ATOMIC); | ||
75 | if (!r2t) { | ||
76 | pr_err("Unable to allocate memory for struct iscsi_r2t.\n"); | ||
77 | return -1; | ||
78 | } | ||
79 | INIT_LIST_HEAD(&r2t->r2t_list); | ||
80 | |||
81 | r2t->recovery_r2t = recovery; | ||
82 | r2t->r2t_sn = (!r2t_sn) ? cmd->r2t_sn++ : r2t_sn; | ||
83 | r2t->offset = offset; | ||
84 | r2t->xfer_len = xfer_len; | ||
85 | list_add_tail(&r2t->r2t_list, &cmd->cmd_r2t_list); | ||
86 | spin_unlock_bh(&cmd->r2t_lock); | ||
87 | |||
88 | iscsit_add_cmd_to_immediate_queue(cmd, cmd->conn, ISTATE_SEND_R2T); | ||
89 | |||
90 | spin_lock_bh(&cmd->r2t_lock); | ||
91 | return 0; | ||
92 | } | ||
93 | |||
94 | struct iscsi_r2t *iscsit_get_r2t_for_eos( | ||
95 | struct iscsi_cmd *cmd, | ||
96 | u32 offset, | ||
97 | u32 length) | ||
98 | { | ||
99 | struct iscsi_r2t *r2t; | ||
100 | |||
101 | spin_lock_bh(&cmd->r2t_lock); | ||
102 | list_for_each_entry(r2t, &cmd->cmd_r2t_list, r2t_list) { | ||
103 | if ((r2t->offset <= offset) && | ||
104 | (r2t->offset + r2t->xfer_len) >= (offset + length)) { | ||
105 | spin_unlock_bh(&cmd->r2t_lock); | ||
106 | return r2t; | ||
107 | } | ||
108 | } | ||
109 | spin_unlock_bh(&cmd->r2t_lock); | ||
110 | |||
111 | pr_err("Unable to locate R2T for Offset: %u, Length:" | ||
112 | " %u\n", offset, length); | ||
113 | return NULL; | ||
114 | } | ||
115 | |||
116 | struct iscsi_r2t *iscsit_get_r2t_from_list(struct iscsi_cmd *cmd) | ||
117 | { | ||
118 | struct iscsi_r2t *r2t; | ||
119 | |||
120 | spin_lock_bh(&cmd->r2t_lock); | ||
121 | list_for_each_entry(r2t, &cmd->cmd_r2t_list, r2t_list) { | ||
122 | if (!r2t->sent_r2t) { | ||
123 | spin_unlock_bh(&cmd->r2t_lock); | ||
124 | return r2t; | ||
125 | } | ||
126 | } | ||
127 | spin_unlock_bh(&cmd->r2t_lock); | ||
128 | |||
129 | pr_err("Unable to locate next R2T to send for ITT:" | ||
130 | " 0x%08x.\n", cmd->init_task_tag); | ||
131 | return NULL; | ||
132 | } | ||
133 | |||
134 | /* | ||
135 | * Called with cmd->r2t_lock held. | ||
136 | */ | ||
137 | void iscsit_free_r2t(struct iscsi_r2t *r2t, struct iscsi_cmd *cmd) | ||
138 | { | ||
139 | list_del(&r2t->r2t_list); | ||
140 | kmem_cache_free(lio_r2t_cache, r2t); | ||
141 | } | ||
142 | |||
143 | void iscsit_free_r2ts_from_list(struct iscsi_cmd *cmd) | ||
144 | { | ||
145 | struct iscsi_r2t *r2t, *r2t_tmp; | ||
146 | |||
147 | spin_lock_bh(&cmd->r2t_lock); | ||
148 | list_for_each_entry_safe(r2t, r2t_tmp, &cmd->cmd_r2t_list, r2t_list) | ||
149 | iscsit_free_r2t(r2t, cmd); | ||
150 | spin_unlock_bh(&cmd->r2t_lock); | ||
151 | } | ||
152 | |||
153 | /* | ||
154 | * May be called from software interrupt (timer) context for allocating | ||
155 | * iSCSI NopINs. | ||
156 | */ | ||
157 | struct iscsi_cmd *iscsit_allocate_cmd(struct iscsi_conn *conn, gfp_t gfp_mask) | ||
158 | { | ||
159 | struct iscsi_cmd *cmd; | ||
160 | |||
161 | cmd = kmem_cache_zalloc(lio_cmd_cache, gfp_mask); | ||
162 | if (!cmd) { | ||
163 | pr_err("Unable to allocate memory for struct iscsi_cmd.\n"); | ||
164 | return NULL; | ||
165 | } | ||
166 | |||
167 | cmd->conn = conn; | ||
168 | INIT_LIST_HEAD(&cmd->i_list); | ||
169 | INIT_LIST_HEAD(&cmd->datain_list); | ||
170 | INIT_LIST_HEAD(&cmd->cmd_r2t_list); | ||
171 | init_completion(&cmd->reject_comp); | ||
172 | spin_lock_init(&cmd->datain_lock); | ||
173 | spin_lock_init(&cmd->dataout_timeout_lock); | ||
174 | spin_lock_init(&cmd->istate_lock); | ||
175 | spin_lock_init(&cmd->error_lock); | ||
176 | spin_lock_init(&cmd->r2t_lock); | ||
177 | |||
178 | return cmd; | ||
179 | } | ||
180 | |||
181 | /* | ||
182 | * Called from iscsi_handle_scsi_cmd() | ||
183 | */ | ||
184 | struct iscsi_cmd *iscsit_allocate_se_cmd( | ||
185 | struct iscsi_conn *conn, | ||
186 | u32 data_length, | ||
187 | int data_direction, | ||
188 | int iscsi_task_attr) | ||
189 | { | ||
190 | struct iscsi_cmd *cmd; | ||
191 | struct se_cmd *se_cmd; | ||
192 | int sam_task_attr; | ||
193 | |||
194 | cmd = iscsit_allocate_cmd(conn, GFP_KERNEL); | ||
195 | if (!cmd) | ||
196 | return NULL; | ||
197 | |||
198 | cmd->data_direction = data_direction; | ||
199 | cmd->data_length = data_length; | ||
200 | /* | ||
201 | * Figure out the SAM Task Attribute for the incoming SCSI CDB | ||
202 | */ | ||
203 | if ((iscsi_task_attr == ISCSI_ATTR_UNTAGGED) || | ||
204 | (iscsi_task_attr == ISCSI_ATTR_SIMPLE)) | ||
205 | sam_task_attr = MSG_SIMPLE_TAG; | ||
206 | else if (iscsi_task_attr == ISCSI_ATTR_ORDERED) | ||
207 | sam_task_attr = MSG_ORDERED_TAG; | ||
208 | else if (iscsi_task_attr == ISCSI_ATTR_HEAD_OF_QUEUE) | ||
209 | sam_task_attr = MSG_HEAD_TAG; | ||
210 | else if (iscsi_task_attr == ISCSI_ATTR_ACA) | ||
211 | sam_task_attr = MSG_ACA_TAG; | ||
212 | else { | ||
213 | pr_debug("Unknown iSCSI Task Attribute: 0x%02x, using" | ||
214 | " MSG_SIMPLE_TAG\n", iscsi_task_attr); | ||
215 | sam_task_attr = MSG_SIMPLE_TAG; | ||
216 | } | ||
217 | |||
218 | se_cmd = &cmd->se_cmd; | ||
219 | /* | ||
220 | * Initialize struct se_cmd descriptor from target_core_mod infrastructure | ||
221 | */ | ||
222 | transport_init_se_cmd(se_cmd, &lio_target_fabric_configfs->tf_ops, | ||
223 | conn->sess->se_sess, data_length, data_direction, | ||
224 | sam_task_attr, &cmd->sense_buffer[0]); | ||
225 | return cmd; | ||
226 | } | ||
227 | |||
228 | struct iscsi_cmd *iscsit_allocate_se_cmd_for_tmr( | ||
229 | struct iscsi_conn *conn, | ||
230 | u8 function) | ||
231 | { | ||
232 | struct iscsi_cmd *cmd; | ||
233 | struct se_cmd *se_cmd; | ||
234 | u8 tcm_function; | ||
235 | |||
236 | cmd = iscsit_allocate_cmd(conn, GFP_KERNEL); | ||
237 | if (!cmd) | ||
238 | return NULL; | ||
239 | |||
240 | cmd->data_direction = DMA_NONE; | ||
241 | |||
242 | cmd->tmr_req = kzalloc(sizeof(struct iscsi_tmr_req), GFP_KERNEL); | ||
243 | if (!cmd->tmr_req) { | ||
244 | pr_err("Unable to allocate memory for" | ||
245 | " Task Management command!\n"); | ||
246 | return NULL; | ||
247 | } | ||
248 | /* | ||
249 | * TASK_REASSIGN for ERL=2 / connection stays inside of | ||
250 | * LIO-Target $FABRIC_MOD | ||
251 | */ | ||
252 | if (function == ISCSI_TM_FUNC_TASK_REASSIGN) | ||
253 | return cmd; | ||
254 | |||
255 | se_cmd = &cmd->se_cmd; | ||
256 | /* | ||
257 | * Initialize struct se_cmd descriptor from target_core_mod infrastructure | ||
258 | */ | ||
259 | transport_init_se_cmd(se_cmd, &lio_target_fabric_configfs->tf_ops, | ||
260 | conn->sess->se_sess, 0, DMA_NONE, | ||
261 | MSG_SIMPLE_TAG, &cmd->sense_buffer[0]); | ||
262 | |||
263 | switch (function) { | ||
264 | case ISCSI_TM_FUNC_ABORT_TASK: | ||
265 | tcm_function = TMR_ABORT_TASK; | ||
266 | break; | ||
267 | case ISCSI_TM_FUNC_ABORT_TASK_SET: | ||
268 | tcm_function = TMR_ABORT_TASK_SET; | ||
269 | break; | ||
270 | case ISCSI_TM_FUNC_CLEAR_ACA: | ||
271 | tcm_function = TMR_CLEAR_ACA; | ||
272 | break; | ||
273 | case ISCSI_TM_FUNC_CLEAR_TASK_SET: | ||
274 | tcm_function = TMR_CLEAR_TASK_SET; | ||
275 | break; | ||
276 | case ISCSI_TM_FUNC_LOGICAL_UNIT_RESET: | ||
277 | tcm_function = TMR_LUN_RESET; | ||
278 | break; | ||
279 | case ISCSI_TM_FUNC_TARGET_WARM_RESET: | ||
280 | tcm_function = TMR_TARGET_WARM_RESET; | ||
281 | break; | ||
282 | case ISCSI_TM_FUNC_TARGET_COLD_RESET: | ||
283 | tcm_function = TMR_TARGET_COLD_RESET; | ||
284 | break; | ||
285 | default: | ||
286 | pr_err("Unknown iSCSI TMR Function:" | ||
287 | " 0x%02x\n", function); | ||
288 | goto out; | ||
289 | } | ||
290 | |||
291 | se_cmd->se_tmr_req = core_tmr_alloc_req(se_cmd, | ||
292 | (void *)cmd->tmr_req, tcm_function); | ||
293 | if (!se_cmd->se_tmr_req) | ||
294 | goto out; | ||
295 | |||
296 | cmd->tmr_req->se_tmr_req = se_cmd->se_tmr_req; | ||
297 | |||
298 | return cmd; | ||
299 | out: | ||
300 | iscsit_release_cmd(cmd); | ||
301 | if (se_cmd) | ||
302 | transport_free_se_cmd(se_cmd); | ||
303 | return NULL; | ||
304 | } | ||
305 | |||
306 | int iscsit_decide_list_to_build( | ||
307 | struct iscsi_cmd *cmd, | ||
308 | u32 immediate_data_length) | ||
309 | { | ||
310 | struct iscsi_build_list bl; | ||
311 | struct iscsi_conn *conn = cmd->conn; | ||
312 | struct iscsi_session *sess = conn->sess; | ||
313 | struct iscsi_node_attrib *na; | ||
314 | |||
315 | if (sess->sess_ops->DataSequenceInOrder && | ||
316 | sess->sess_ops->DataPDUInOrder) | ||
317 | return 0; | ||
318 | |||
319 | if (cmd->data_direction == DMA_NONE) | ||
320 | return 0; | ||
321 | |||
322 | na = iscsit_tpg_get_node_attrib(sess); | ||
323 | memset(&bl, 0, sizeof(struct iscsi_build_list)); | ||
324 | |||
325 | if (cmd->data_direction == DMA_FROM_DEVICE) { | ||
326 | bl.data_direction = ISCSI_PDU_READ; | ||
327 | bl.type = PDULIST_NORMAL; | ||
328 | if (na->random_datain_pdu_offsets) | ||
329 | bl.randomize |= RANDOM_DATAIN_PDU_OFFSETS; | ||
330 | if (na->random_datain_seq_offsets) | ||
331 | bl.randomize |= RANDOM_DATAIN_SEQ_OFFSETS; | ||
332 | } else { | ||
333 | bl.data_direction = ISCSI_PDU_WRITE; | ||
334 | bl.immediate_data_length = immediate_data_length; | ||
335 | if (na->random_r2t_offsets) | ||
336 | bl.randomize |= RANDOM_R2T_OFFSETS; | ||
337 | |||
338 | if (!cmd->immediate_data && !cmd->unsolicited_data) | ||
339 | bl.type = PDULIST_NORMAL; | ||
340 | else if (cmd->immediate_data && !cmd->unsolicited_data) | ||
341 | bl.type = PDULIST_IMMEDIATE; | ||
342 | else if (!cmd->immediate_data && cmd->unsolicited_data) | ||
343 | bl.type = PDULIST_UNSOLICITED; | ||
344 | else if (cmd->immediate_data && cmd->unsolicited_data) | ||
345 | bl.type = PDULIST_IMMEDIATE_AND_UNSOLICITED; | ||
346 | } | ||
347 | |||
348 | return iscsit_do_build_list(cmd, &bl); | ||
349 | } | ||
350 | |||
351 | struct iscsi_seq *iscsit_get_seq_holder_for_datain( | ||
352 | struct iscsi_cmd *cmd, | ||
353 | u32 seq_send_order) | ||
354 | { | ||
355 | u32 i; | ||
356 | |||
357 | for (i = 0; i < cmd->seq_count; i++) | ||
358 | if (cmd->seq_list[i].seq_send_order == seq_send_order) | ||
359 | return &cmd->seq_list[i]; | ||
360 | |||
361 | return NULL; | ||
362 | } | ||
363 | |||
364 | struct iscsi_seq *iscsit_get_seq_holder_for_r2t(struct iscsi_cmd *cmd) | ||
365 | { | ||
366 | u32 i; | ||
367 | |||
368 | if (!cmd->seq_list) { | ||
369 | pr_err("struct iscsi_cmd->seq_list is NULL!\n"); | ||
370 | return NULL; | ||
371 | } | ||
372 | |||
373 | for (i = 0; i < cmd->seq_count; i++) { | ||
374 | if (cmd->seq_list[i].type != SEQTYPE_NORMAL) | ||
375 | continue; | ||
376 | if (cmd->seq_list[i].seq_send_order == cmd->seq_send_order) { | ||
377 | cmd->seq_send_order++; | ||
378 | return &cmd->seq_list[i]; | ||
379 | } | ||
380 | } | ||
381 | |||
382 | return NULL; | ||
383 | } | ||
384 | |||
385 | struct iscsi_r2t *iscsit_get_holder_for_r2tsn( | ||
386 | struct iscsi_cmd *cmd, | ||
387 | u32 r2t_sn) | ||
388 | { | ||
389 | struct iscsi_r2t *r2t; | ||
390 | |||
391 | spin_lock_bh(&cmd->r2t_lock); | ||
392 | list_for_each_entry(r2t, &cmd->cmd_r2t_list, r2t_list) { | ||
393 | if (r2t->r2t_sn == r2t_sn) { | ||
394 | spin_unlock_bh(&cmd->r2t_lock); | ||
395 | return r2t; | ||
396 | } | ||
397 | } | ||
398 | spin_unlock_bh(&cmd->r2t_lock); | ||
399 | |||
400 | return NULL; | ||
401 | } | ||
402 | |||
403 | static inline int iscsit_check_received_cmdsn(struct iscsi_session *sess, u32 cmdsn) | ||
404 | { | ||
405 | int ret; | ||
406 | |||
407 | /* | ||
408 | * This is the proper method of checking received CmdSN against | ||
409 | * ExpCmdSN and MaxCmdSN values, as well as accounting for out | ||
410 | * or order CmdSNs due to multiple connection sessions and/or | ||
411 | * CRC failures. | ||
412 | */ | ||
413 | if (iscsi_sna_gt(cmdsn, sess->max_cmd_sn)) { | ||
414 | pr_err("Received CmdSN: 0x%08x is greater than" | ||
415 | " MaxCmdSN: 0x%08x, protocol error.\n", cmdsn, | ||
416 | sess->max_cmd_sn); | ||
417 | ret = CMDSN_ERROR_CANNOT_RECOVER; | ||
418 | |||
419 | } else if (cmdsn == sess->exp_cmd_sn) { | ||
420 | sess->exp_cmd_sn++; | ||
421 | pr_debug("Received CmdSN matches ExpCmdSN," | ||
422 | " incremented ExpCmdSN to: 0x%08x\n", | ||
423 | sess->exp_cmd_sn); | ||
424 | ret = CMDSN_NORMAL_OPERATION; | ||
425 | |||
426 | } else if (iscsi_sna_gt(cmdsn, sess->exp_cmd_sn)) { | ||
427 | pr_debug("Received CmdSN: 0x%08x is greater" | ||
428 | " than ExpCmdSN: 0x%08x, not acknowledging.\n", | ||
429 | cmdsn, sess->exp_cmd_sn); | ||
430 | ret = CMDSN_HIGHER_THAN_EXP; | ||
431 | |||
432 | } else { | ||
433 | pr_err("Received CmdSN: 0x%08x is less than" | ||
434 | " ExpCmdSN: 0x%08x, ignoring.\n", cmdsn, | ||
435 | sess->exp_cmd_sn); | ||
436 | ret = CMDSN_LOWER_THAN_EXP; | ||
437 | } | ||
438 | |||
439 | return ret; | ||
440 | } | ||
441 | |||
442 | /* | ||
443 | * Commands may be received out of order if MC/S is in use. | ||
444 | * Ensure they are executed in CmdSN order. | ||
445 | */ | ||
446 | int iscsit_sequence_cmd( | ||
447 | struct iscsi_conn *conn, | ||
448 | struct iscsi_cmd *cmd, | ||
449 | u32 cmdsn) | ||
450 | { | ||
451 | int ret; | ||
452 | int cmdsn_ret; | ||
453 | |||
454 | mutex_lock(&conn->sess->cmdsn_mutex); | ||
455 | |||
456 | cmdsn_ret = iscsit_check_received_cmdsn(conn->sess, cmdsn); | ||
457 | switch (cmdsn_ret) { | ||
458 | case CMDSN_NORMAL_OPERATION: | ||
459 | ret = iscsit_execute_cmd(cmd, 0); | ||
460 | if ((ret >= 0) && !list_empty(&conn->sess->sess_ooo_cmdsn_list)) | ||
461 | iscsit_execute_ooo_cmdsns(conn->sess); | ||
462 | break; | ||
463 | case CMDSN_HIGHER_THAN_EXP: | ||
464 | ret = iscsit_handle_ooo_cmdsn(conn->sess, cmd, cmdsn); | ||
465 | break; | ||
466 | case CMDSN_LOWER_THAN_EXP: | ||
467 | cmd->i_state = ISTATE_REMOVE; | ||
468 | iscsit_add_cmd_to_immediate_queue(cmd, conn, cmd->i_state); | ||
469 | ret = cmdsn_ret; | ||
470 | break; | ||
471 | default: | ||
472 | ret = cmdsn_ret; | ||
473 | break; | ||
474 | } | ||
475 | mutex_unlock(&conn->sess->cmdsn_mutex); | ||
476 | |||
477 | return ret; | ||
478 | } | ||
479 | |||
480 | int iscsit_check_unsolicited_dataout(struct iscsi_cmd *cmd, unsigned char *buf) | ||
481 | { | ||
482 | struct iscsi_conn *conn = cmd->conn; | ||
483 | struct se_cmd *se_cmd = &cmd->se_cmd; | ||
484 | struct iscsi_data *hdr = (struct iscsi_data *) buf; | ||
485 | u32 payload_length = ntoh24(hdr->dlength); | ||
486 | |||
487 | if (conn->sess->sess_ops->InitialR2T) { | ||
488 | pr_err("Received unexpected unsolicited data" | ||
489 | " while InitialR2T=Yes, protocol error.\n"); | ||
490 | transport_send_check_condition_and_sense(se_cmd, | ||
491 | TCM_UNEXPECTED_UNSOLICITED_DATA, 0); | ||
492 | return -1; | ||
493 | } | ||
494 | |||
495 | if ((cmd->first_burst_len + payload_length) > | ||
496 | conn->sess->sess_ops->FirstBurstLength) { | ||
497 | pr_err("Total %u bytes exceeds FirstBurstLength: %u" | ||
498 | " for this Unsolicited DataOut Burst.\n", | ||
499 | (cmd->first_burst_len + payload_length), | ||
500 | conn->sess->sess_ops->FirstBurstLength); | ||
501 | transport_send_check_condition_and_sense(se_cmd, | ||
502 | TCM_INCORRECT_AMOUNT_OF_DATA, 0); | ||
503 | return -1; | ||
504 | } | ||
505 | |||
506 | if (!(hdr->flags & ISCSI_FLAG_CMD_FINAL)) | ||
507 | return 0; | ||
508 | |||
509 | if (((cmd->first_burst_len + payload_length) != cmd->data_length) && | ||
510 | ((cmd->first_burst_len + payload_length) != | ||
511 | conn->sess->sess_ops->FirstBurstLength)) { | ||
512 | pr_err("Unsolicited non-immediate data received %u" | ||
513 | " does not equal FirstBurstLength: %u, and does" | ||
514 | " not equal ExpXferLen %u.\n", | ||
515 | (cmd->first_burst_len + payload_length), | ||
516 | conn->sess->sess_ops->FirstBurstLength, cmd->data_length); | ||
517 | transport_send_check_condition_and_sense(se_cmd, | ||
518 | TCM_INCORRECT_AMOUNT_OF_DATA, 0); | ||
519 | return -1; | ||
520 | } | ||
521 | return 0; | ||
522 | } | ||
523 | |||
524 | struct iscsi_cmd *iscsit_find_cmd_from_itt( | ||
525 | struct iscsi_conn *conn, | ||
526 | u32 init_task_tag) | ||
527 | { | ||
528 | struct iscsi_cmd *cmd; | ||
529 | |||
530 | spin_lock_bh(&conn->cmd_lock); | ||
531 | list_for_each_entry(cmd, &conn->conn_cmd_list, i_list) { | ||
532 | if (cmd->init_task_tag == init_task_tag) { | ||
533 | spin_unlock_bh(&conn->cmd_lock); | ||
534 | return cmd; | ||
535 | } | ||
536 | } | ||
537 | spin_unlock_bh(&conn->cmd_lock); | ||
538 | |||
539 | pr_err("Unable to locate ITT: 0x%08x on CID: %hu", | ||
540 | init_task_tag, conn->cid); | ||
541 | return NULL; | ||
542 | } | ||
543 | |||
544 | struct iscsi_cmd *iscsit_find_cmd_from_itt_or_dump( | ||
545 | struct iscsi_conn *conn, | ||
546 | u32 init_task_tag, | ||
547 | u32 length) | ||
548 | { | ||
549 | struct iscsi_cmd *cmd; | ||
550 | |||
551 | spin_lock_bh(&conn->cmd_lock); | ||
552 | list_for_each_entry(cmd, &conn->conn_cmd_list, i_list) { | ||
553 | if (cmd->init_task_tag == init_task_tag) { | ||
554 | spin_unlock_bh(&conn->cmd_lock); | ||
555 | return cmd; | ||
556 | } | ||
557 | } | ||
558 | spin_unlock_bh(&conn->cmd_lock); | ||
559 | |||
560 | pr_err("Unable to locate ITT: 0x%08x on CID: %hu," | ||
561 | " dumping payload\n", init_task_tag, conn->cid); | ||
562 | if (length) | ||
563 | iscsit_dump_data_payload(conn, length, 1); | ||
564 | |||
565 | return NULL; | ||
566 | } | ||
567 | |||
568 | struct iscsi_cmd *iscsit_find_cmd_from_ttt( | ||
569 | struct iscsi_conn *conn, | ||
570 | u32 targ_xfer_tag) | ||
571 | { | ||
572 | struct iscsi_cmd *cmd = NULL; | ||
573 | |||
574 | spin_lock_bh(&conn->cmd_lock); | ||
575 | list_for_each_entry(cmd, &conn->conn_cmd_list, i_list) { | ||
576 | if (cmd->targ_xfer_tag == targ_xfer_tag) { | ||
577 | spin_unlock_bh(&conn->cmd_lock); | ||
578 | return cmd; | ||
579 | } | ||
580 | } | ||
581 | spin_unlock_bh(&conn->cmd_lock); | ||
582 | |||
583 | pr_err("Unable to locate TTT: 0x%08x on CID: %hu\n", | ||
584 | targ_xfer_tag, conn->cid); | ||
585 | return NULL; | ||
586 | } | ||
587 | |||
588 | int iscsit_find_cmd_for_recovery( | ||
589 | struct iscsi_session *sess, | ||
590 | struct iscsi_cmd **cmd_ptr, | ||
591 | struct iscsi_conn_recovery **cr_ptr, | ||
592 | u32 init_task_tag) | ||
593 | { | ||
594 | struct iscsi_cmd *cmd = NULL; | ||
595 | struct iscsi_conn_recovery *cr; | ||
596 | /* | ||
597 | * Scan through the inactive connection recovery list's command list. | ||
598 | * If init_task_tag matches the command is still alligent. | ||
599 | */ | ||
600 | spin_lock(&sess->cr_i_lock); | ||
601 | list_for_each_entry(cr, &sess->cr_inactive_list, cr_list) { | ||
602 | spin_lock(&cr->conn_recovery_cmd_lock); | ||
603 | list_for_each_entry(cmd, &cr->conn_recovery_cmd_list, i_list) { | ||
604 | if (cmd->init_task_tag == init_task_tag) { | ||
605 | spin_unlock(&cr->conn_recovery_cmd_lock); | ||
606 | spin_unlock(&sess->cr_i_lock); | ||
607 | |||
608 | *cr_ptr = cr; | ||
609 | *cmd_ptr = cmd; | ||
610 | return -2; | ||
611 | } | ||
612 | } | ||
613 | spin_unlock(&cr->conn_recovery_cmd_lock); | ||
614 | } | ||
615 | spin_unlock(&sess->cr_i_lock); | ||
616 | /* | ||
617 | * Scan through the active connection recovery list's command list. | ||
618 | * If init_task_tag matches the command is ready to be reassigned. | ||
619 | */ | ||
620 | spin_lock(&sess->cr_a_lock); | ||
621 | list_for_each_entry(cr, &sess->cr_active_list, cr_list) { | ||
622 | spin_lock(&cr->conn_recovery_cmd_lock); | ||
623 | list_for_each_entry(cmd, &cr->conn_recovery_cmd_list, i_list) { | ||
624 | if (cmd->init_task_tag == init_task_tag) { | ||
625 | spin_unlock(&cr->conn_recovery_cmd_lock); | ||
626 | spin_unlock(&sess->cr_a_lock); | ||
627 | |||
628 | *cr_ptr = cr; | ||
629 | *cmd_ptr = cmd; | ||
630 | return 0; | ||
631 | } | ||
632 | } | ||
633 | spin_unlock(&cr->conn_recovery_cmd_lock); | ||
634 | } | ||
635 | spin_unlock(&sess->cr_a_lock); | ||
636 | |||
637 | return -1; | ||
638 | } | ||
639 | |||
640 | void iscsit_add_cmd_to_immediate_queue( | ||
641 | struct iscsi_cmd *cmd, | ||
642 | struct iscsi_conn *conn, | ||
643 | u8 state) | ||
644 | { | ||
645 | struct iscsi_queue_req *qr; | ||
646 | |||
647 | qr = kmem_cache_zalloc(lio_qr_cache, GFP_ATOMIC); | ||
648 | if (!qr) { | ||
649 | pr_err("Unable to allocate memory for" | ||
650 | " struct iscsi_queue_req\n"); | ||
651 | return; | ||
652 | } | ||
653 | INIT_LIST_HEAD(&qr->qr_list); | ||
654 | qr->cmd = cmd; | ||
655 | qr->state = state; | ||
656 | |||
657 | spin_lock_bh(&conn->immed_queue_lock); | ||
658 | list_add_tail(&qr->qr_list, &conn->immed_queue_list); | ||
659 | atomic_inc(&cmd->immed_queue_count); | ||
660 | atomic_set(&conn->check_immediate_queue, 1); | ||
661 | spin_unlock_bh(&conn->immed_queue_lock); | ||
662 | |||
663 | wake_up_process(conn->thread_set->tx_thread); | ||
664 | } | ||
665 | |||
666 | struct iscsi_queue_req *iscsit_get_cmd_from_immediate_queue(struct iscsi_conn *conn) | ||
667 | { | ||
668 | struct iscsi_queue_req *qr; | ||
669 | |||
670 | spin_lock_bh(&conn->immed_queue_lock); | ||
671 | if (list_empty(&conn->immed_queue_list)) { | ||
672 | spin_unlock_bh(&conn->immed_queue_lock); | ||
673 | return NULL; | ||
674 | } | ||
675 | list_for_each_entry(qr, &conn->immed_queue_list, qr_list) | ||
676 | break; | ||
677 | |||
678 | list_del(&qr->qr_list); | ||
679 | if (qr->cmd) | ||
680 | atomic_dec(&qr->cmd->immed_queue_count); | ||
681 | spin_unlock_bh(&conn->immed_queue_lock); | ||
682 | |||
683 | return qr; | ||
684 | } | ||
685 | |||
686 | static void iscsit_remove_cmd_from_immediate_queue( | ||
687 | struct iscsi_cmd *cmd, | ||
688 | struct iscsi_conn *conn) | ||
689 | { | ||
690 | struct iscsi_queue_req *qr, *qr_tmp; | ||
691 | |||
692 | spin_lock_bh(&conn->immed_queue_lock); | ||
693 | if (!atomic_read(&cmd->immed_queue_count)) { | ||
694 | spin_unlock_bh(&conn->immed_queue_lock); | ||
695 | return; | ||
696 | } | ||
697 | |||
698 | list_for_each_entry_safe(qr, qr_tmp, &conn->immed_queue_list, qr_list) { | ||
699 | if (qr->cmd != cmd) | ||
700 | continue; | ||
701 | |||
702 | atomic_dec(&qr->cmd->immed_queue_count); | ||
703 | list_del(&qr->qr_list); | ||
704 | kmem_cache_free(lio_qr_cache, qr); | ||
705 | } | ||
706 | spin_unlock_bh(&conn->immed_queue_lock); | ||
707 | |||
708 | if (atomic_read(&cmd->immed_queue_count)) { | ||
709 | pr_err("ITT: 0x%08x immed_queue_count: %d\n", | ||
710 | cmd->init_task_tag, | ||
711 | atomic_read(&cmd->immed_queue_count)); | ||
712 | } | ||
713 | } | ||
714 | |||
715 | void iscsit_add_cmd_to_response_queue( | ||
716 | struct iscsi_cmd *cmd, | ||
717 | struct iscsi_conn *conn, | ||
718 | u8 state) | ||
719 | { | ||
720 | struct iscsi_queue_req *qr; | ||
721 | |||
722 | qr = kmem_cache_zalloc(lio_qr_cache, GFP_ATOMIC); | ||
723 | if (!qr) { | ||
724 | pr_err("Unable to allocate memory for" | ||
725 | " struct iscsi_queue_req\n"); | ||
726 | return; | ||
727 | } | ||
728 | INIT_LIST_HEAD(&qr->qr_list); | ||
729 | qr->cmd = cmd; | ||
730 | qr->state = state; | ||
731 | |||
732 | spin_lock_bh(&conn->response_queue_lock); | ||
733 | list_add_tail(&qr->qr_list, &conn->response_queue_list); | ||
734 | atomic_inc(&cmd->response_queue_count); | ||
735 | spin_unlock_bh(&conn->response_queue_lock); | ||
736 | |||
737 | wake_up_process(conn->thread_set->tx_thread); | ||
738 | } | ||
739 | |||
740 | struct iscsi_queue_req *iscsit_get_cmd_from_response_queue(struct iscsi_conn *conn) | ||
741 | { | ||
742 | struct iscsi_queue_req *qr; | ||
743 | |||
744 | spin_lock_bh(&conn->response_queue_lock); | ||
745 | if (list_empty(&conn->response_queue_list)) { | ||
746 | spin_unlock_bh(&conn->response_queue_lock); | ||
747 | return NULL; | ||
748 | } | ||
749 | |||
750 | list_for_each_entry(qr, &conn->response_queue_list, qr_list) | ||
751 | break; | ||
752 | |||
753 | list_del(&qr->qr_list); | ||
754 | if (qr->cmd) | ||
755 | atomic_dec(&qr->cmd->response_queue_count); | ||
756 | spin_unlock_bh(&conn->response_queue_lock); | ||
757 | |||
758 | return qr; | ||
759 | } | ||
760 | |||
761 | static void iscsit_remove_cmd_from_response_queue( | ||
762 | struct iscsi_cmd *cmd, | ||
763 | struct iscsi_conn *conn) | ||
764 | { | ||
765 | struct iscsi_queue_req *qr, *qr_tmp; | ||
766 | |||
767 | spin_lock_bh(&conn->response_queue_lock); | ||
768 | if (!atomic_read(&cmd->response_queue_count)) { | ||
769 | spin_unlock_bh(&conn->response_queue_lock); | ||
770 | return; | ||
771 | } | ||
772 | |||
773 | list_for_each_entry_safe(qr, qr_tmp, &conn->response_queue_list, | ||
774 | qr_list) { | ||
775 | if (qr->cmd != cmd) | ||
776 | continue; | ||
777 | |||
778 | atomic_dec(&qr->cmd->response_queue_count); | ||
779 | list_del(&qr->qr_list); | ||
780 | kmem_cache_free(lio_qr_cache, qr); | ||
781 | } | ||
782 | spin_unlock_bh(&conn->response_queue_lock); | ||
783 | |||
784 | if (atomic_read(&cmd->response_queue_count)) { | ||
785 | pr_err("ITT: 0x%08x response_queue_count: %d\n", | ||
786 | cmd->init_task_tag, | ||
787 | atomic_read(&cmd->response_queue_count)); | ||
788 | } | ||
789 | } | ||
790 | |||
791 | void iscsit_free_queue_reqs_for_conn(struct iscsi_conn *conn) | ||
792 | { | ||
793 | struct iscsi_queue_req *qr, *qr_tmp; | ||
794 | |||
795 | spin_lock_bh(&conn->immed_queue_lock); | ||
796 | list_for_each_entry_safe(qr, qr_tmp, &conn->immed_queue_list, qr_list) { | ||
797 | list_del(&qr->qr_list); | ||
798 | if (qr->cmd) | ||
799 | atomic_dec(&qr->cmd->immed_queue_count); | ||
800 | |||
801 | kmem_cache_free(lio_qr_cache, qr); | ||
802 | } | ||
803 | spin_unlock_bh(&conn->immed_queue_lock); | ||
804 | |||
805 | spin_lock_bh(&conn->response_queue_lock); | ||
806 | list_for_each_entry_safe(qr, qr_tmp, &conn->response_queue_list, | ||
807 | qr_list) { | ||
808 | list_del(&qr->qr_list); | ||
809 | if (qr->cmd) | ||
810 | atomic_dec(&qr->cmd->response_queue_count); | ||
811 | |||
812 | kmem_cache_free(lio_qr_cache, qr); | ||
813 | } | ||
814 | spin_unlock_bh(&conn->response_queue_lock); | ||
815 | } | ||
816 | |||
817 | void iscsit_release_cmd(struct iscsi_cmd *cmd) | ||
818 | { | ||
819 | struct iscsi_conn *conn = cmd->conn; | ||
820 | int i; | ||
821 | |||
822 | iscsit_free_r2ts_from_list(cmd); | ||
823 | iscsit_free_all_datain_reqs(cmd); | ||
824 | |||
825 | kfree(cmd->buf_ptr); | ||
826 | kfree(cmd->pdu_list); | ||
827 | kfree(cmd->seq_list); | ||
828 | kfree(cmd->tmr_req); | ||
829 | kfree(cmd->iov_data); | ||
830 | |||
831 | for (i = 0; i < cmd->t_mem_sg_nents; i++) | ||
832 | __free_page(sg_page(&cmd->t_mem_sg[i])); | ||
833 | |||
834 | kfree(cmd->t_mem_sg); | ||
835 | |||
836 | if (conn) { | ||
837 | iscsit_remove_cmd_from_immediate_queue(cmd, conn); | ||
838 | iscsit_remove_cmd_from_response_queue(cmd, conn); | ||
839 | } | ||
840 | |||
841 | kmem_cache_free(lio_cmd_cache, cmd); | ||
842 | } | ||
843 | |||
844 | int iscsit_check_session_usage_count(struct iscsi_session *sess) | ||
845 | { | ||
846 | spin_lock_bh(&sess->session_usage_lock); | ||
847 | if (sess->session_usage_count != 0) { | ||
848 | sess->session_waiting_on_uc = 1; | ||
849 | spin_unlock_bh(&sess->session_usage_lock); | ||
850 | if (in_interrupt()) | ||
851 | return 2; | ||
852 | |||
853 | wait_for_completion(&sess->session_waiting_on_uc_comp); | ||
854 | return 1; | ||
855 | } | ||
856 | spin_unlock_bh(&sess->session_usage_lock); | ||
857 | |||
858 | return 0; | ||
859 | } | ||
860 | |||
861 | void iscsit_dec_session_usage_count(struct iscsi_session *sess) | ||
862 | { | ||
863 | spin_lock_bh(&sess->session_usage_lock); | ||
864 | sess->session_usage_count--; | ||
865 | |||
866 | if (!sess->session_usage_count && sess->session_waiting_on_uc) | ||
867 | complete(&sess->session_waiting_on_uc_comp); | ||
868 | |||
869 | spin_unlock_bh(&sess->session_usage_lock); | ||
870 | } | ||
871 | |||
872 | void iscsit_inc_session_usage_count(struct iscsi_session *sess) | ||
873 | { | ||
874 | spin_lock_bh(&sess->session_usage_lock); | ||
875 | sess->session_usage_count++; | ||
876 | spin_unlock_bh(&sess->session_usage_lock); | ||
877 | } | ||
878 | |||
879 | /* | ||
880 | * Used before iscsi_do[rx,tx]_data() to determine iov and [rx,tx]_marker | ||
881 | * array counts needed for sync and steering. | ||
882 | */ | ||
883 | static int iscsit_determine_sync_and_steering_counts( | ||
884 | struct iscsi_conn *conn, | ||
885 | struct iscsi_data_count *count) | ||
886 | { | ||
887 | u32 length = count->data_length; | ||
888 | u32 marker, markint; | ||
889 | |||
890 | count->sync_and_steering = 1; | ||
891 | |||
892 | marker = (count->type == ISCSI_RX_DATA) ? | ||
893 | conn->of_marker : conn->if_marker; | ||
894 | markint = (count->type == ISCSI_RX_DATA) ? | ||
895 | (conn->conn_ops->OFMarkInt * 4) : | ||
896 | (conn->conn_ops->IFMarkInt * 4); | ||
897 | count->ss_iov_count = count->iov_count; | ||
898 | |||
899 | while (length > 0) { | ||
900 | if (length >= marker) { | ||
901 | count->ss_iov_count += 3; | ||
902 | count->ss_marker_count += 2; | ||
903 | |||
904 | length -= marker; | ||
905 | marker = markint; | ||
906 | } else | ||
907 | length = 0; | ||
908 | } | ||
909 | |||
910 | return 0; | ||
911 | } | ||
912 | |||
913 | /* | ||
914 | * Setup conn->if_marker and conn->of_marker values based upon | ||
915 | * the initial marker-less interval. (see iSCSI v19 A.2) | ||
916 | */ | ||
917 | int iscsit_set_sync_and_steering_values(struct iscsi_conn *conn) | ||
918 | { | ||
919 | int login_ifmarker_count = 0, login_ofmarker_count = 0, next_marker = 0; | ||
920 | /* | ||
921 | * IFMarkInt and OFMarkInt are negotiated as 32-bit words. | ||
922 | */ | ||
923 | u32 IFMarkInt = (conn->conn_ops->IFMarkInt * 4); | ||
924 | u32 OFMarkInt = (conn->conn_ops->OFMarkInt * 4); | ||
925 | |||
926 | if (conn->conn_ops->OFMarker) { | ||
927 | /* | ||
928 | * Account for the first Login Command received not | ||
929 | * via iscsi_recv_msg(). | ||
930 | */ | ||
931 | conn->of_marker += ISCSI_HDR_LEN; | ||
932 | if (conn->of_marker <= OFMarkInt) { | ||
933 | conn->of_marker = (OFMarkInt - conn->of_marker); | ||
934 | } else { | ||
935 | login_ofmarker_count = (conn->of_marker / OFMarkInt); | ||
936 | next_marker = (OFMarkInt * (login_ofmarker_count + 1)) + | ||
937 | (login_ofmarker_count * MARKER_SIZE); | ||
938 | conn->of_marker = (next_marker - conn->of_marker); | ||
939 | } | ||
940 | conn->of_marker_offset = 0; | ||
941 | pr_debug("Setting OFMarker value to %u based on Initial" | ||
942 | " Markerless Interval.\n", conn->of_marker); | ||
943 | } | ||
944 | |||
945 | if (conn->conn_ops->IFMarker) { | ||
946 | if (conn->if_marker <= IFMarkInt) { | ||
947 | conn->if_marker = (IFMarkInt - conn->if_marker); | ||
948 | } else { | ||
949 | login_ifmarker_count = (conn->if_marker / IFMarkInt); | ||
950 | next_marker = (IFMarkInt * (login_ifmarker_count + 1)) + | ||
951 | (login_ifmarker_count * MARKER_SIZE); | ||
952 | conn->if_marker = (next_marker - conn->if_marker); | ||
953 | } | ||
954 | pr_debug("Setting IFMarker value to %u based on Initial" | ||
955 | " Markerless Interval.\n", conn->if_marker); | ||
956 | } | ||
957 | |||
958 | return 0; | ||
959 | } | ||
960 | |||
961 | struct iscsi_conn *iscsit_get_conn_from_cid(struct iscsi_session *sess, u16 cid) | ||
962 | { | ||
963 | struct iscsi_conn *conn; | ||
964 | |||
965 | spin_lock_bh(&sess->conn_lock); | ||
966 | list_for_each_entry(conn, &sess->sess_conn_list, conn_list) { | ||
967 | if ((conn->cid == cid) && | ||
968 | (conn->conn_state == TARG_CONN_STATE_LOGGED_IN)) { | ||
969 | iscsit_inc_conn_usage_count(conn); | ||
970 | spin_unlock_bh(&sess->conn_lock); | ||
971 | return conn; | ||
972 | } | ||
973 | } | ||
974 | spin_unlock_bh(&sess->conn_lock); | ||
975 | |||
976 | return NULL; | ||
977 | } | ||
978 | |||
979 | struct iscsi_conn *iscsit_get_conn_from_cid_rcfr(struct iscsi_session *sess, u16 cid) | ||
980 | { | ||
981 | struct iscsi_conn *conn; | ||
982 | |||
983 | spin_lock_bh(&sess->conn_lock); | ||
984 | list_for_each_entry(conn, &sess->sess_conn_list, conn_list) { | ||
985 | if (conn->cid == cid) { | ||
986 | iscsit_inc_conn_usage_count(conn); | ||
987 | spin_lock(&conn->state_lock); | ||
988 | atomic_set(&conn->connection_wait_rcfr, 1); | ||
989 | spin_unlock(&conn->state_lock); | ||
990 | spin_unlock_bh(&sess->conn_lock); | ||
991 | return conn; | ||
992 | } | ||
993 | } | ||
994 | spin_unlock_bh(&sess->conn_lock); | ||
995 | |||
996 | return NULL; | ||
997 | } | ||
998 | |||
999 | void iscsit_check_conn_usage_count(struct iscsi_conn *conn) | ||
1000 | { | ||
1001 | spin_lock_bh(&conn->conn_usage_lock); | ||
1002 | if (conn->conn_usage_count != 0) { | ||
1003 | conn->conn_waiting_on_uc = 1; | ||
1004 | spin_unlock_bh(&conn->conn_usage_lock); | ||
1005 | |||
1006 | wait_for_completion(&conn->conn_waiting_on_uc_comp); | ||
1007 | return; | ||
1008 | } | ||
1009 | spin_unlock_bh(&conn->conn_usage_lock); | ||
1010 | } | ||
1011 | |||
1012 | void iscsit_dec_conn_usage_count(struct iscsi_conn *conn) | ||
1013 | { | ||
1014 | spin_lock_bh(&conn->conn_usage_lock); | ||
1015 | conn->conn_usage_count--; | ||
1016 | |||
1017 | if (!conn->conn_usage_count && conn->conn_waiting_on_uc) | ||
1018 | complete(&conn->conn_waiting_on_uc_comp); | ||
1019 | |||
1020 | spin_unlock_bh(&conn->conn_usage_lock); | ||
1021 | } | ||
1022 | |||
1023 | void iscsit_inc_conn_usage_count(struct iscsi_conn *conn) | ||
1024 | { | ||
1025 | spin_lock_bh(&conn->conn_usage_lock); | ||
1026 | conn->conn_usage_count++; | ||
1027 | spin_unlock_bh(&conn->conn_usage_lock); | ||
1028 | } | ||
1029 | |||
1030 | static int iscsit_add_nopin(struct iscsi_conn *conn, int want_response) | ||
1031 | { | ||
1032 | u8 state; | ||
1033 | struct iscsi_cmd *cmd; | ||
1034 | |||
1035 | cmd = iscsit_allocate_cmd(conn, GFP_ATOMIC); | ||
1036 | if (!cmd) | ||
1037 | return -1; | ||
1038 | |||
1039 | cmd->iscsi_opcode = ISCSI_OP_NOOP_IN; | ||
1040 | state = (want_response) ? ISTATE_SEND_NOPIN_WANT_RESPONSE : | ||
1041 | ISTATE_SEND_NOPIN_NO_RESPONSE; | ||
1042 | cmd->init_task_tag = 0xFFFFFFFF; | ||
1043 | spin_lock_bh(&conn->sess->ttt_lock); | ||
1044 | cmd->targ_xfer_tag = (want_response) ? conn->sess->targ_xfer_tag++ : | ||
1045 | 0xFFFFFFFF; | ||
1046 | if (want_response && (cmd->targ_xfer_tag == 0xFFFFFFFF)) | ||
1047 | cmd->targ_xfer_tag = conn->sess->targ_xfer_tag++; | ||
1048 | spin_unlock_bh(&conn->sess->ttt_lock); | ||
1049 | |||
1050 | spin_lock_bh(&conn->cmd_lock); | ||
1051 | list_add_tail(&cmd->i_list, &conn->conn_cmd_list); | ||
1052 | spin_unlock_bh(&conn->cmd_lock); | ||
1053 | |||
1054 | if (want_response) | ||
1055 | iscsit_start_nopin_response_timer(conn); | ||
1056 | iscsit_add_cmd_to_immediate_queue(cmd, conn, state); | ||
1057 | |||
1058 | return 0; | ||
1059 | } | ||
1060 | |||
1061 | static void iscsit_handle_nopin_response_timeout(unsigned long data) | ||
1062 | { | ||
1063 | struct iscsi_conn *conn = (struct iscsi_conn *) data; | ||
1064 | |||
1065 | iscsit_inc_conn_usage_count(conn); | ||
1066 | |||
1067 | spin_lock_bh(&conn->nopin_timer_lock); | ||
1068 | if (conn->nopin_response_timer_flags & ISCSI_TF_STOP) { | ||
1069 | spin_unlock_bh(&conn->nopin_timer_lock); | ||
1070 | iscsit_dec_conn_usage_count(conn); | ||
1071 | return; | ||
1072 | } | ||
1073 | |||
1074 | pr_debug("Did not receive response to NOPIN on CID: %hu on" | ||
1075 | " SID: %u, failing connection.\n", conn->cid, | ||
1076 | conn->sess->sid); | ||
1077 | conn->nopin_response_timer_flags &= ~ISCSI_TF_RUNNING; | ||
1078 | spin_unlock_bh(&conn->nopin_timer_lock); | ||
1079 | |||
1080 | { | ||
1081 | struct iscsi_portal_group *tpg = conn->sess->tpg; | ||
1082 | struct iscsi_tiqn *tiqn = tpg->tpg_tiqn; | ||
1083 | |||
1084 | if (tiqn) { | ||
1085 | spin_lock_bh(&tiqn->sess_err_stats.lock); | ||
1086 | strcpy(tiqn->sess_err_stats.last_sess_fail_rem_name, | ||
1087 | (void *)conn->sess->sess_ops->InitiatorName); | ||
1088 | tiqn->sess_err_stats.last_sess_failure_type = | ||
1089 | ISCSI_SESS_ERR_CXN_TIMEOUT; | ||
1090 | tiqn->sess_err_stats.cxn_timeout_errors++; | ||
1091 | conn->sess->conn_timeout_errors++; | ||
1092 | spin_unlock_bh(&tiqn->sess_err_stats.lock); | ||
1093 | } | ||
1094 | } | ||
1095 | |||
1096 | iscsit_cause_connection_reinstatement(conn, 0); | ||
1097 | iscsit_dec_conn_usage_count(conn); | ||
1098 | } | ||
1099 | |||
1100 | void iscsit_mod_nopin_response_timer(struct iscsi_conn *conn) | ||
1101 | { | ||
1102 | struct iscsi_session *sess = conn->sess; | ||
1103 | struct iscsi_node_attrib *na = iscsit_tpg_get_node_attrib(sess); | ||
1104 | |||
1105 | spin_lock_bh(&conn->nopin_timer_lock); | ||
1106 | if (!(conn->nopin_response_timer_flags & ISCSI_TF_RUNNING)) { | ||
1107 | spin_unlock_bh(&conn->nopin_timer_lock); | ||
1108 | return; | ||
1109 | } | ||
1110 | |||
1111 | mod_timer(&conn->nopin_response_timer, | ||
1112 | (get_jiffies_64() + na->nopin_response_timeout * HZ)); | ||
1113 | spin_unlock_bh(&conn->nopin_timer_lock); | ||
1114 | } | ||
1115 | |||
1116 | /* | ||
1117 | * Called with conn->nopin_timer_lock held. | ||
1118 | */ | ||
1119 | void iscsit_start_nopin_response_timer(struct iscsi_conn *conn) | ||
1120 | { | ||
1121 | struct iscsi_session *sess = conn->sess; | ||
1122 | struct iscsi_node_attrib *na = iscsit_tpg_get_node_attrib(sess); | ||
1123 | |||
1124 | spin_lock_bh(&conn->nopin_timer_lock); | ||
1125 | if (conn->nopin_response_timer_flags & ISCSI_TF_RUNNING) { | ||
1126 | spin_unlock_bh(&conn->nopin_timer_lock); | ||
1127 | return; | ||
1128 | } | ||
1129 | |||
1130 | init_timer(&conn->nopin_response_timer); | ||
1131 | conn->nopin_response_timer.expires = | ||
1132 | (get_jiffies_64() + na->nopin_response_timeout * HZ); | ||
1133 | conn->nopin_response_timer.data = (unsigned long)conn; | ||
1134 | conn->nopin_response_timer.function = iscsit_handle_nopin_response_timeout; | ||
1135 | conn->nopin_response_timer_flags &= ~ISCSI_TF_STOP; | ||
1136 | conn->nopin_response_timer_flags |= ISCSI_TF_RUNNING; | ||
1137 | add_timer(&conn->nopin_response_timer); | ||
1138 | |||
1139 | pr_debug("Started NOPIN Response Timer on CID: %d to %u" | ||
1140 | " seconds\n", conn->cid, na->nopin_response_timeout); | ||
1141 | spin_unlock_bh(&conn->nopin_timer_lock); | ||
1142 | } | ||
1143 | |||
1144 | void iscsit_stop_nopin_response_timer(struct iscsi_conn *conn) | ||
1145 | { | ||
1146 | spin_lock_bh(&conn->nopin_timer_lock); | ||
1147 | if (!(conn->nopin_response_timer_flags & ISCSI_TF_RUNNING)) { | ||
1148 | spin_unlock_bh(&conn->nopin_timer_lock); | ||
1149 | return; | ||
1150 | } | ||
1151 | conn->nopin_response_timer_flags |= ISCSI_TF_STOP; | ||
1152 | spin_unlock_bh(&conn->nopin_timer_lock); | ||
1153 | |||
1154 | del_timer_sync(&conn->nopin_response_timer); | ||
1155 | |||
1156 | spin_lock_bh(&conn->nopin_timer_lock); | ||
1157 | conn->nopin_response_timer_flags &= ~ISCSI_TF_RUNNING; | ||
1158 | spin_unlock_bh(&conn->nopin_timer_lock); | ||
1159 | } | ||
1160 | |||
1161 | static void iscsit_handle_nopin_timeout(unsigned long data) | ||
1162 | { | ||
1163 | struct iscsi_conn *conn = (struct iscsi_conn *) data; | ||
1164 | |||
1165 | iscsit_inc_conn_usage_count(conn); | ||
1166 | |||
1167 | spin_lock_bh(&conn->nopin_timer_lock); | ||
1168 | if (conn->nopin_timer_flags & ISCSI_TF_STOP) { | ||
1169 | spin_unlock_bh(&conn->nopin_timer_lock); | ||
1170 | iscsit_dec_conn_usage_count(conn); | ||
1171 | return; | ||
1172 | } | ||
1173 | conn->nopin_timer_flags &= ~ISCSI_TF_RUNNING; | ||
1174 | spin_unlock_bh(&conn->nopin_timer_lock); | ||
1175 | |||
1176 | iscsit_add_nopin(conn, 1); | ||
1177 | iscsit_dec_conn_usage_count(conn); | ||
1178 | } | ||
1179 | |||
1180 | /* | ||
1181 | * Called with conn->nopin_timer_lock held. | ||
1182 | */ | ||
1183 | void __iscsit_start_nopin_timer(struct iscsi_conn *conn) | ||
1184 | { | ||
1185 | struct iscsi_session *sess = conn->sess; | ||
1186 | struct iscsi_node_attrib *na = iscsit_tpg_get_node_attrib(sess); | ||
1187 | /* | ||
1188 | * NOPIN timeout is disabled. | ||
1189 | */ | ||
1190 | if (!na->nopin_timeout) | ||
1191 | return; | ||
1192 | |||
1193 | if (conn->nopin_timer_flags & ISCSI_TF_RUNNING) | ||
1194 | return; | ||
1195 | |||
1196 | init_timer(&conn->nopin_timer); | ||
1197 | conn->nopin_timer.expires = (get_jiffies_64() + na->nopin_timeout * HZ); | ||
1198 | conn->nopin_timer.data = (unsigned long)conn; | ||
1199 | conn->nopin_timer.function = iscsit_handle_nopin_timeout; | ||
1200 | conn->nopin_timer_flags &= ~ISCSI_TF_STOP; | ||
1201 | conn->nopin_timer_flags |= ISCSI_TF_RUNNING; | ||
1202 | add_timer(&conn->nopin_timer); | ||
1203 | |||
1204 | pr_debug("Started NOPIN Timer on CID: %d at %u second" | ||
1205 | " interval\n", conn->cid, na->nopin_timeout); | ||
1206 | } | ||
1207 | |||
1208 | void iscsit_start_nopin_timer(struct iscsi_conn *conn) | ||
1209 | { | ||
1210 | struct iscsi_session *sess = conn->sess; | ||
1211 | struct iscsi_node_attrib *na = iscsit_tpg_get_node_attrib(sess); | ||
1212 | /* | ||
1213 | * NOPIN timeout is disabled.. | ||
1214 | */ | ||
1215 | if (!na->nopin_timeout) | ||
1216 | return; | ||
1217 | |||
1218 | spin_lock_bh(&conn->nopin_timer_lock); | ||
1219 | if (conn->nopin_timer_flags & ISCSI_TF_RUNNING) { | ||
1220 | spin_unlock_bh(&conn->nopin_timer_lock); | ||
1221 | return; | ||
1222 | } | ||
1223 | |||
1224 | init_timer(&conn->nopin_timer); | ||
1225 | conn->nopin_timer.expires = (get_jiffies_64() + na->nopin_timeout * HZ); | ||
1226 | conn->nopin_timer.data = (unsigned long)conn; | ||
1227 | conn->nopin_timer.function = iscsit_handle_nopin_timeout; | ||
1228 | conn->nopin_timer_flags &= ~ISCSI_TF_STOP; | ||
1229 | conn->nopin_timer_flags |= ISCSI_TF_RUNNING; | ||
1230 | add_timer(&conn->nopin_timer); | ||
1231 | |||
1232 | pr_debug("Started NOPIN Timer on CID: %d at %u second" | ||
1233 | " interval\n", conn->cid, na->nopin_timeout); | ||
1234 | spin_unlock_bh(&conn->nopin_timer_lock); | ||
1235 | } | ||
1236 | |||
1237 | void iscsit_stop_nopin_timer(struct iscsi_conn *conn) | ||
1238 | { | ||
1239 | spin_lock_bh(&conn->nopin_timer_lock); | ||
1240 | if (!(conn->nopin_timer_flags & ISCSI_TF_RUNNING)) { | ||
1241 | spin_unlock_bh(&conn->nopin_timer_lock); | ||
1242 | return; | ||
1243 | } | ||
1244 | conn->nopin_timer_flags |= ISCSI_TF_STOP; | ||
1245 | spin_unlock_bh(&conn->nopin_timer_lock); | ||
1246 | |||
1247 | del_timer_sync(&conn->nopin_timer); | ||
1248 | |||
1249 | spin_lock_bh(&conn->nopin_timer_lock); | ||
1250 | conn->nopin_timer_flags &= ~ISCSI_TF_RUNNING; | ||
1251 | spin_unlock_bh(&conn->nopin_timer_lock); | ||
1252 | } | ||
1253 | |||
1254 | int iscsit_send_tx_data( | ||
1255 | struct iscsi_cmd *cmd, | ||
1256 | struct iscsi_conn *conn, | ||
1257 | int use_misc) | ||
1258 | { | ||
1259 | int tx_sent, tx_size; | ||
1260 | u32 iov_count; | ||
1261 | struct kvec *iov; | ||
1262 | |||
1263 | send_data: | ||
1264 | tx_size = cmd->tx_size; | ||
1265 | |||
1266 | if (!use_misc) { | ||
1267 | iov = &cmd->iov_data[0]; | ||
1268 | iov_count = cmd->iov_data_count; | ||
1269 | } else { | ||
1270 | iov = &cmd->iov_misc[0]; | ||
1271 | iov_count = cmd->iov_misc_count; | ||
1272 | } | ||
1273 | |||
1274 | tx_sent = tx_data(conn, &iov[0], iov_count, tx_size); | ||
1275 | if (tx_size != tx_sent) { | ||
1276 | if (tx_sent == -EAGAIN) { | ||
1277 | pr_err("tx_data() returned -EAGAIN\n"); | ||
1278 | goto send_data; | ||
1279 | } else | ||
1280 | return -1; | ||
1281 | } | ||
1282 | cmd->tx_size = 0; | ||
1283 | |||
1284 | return 0; | ||
1285 | } | ||
1286 | |||
1287 | int iscsit_fe_sendpage_sg( | ||
1288 | struct iscsi_cmd *cmd, | ||
1289 | struct iscsi_conn *conn) | ||
1290 | { | ||
1291 | struct scatterlist *sg = cmd->first_data_sg; | ||
1292 | struct kvec iov; | ||
1293 | u32 tx_hdr_size, data_len; | ||
1294 | u32 offset = cmd->first_data_sg_off; | ||
1295 | int tx_sent; | ||
1296 | |||
1297 | send_hdr: | ||
1298 | tx_hdr_size = ISCSI_HDR_LEN; | ||
1299 | if (conn->conn_ops->HeaderDigest) | ||
1300 | tx_hdr_size += ISCSI_CRC_LEN; | ||
1301 | |||
1302 | iov.iov_base = cmd->pdu; | ||
1303 | iov.iov_len = tx_hdr_size; | ||
1304 | |||
1305 | tx_sent = tx_data(conn, &iov, 1, tx_hdr_size); | ||
1306 | if (tx_hdr_size != tx_sent) { | ||
1307 | if (tx_sent == -EAGAIN) { | ||
1308 | pr_err("tx_data() returned -EAGAIN\n"); | ||
1309 | goto send_hdr; | ||
1310 | } | ||
1311 | return -1; | ||
1312 | } | ||
1313 | |||
1314 | data_len = cmd->tx_size - tx_hdr_size - cmd->padding; | ||
1315 | if (conn->conn_ops->DataDigest) | ||
1316 | data_len -= ISCSI_CRC_LEN; | ||
1317 | |||
1318 | /* | ||
1319 | * Perform sendpage() for each page in the scatterlist | ||
1320 | */ | ||
1321 | while (data_len) { | ||
1322 | u32 space = (sg->length - offset); | ||
1323 | u32 sub_len = min_t(u32, data_len, space); | ||
1324 | send_pg: | ||
1325 | tx_sent = conn->sock->ops->sendpage(conn->sock, | ||
1326 | sg_page(sg), sg->offset + offset, sub_len, 0); | ||
1327 | if (tx_sent != sub_len) { | ||
1328 | if (tx_sent == -EAGAIN) { | ||
1329 | pr_err("tcp_sendpage() returned" | ||
1330 | " -EAGAIN\n"); | ||
1331 | goto send_pg; | ||
1332 | } | ||
1333 | |||
1334 | pr_err("tcp_sendpage() failure: %d\n", | ||
1335 | tx_sent); | ||
1336 | return -1; | ||
1337 | } | ||
1338 | |||
1339 | data_len -= sub_len; | ||
1340 | offset = 0; | ||
1341 | sg = sg_next(sg); | ||
1342 | } | ||
1343 | |||
1344 | send_padding: | ||
1345 | if (cmd->padding) { | ||
1346 | struct kvec *iov_p = | ||
1347 | &cmd->iov_data[cmd->iov_data_count-1]; | ||
1348 | |||
1349 | tx_sent = tx_data(conn, iov_p, 1, cmd->padding); | ||
1350 | if (cmd->padding != tx_sent) { | ||
1351 | if (tx_sent == -EAGAIN) { | ||
1352 | pr_err("tx_data() returned -EAGAIN\n"); | ||
1353 | goto send_padding; | ||
1354 | } | ||
1355 | return -1; | ||
1356 | } | ||
1357 | } | ||
1358 | |||
1359 | send_datacrc: | ||
1360 | if (conn->conn_ops->DataDigest) { | ||
1361 | struct kvec *iov_d = | ||
1362 | &cmd->iov_data[cmd->iov_data_count]; | ||
1363 | |||
1364 | tx_sent = tx_data(conn, iov_d, 1, ISCSI_CRC_LEN); | ||
1365 | if (ISCSI_CRC_LEN != tx_sent) { | ||
1366 | if (tx_sent == -EAGAIN) { | ||
1367 | pr_err("tx_data() returned -EAGAIN\n"); | ||
1368 | goto send_datacrc; | ||
1369 | } | ||
1370 | return -1; | ||
1371 | } | ||
1372 | } | ||
1373 | |||
1374 | return 0; | ||
1375 | } | ||
1376 | |||
1377 | /* | ||
1378 | * This function is used for mainly sending a ISCSI_TARG_LOGIN_RSP PDU | ||
1379 | * back to the Initiator when an expection condition occurs with the | ||
1380 | * errors set in status_class and status_detail. | ||
1381 | * | ||
1382 | * Parameters: iSCSI Connection, Status Class, Status Detail. | ||
1383 | * Returns: 0 on success, -1 on error. | ||
1384 | */ | ||
1385 | int iscsit_tx_login_rsp(struct iscsi_conn *conn, u8 status_class, u8 status_detail) | ||
1386 | { | ||
1387 | u8 iscsi_hdr[ISCSI_HDR_LEN]; | ||
1388 | int err; | ||
1389 | struct kvec iov; | ||
1390 | struct iscsi_login_rsp *hdr; | ||
1391 | |||
1392 | iscsit_collect_login_stats(conn, status_class, status_detail); | ||
1393 | |||
1394 | memset(&iov, 0, sizeof(struct kvec)); | ||
1395 | memset(&iscsi_hdr, 0x0, ISCSI_HDR_LEN); | ||
1396 | |||
1397 | hdr = (struct iscsi_login_rsp *)&iscsi_hdr; | ||
1398 | hdr->opcode = ISCSI_OP_LOGIN_RSP; | ||
1399 | hdr->status_class = status_class; | ||
1400 | hdr->status_detail = status_detail; | ||
1401 | hdr->itt = cpu_to_be32(conn->login_itt); | ||
1402 | |||
1403 | iov.iov_base = &iscsi_hdr; | ||
1404 | iov.iov_len = ISCSI_HDR_LEN; | ||
1405 | |||
1406 | PRINT_BUFF(iscsi_hdr, ISCSI_HDR_LEN); | ||
1407 | |||
1408 | err = tx_data(conn, &iov, 1, ISCSI_HDR_LEN); | ||
1409 | if (err != ISCSI_HDR_LEN) { | ||
1410 | pr_err("tx_data returned less than expected\n"); | ||
1411 | return -1; | ||
1412 | } | ||
1413 | |||
1414 | return 0; | ||
1415 | } | ||
1416 | |||
1417 | void iscsit_print_session_params(struct iscsi_session *sess) | ||
1418 | { | ||
1419 | struct iscsi_conn *conn; | ||
1420 | |||
1421 | pr_debug("-----------------------------[Session Params for" | ||
1422 | " SID: %u]-----------------------------\n", sess->sid); | ||
1423 | spin_lock_bh(&sess->conn_lock); | ||
1424 | list_for_each_entry(conn, &sess->sess_conn_list, conn_list) | ||
1425 | iscsi_dump_conn_ops(conn->conn_ops); | ||
1426 | spin_unlock_bh(&sess->conn_lock); | ||
1427 | |||
1428 | iscsi_dump_sess_ops(sess->sess_ops); | ||
1429 | } | ||
1430 | |||
1431 | static int iscsit_do_rx_data( | ||
1432 | struct iscsi_conn *conn, | ||
1433 | struct iscsi_data_count *count) | ||
1434 | { | ||
1435 | int data = count->data_length, rx_loop = 0, total_rx = 0, iov_len; | ||
1436 | u32 rx_marker_val[count->ss_marker_count], rx_marker_iov = 0; | ||
1437 | struct kvec iov[count->ss_iov_count], *iov_p; | ||
1438 | struct msghdr msg; | ||
1439 | |||
1440 | if (!conn || !conn->sock || !conn->conn_ops) | ||
1441 | return -1; | ||
1442 | |||
1443 | memset(&msg, 0, sizeof(struct msghdr)); | ||
1444 | |||
1445 | if (count->sync_and_steering) { | ||
1446 | int size = 0; | ||
1447 | u32 i, orig_iov_count = 0; | ||
1448 | u32 orig_iov_len = 0, orig_iov_loc = 0; | ||
1449 | u32 iov_count = 0, per_iov_bytes = 0; | ||
1450 | u32 *rx_marker, old_rx_marker = 0; | ||
1451 | struct kvec *iov_record; | ||
1452 | |||
1453 | memset(&rx_marker_val, 0, | ||
1454 | count->ss_marker_count * sizeof(u32)); | ||
1455 | memset(&iov, 0, count->ss_iov_count * sizeof(struct kvec)); | ||
1456 | |||
1457 | iov_record = count->iov; | ||
1458 | orig_iov_count = count->iov_count; | ||
1459 | rx_marker = &conn->of_marker; | ||
1460 | |||
1461 | i = 0; | ||
1462 | size = data; | ||
1463 | orig_iov_len = iov_record[orig_iov_loc].iov_len; | ||
1464 | while (size > 0) { | ||
1465 | pr_debug("rx_data: #1 orig_iov_len %u," | ||
1466 | " orig_iov_loc %u\n", orig_iov_len, orig_iov_loc); | ||
1467 | pr_debug("rx_data: #2 rx_marker %u, size" | ||
1468 | " %u\n", *rx_marker, size); | ||
1469 | |||
1470 | if (orig_iov_len >= *rx_marker) { | ||
1471 | iov[iov_count].iov_len = *rx_marker; | ||
1472 | iov[iov_count++].iov_base = | ||
1473 | (iov_record[orig_iov_loc].iov_base + | ||
1474 | per_iov_bytes); | ||
1475 | |||
1476 | iov[iov_count].iov_len = (MARKER_SIZE / 2); | ||
1477 | iov[iov_count++].iov_base = | ||
1478 | &rx_marker_val[rx_marker_iov++]; | ||
1479 | iov[iov_count].iov_len = (MARKER_SIZE / 2); | ||
1480 | iov[iov_count++].iov_base = | ||
1481 | &rx_marker_val[rx_marker_iov++]; | ||
1482 | old_rx_marker = *rx_marker; | ||
1483 | |||
1484 | /* | ||
1485 | * OFMarkInt is in 32-bit words. | ||
1486 | */ | ||
1487 | *rx_marker = (conn->conn_ops->OFMarkInt * 4); | ||
1488 | size -= old_rx_marker; | ||
1489 | orig_iov_len -= old_rx_marker; | ||
1490 | per_iov_bytes += old_rx_marker; | ||
1491 | |||
1492 | pr_debug("rx_data: #3 new_rx_marker" | ||
1493 | " %u, size %u\n", *rx_marker, size); | ||
1494 | } else { | ||
1495 | iov[iov_count].iov_len = orig_iov_len; | ||
1496 | iov[iov_count++].iov_base = | ||
1497 | (iov_record[orig_iov_loc].iov_base + | ||
1498 | per_iov_bytes); | ||
1499 | |||
1500 | per_iov_bytes = 0; | ||
1501 | *rx_marker -= orig_iov_len; | ||
1502 | size -= orig_iov_len; | ||
1503 | |||
1504 | if (size) | ||
1505 | orig_iov_len = | ||
1506 | iov_record[++orig_iov_loc].iov_len; | ||
1507 | |||
1508 | pr_debug("rx_data: #4 new_rx_marker" | ||
1509 | " %u, size %u\n", *rx_marker, size); | ||
1510 | } | ||
1511 | } | ||
1512 | data += (rx_marker_iov * (MARKER_SIZE / 2)); | ||
1513 | |||
1514 | iov_p = &iov[0]; | ||
1515 | iov_len = iov_count; | ||
1516 | |||
1517 | if (iov_count > count->ss_iov_count) { | ||
1518 | pr_err("iov_count: %d, count->ss_iov_count:" | ||
1519 | " %d\n", iov_count, count->ss_iov_count); | ||
1520 | return -1; | ||
1521 | } | ||
1522 | if (rx_marker_iov > count->ss_marker_count) { | ||
1523 | pr_err("rx_marker_iov: %d, count->ss_marker" | ||
1524 | "_count: %d\n", rx_marker_iov, | ||
1525 | count->ss_marker_count); | ||
1526 | return -1; | ||
1527 | } | ||
1528 | } else { | ||
1529 | iov_p = count->iov; | ||
1530 | iov_len = count->iov_count; | ||
1531 | } | ||
1532 | |||
1533 | while (total_rx < data) { | ||
1534 | rx_loop = kernel_recvmsg(conn->sock, &msg, iov_p, iov_len, | ||
1535 | (data - total_rx), MSG_WAITALL); | ||
1536 | if (rx_loop <= 0) { | ||
1537 | pr_debug("rx_loop: %d total_rx: %d\n", | ||
1538 | rx_loop, total_rx); | ||
1539 | return rx_loop; | ||
1540 | } | ||
1541 | total_rx += rx_loop; | ||
1542 | pr_debug("rx_loop: %d, total_rx: %d, data: %d\n", | ||
1543 | rx_loop, total_rx, data); | ||
1544 | } | ||
1545 | |||
1546 | if (count->sync_and_steering) { | ||
1547 | int j; | ||
1548 | for (j = 0; j < rx_marker_iov; j++) { | ||
1549 | pr_debug("rx_data: #5 j: %d, offset: %d\n", | ||
1550 | j, rx_marker_val[j]); | ||
1551 | conn->of_marker_offset = rx_marker_val[j]; | ||
1552 | } | ||
1553 | total_rx -= (rx_marker_iov * (MARKER_SIZE / 2)); | ||
1554 | } | ||
1555 | |||
1556 | return total_rx; | ||
1557 | } | ||
1558 | |||
1559 | static int iscsit_do_tx_data( | ||
1560 | struct iscsi_conn *conn, | ||
1561 | struct iscsi_data_count *count) | ||
1562 | { | ||
1563 | int data = count->data_length, total_tx = 0, tx_loop = 0, iov_len; | ||
1564 | u32 tx_marker_val[count->ss_marker_count], tx_marker_iov = 0; | ||
1565 | struct kvec iov[count->ss_iov_count], *iov_p; | ||
1566 | struct msghdr msg; | ||
1567 | |||
1568 | if (!conn || !conn->sock || !conn->conn_ops) | ||
1569 | return -1; | ||
1570 | |||
1571 | if (data <= 0) { | ||
1572 | pr_err("Data length is: %d\n", data); | ||
1573 | return -1; | ||
1574 | } | ||
1575 | |||
1576 | memset(&msg, 0, sizeof(struct msghdr)); | ||
1577 | |||
1578 | if (count->sync_and_steering) { | ||
1579 | int size = 0; | ||
1580 | u32 i, orig_iov_count = 0; | ||
1581 | u32 orig_iov_len = 0, orig_iov_loc = 0; | ||
1582 | u32 iov_count = 0, per_iov_bytes = 0; | ||
1583 | u32 *tx_marker, old_tx_marker = 0; | ||
1584 | struct kvec *iov_record; | ||
1585 | |||
1586 | memset(&tx_marker_val, 0, | ||
1587 | count->ss_marker_count * sizeof(u32)); | ||
1588 | memset(&iov, 0, count->ss_iov_count * sizeof(struct kvec)); | ||
1589 | |||
1590 | iov_record = count->iov; | ||
1591 | orig_iov_count = count->iov_count; | ||
1592 | tx_marker = &conn->if_marker; | ||
1593 | |||
1594 | i = 0; | ||
1595 | size = data; | ||
1596 | orig_iov_len = iov_record[orig_iov_loc].iov_len; | ||
1597 | while (size > 0) { | ||
1598 | pr_debug("tx_data: #1 orig_iov_len %u," | ||
1599 | " orig_iov_loc %u\n", orig_iov_len, orig_iov_loc); | ||
1600 | pr_debug("tx_data: #2 tx_marker %u, size" | ||
1601 | " %u\n", *tx_marker, size); | ||
1602 | |||
1603 | if (orig_iov_len >= *tx_marker) { | ||
1604 | iov[iov_count].iov_len = *tx_marker; | ||
1605 | iov[iov_count++].iov_base = | ||
1606 | (iov_record[orig_iov_loc].iov_base + | ||
1607 | per_iov_bytes); | ||
1608 | |||
1609 | tx_marker_val[tx_marker_iov] = | ||
1610 | (size - *tx_marker); | ||
1611 | iov[iov_count].iov_len = (MARKER_SIZE / 2); | ||
1612 | iov[iov_count++].iov_base = | ||
1613 | &tx_marker_val[tx_marker_iov++]; | ||
1614 | iov[iov_count].iov_len = (MARKER_SIZE / 2); | ||
1615 | iov[iov_count++].iov_base = | ||
1616 | &tx_marker_val[tx_marker_iov++]; | ||
1617 | old_tx_marker = *tx_marker; | ||
1618 | |||
1619 | /* | ||
1620 | * IFMarkInt is in 32-bit words. | ||
1621 | */ | ||
1622 | *tx_marker = (conn->conn_ops->IFMarkInt * 4); | ||
1623 | size -= old_tx_marker; | ||
1624 | orig_iov_len -= old_tx_marker; | ||
1625 | per_iov_bytes += old_tx_marker; | ||
1626 | |||
1627 | pr_debug("tx_data: #3 new_tx_marker" | ||
1628 | " %u, size %u\n", *tx_marker, size); | ||
1629 | pr_debug("tx_data: #4 offset %u\n", | ||
1630 | tx_marker_val[tx_marker_iov-1]); | ||
1631 | } else { | ||
1632 | iov[iov_count].iov_len = orig_iov_len; | ||
1633 | iov[iov_count++].iov_base | ||
1634 | = (iov_record[orig_iov_loc].iov_base + | ||
1635 | per_iov_bytes); | ||
1636 | |||
1637 | per_iov_bytes = 0; | ||
1638 | *tx_marker -= orig_iov_len; | ||
1639 | size -= orig_iov_len; | ||
1640 | |||
1641 | if (size) | ||
1642 | orig_iov_len = | ||
1643 | iov_record[++orig_iov_loc].iov_len; | ||
1644 | |||
1645 | pr_debug("tx_data: #5 new_tx_marker" | ||
1646 | " %u, size %u\n", *tx_marker, size); | ||
1647 | } | ||
1648 | } | ||
1649 | |||
1650 | data += (tx_marker_iov * (MARKER_SIZE / 2)); | ||
1651 | |||
1652 | iov_p = &iov[0]; | ||
1653 | iov_len = iov_count; | ||
1654 | |||
1655 | if (iov_count > count->ss_iov_count) { | ||
1656 | pr_err("iov_count: %d, count->ss_iov_count:" | ||
1657 | " %d\n", iov_count, count->ss_iov_count); | ||
1658 | return -1; | ||
1659 | } | ||
1660 | if (tx_marker_iov > count->ss_marker_count) { | ||
1661 | pr_err("tx_marker_iov: %d, count->ss_marker" | ||
1662 | "_count: %d\n", tx_marker_iov, | ||
1663 | count->ss_marker_count); | ||
1664 | return -1; | ||
1665 | } | ||
1666 | } else { | ||
1667 | iov_p = count->iov; | ||
1668 | iov_len = count->iov_count; | ||
1669 | } | ||
1670 | |||
1671 | while (total_tx < data) { | ||
1672 | tx_loop = kernel_sendmsg(conn->sock, &msg, iov_p, iov_len, | ||
1673 | (data - total_tx)); | ||
1674 | if (tx_loop <= 0) { | ||
1675 | pr_debug("tx_loop: %d total_tx %d\n", | ||
1676 | tx_loop, total_tx); | ||
1677 | return tx_loop; | ||
1678 | } | ||
1679 | total_tx += tx_loop; | ||
1680 | pr_debug("tx_loop: %d, total_tx: %d, data: %d\n", | ||
1681 | tx_loop, total_tx, data); | ||
1682 | } | ||
1683 | |||
1684 | if (count->sync_and_steering) | ||
1685 | total_tx -= (tx_marker_iov * (MARKER_SIZE / 2)); | ||
1686 | |||
1687 | return total_tx; | ||
1688 | } | ||
1689 | |||
1690 | int rx_data( | ||
1691 | struct iscsi_conn *conn, | ||
1692 | struct kvec *iov, | ||
1693 | int iov_count, | ||
1694 | int data) | ||
1695 | { | ||
1696 | struct iscsi_data_count c; | ||
1697 | |||
1698 | if (!conn || !conn->sock || !conn->conn_ops) | ||
1699 | return -1; | ||
1700 | |||
1701 | memset(&c, 0, sizeof(struct iscsi_data_count)); | ||
1702 | c.iov = iov; | ||
1703 | c.iov_count = iov_count; | ||
1704 | c.data_length = data; | ||
1705 | c.type = ISCSI_RX_DATA; | ||
1706 | |||
1707 | if (conn->conn_ops->OFMarker && | ||
1708 | (conn->conn_state >= TARG_CONN_STATE_LOGGED_IN)) { | ||
1709 | if (iscsit_determine_sync_and_steering_counts(conn, &c) < 0) | ||
1710 | return -1; | ||
1711 | } | ||
1712 | |||
1713 | return iscsit_do_rx_data(conn, &c); | ||
1714 | } | ||
1715 | |||
1716 | int tx_data( | ||
1717 | struct iscsi_conn *conn, | ||
1718 | struct kvec *iov, | ||
1719 | int iov_count, | ||
1720 | int data) | ||
1721 | { | ||
1722 | struct iscsi_data_count c; | ||
1723 | |||
1724 | if (!conn || !conn->sock || !conn->conn_ops) | ||
1725 | return -1; | ||
1726 | |||
1727 | memset(&c, 0, sizeof(struct iscsi_data_count)); | ||
1728 | c.iov = iov; | ||
1729 | c.iov_count = iov_count; | ||
1730 | c.data_length = data; | ||
1731 | c.type = ISCSI_TX_DATA; | ||
1732 | |||
1733 | if (conn->conn_ops->IFMarker && | ||
1734 | (conn->conn_state >= TARG_CONN_STATE_LOGGED_IN)) { | ||
1735 | if (iscsit_determine_sync_and_steering_counts(conn, &c) < 0) | ||
1736 | return -1; | ||
1737 | } | ||
1738 | |||
1739 | return iscsit_do_tx_data(conn, &c); | ||
1740 | } | ||
1741 | |||
1742 | void iscsit_collect_login_stats( | ||
1743 | struct iscsi_conn *conn, | ||
1744 | u8 status_class, | ||
1745 | u8 status_detail) | ||
1746 | { | ||
1747 | struct iscsi_param *intrname = NULL; | ||
1748 | struct iscsi_tiqn *tiqn; | ||
1749 | struct iscsi_login_stats *ls; | ||
1750 | |||
1751 | tiqn = iscsit_snmp_get_tiqn(conn); | ||
1752 | if (!tiqn) | ||
1753 | return; | ||
1754 | |||
1755 | ls = &tiqn->login_stats; | ||
1756 | |||
1757 | spin_lock(&ls->lock); | ||
1758 | if (!strcmp(conn->login_ip, ls->last_intr_fail_ip_addr) && | ||
1759 | ((get_jiffies_64() - ls->last_fail_time) < 10)) { | ||
1760 | /* We already have the failure info for this login */ | ||
1761 | spin_unlock(&ls->lock); | ||
1762 | return; | ||
1763 | } | ||
1764 | |||
1765 | if (status_class == ISCSI_STATUS_CLS_SUCCESS) | ||
1766 | ls->accepts++; | ||
1767 | else if (status_class == ISCSI_STATUS_CLS_REDIRECT) { | ||
1768 | ls->redirects++; | ||
1769 | ls->last_fail_type = ISCSI_LOGIN_FAIL_REDIRECT; | ||
1770 | } else if ((status_class == ISCSI_STATUS_CLS_INITIATOR_ERR) && | ||
1771 | (status_detail == ISCSI_LOGIN_STATUS_AUTH_FAILED)) { | ||
1772 | ls->authenticate_fails++; | ||
1773 | ls->last_fail_type = ISCSI_LOGIN_FAIL_AUTHENTICATE; | ||
1774 | } else if ((status_class == ISCSI_STATUS_CLS_INITIATOR_ERR) && | ||
1775 | (status_detail == ISCSI_LOGIN_STATUS_TGT_FORBIDDEN)) { | ||
1776 | ls->authorize_fails++; | ||
1777 | ls->last_fail_type = ISCSI_LOGIN_FAIL_AUTHORIZE; | ||
1778 | } else if ((status_class == ISCSI_STATUS_CLS_INITIATOR_ERR) && | ||
1779 | (status_detail == ISCSI_LOGIN_STATUS_INIT_ERR)) { | ||
1780 | ls->negotiate_fails++; | ||
1781 | ls->last_fail_type = ISCSI_LOGIN_FAIL_NEGOTIATE; | ||
1782 | } else { | ||
1783 | ls->other_fails++; | ||
1784 | ls->last_fail_type = ISCSI_LOGIN_FAIL_OTHER; | ||
1785 | } | ||
1786 | |||
1787 | /* Save initiator name, ip address and time, if it is a failed login */ | ||
1788 | if (status_class != ISCSI_STATUS_CLS_SUCCESS) { | ||
1789 | if (conn->param_list) | ||
1790 | intrname = iscsi_find_param_from_key(INITIATORNAME, | ||
1791 | conn->param_list); | ||
1792 | strcpy(ls->last_intr_fail_name, | ||
1793 | (intrname ? intrname->value : "Unknown")); | ||
1794 | |||
1795 | ls->last_intr_fail_ip_family = conn->sock->sk->sk_family; | ||
1796 | snprintf(ls->last_intr_fail_ip_addr, IPV6_ADDRESS_SPACE, | ||
1797 | "%s", conn->login_ip); | ||
1798 | ls->last_fail_time = get_jiffies_64(); | ||
1799 | } | ||
1800 | |||
1801 | spin_unlock(&ls->lock); | ||
1802 | } | ||
1803 | |||
1804 | struct iscsi_tiqn *iscsit_snmp_get_tiqn(struct iscsi_conn *conn) | ||
1805 | { | ||
1806 | struct iscsi_portal_group *tpg; | ||
1807 | |||
1808 | if (!conn || !conn->sess) | ||
1809 | return NULL; | ||
1810 | |||
1811 | tpg = conn->sess->tpg; | ||
1812 | if (!tpg) | ||
1813 | return NULL; | ||
1814 | |||
1815 | if (!tpg->tpg_tiqn) | ||
1816 | return NULL; | ||
1817 | |||
1818 | return tpg->tpg_tiqn; | ||
1819 | } | ||
diff --git a/drivers/target/iscsi/iscsi_target_util.h b/drivers/target/iscsi/iscsi_target_util.h new file mode 100644 index 000000000000..2cd49d607bda --- /dev/null +++ b/drivers/target/iscsi/iscsi_target_util.h | |||
@@ -0,0 +1,60 @@ | |||
1 | #ifndef ISCSI_TARGET_UTIL_H | ||
2 | #define ISCSI_TARGET_UTIL_H | ||
3 | |||
4 | #define MARKER_SIZE 8 | ||
5 | |||
6 | extern int iscsit_add_r2t_to_list(struct iscsi_cmd *, u32, u32, int, u32); | ||
7 | extern struct iscsi_r2t *iscsit_get_r2t_for_eos(struct iscsi_cmd *, u32, u32); | ||
8 | extern struct iscsi_r2t *iscsit_get_r2t_from_list(struct iscsi_cmd *); | ||
9 | extern void iscsit_free_r2t(struct iscsi_r2t *, struct iscsi_cmd *); | ||
10 | extern void iscsit_free_r2ts_from_list(struct iscsi_cmd *); | ||
11 | extern struct iscsi_cmd *iscsit_allocate_cmd(struct iscsi_conn *, gfp_t); | ||
12 | extern struct iscsi_cmd *iscsit_allocate_se_cmd(struct iscsi_conn *, u32, int, int); | ||
13 | extern struct iscsi_cmd *iscsit_allocate_se_cmd_for_tmr(struct iscsi_conn *, u8); | ||
14 | extern int iscsit_decide_list_to_build(struct iscsi_cmd *, u32); | ||
15 | extern struct iscsi_seq *iscsit_get_seq_holder_for_datain(struct iscsi_cmd *, u32); | ||
16 | extern struct iscsi_seq *iscsit_get_seq_holder_for_r2t(struct iscsi_cmd *); | ||
17 | extern struct iscsi_r2t *iscsit_get_holder_for_r2tsn(struct iscsi_cmd *, u32); | ||
18 | int iscsit_sequence_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, u32 cmdsn); | ||
19 | extern int iscsit_check_unsolicited_dataout(struct iscsi_cmd *, unsigned char *); | ||
20 | extern struct iscsi_cmd *iscsit_find_cmd_from_itt(struct iscsi_conn *, u32); | ||
21 | extern struct iscsi_cmd *iscsit_find_cmd_from_itt_or_dump(struct iscsi_conn *, | ||
22 | u32, u32); | ||
23 | extern struct iscsi_cmd *iscsit_find_cmd_from_ttt(struct iscsi_conn *, u32); | ||
24 | extern int iscsit_find_cmd_for_recovery(struct iscsi_session *, struct iscsi_cmd **, | ||
25 | struct iscsi_conn_recovery **, u32); | ||
26 | extern void iscsit_add_cmd_to_immediate_queue(struct iscsi_cmd *, struct iscsi_conn *, u8); | ||
27 | extern struct iscsi_queue_req *iscsit_get_cmd_from_immediate_queue(struct iscsi_conn *); | ||
28 | extern void iscsit_add_cmd_to_response_queue(struct iscsi_cmd *, struct iscsi_conn *, u8); | ||
29 | extern struct iscsi_queue_req *iscsit_get_cmd_from_response_queue(struct iscsi_conn *); | ||
30 | extern void iscsit_remove_cmd_from_tx_queues(struct iscsi_cmd *, struct iscsi_conn *); | ||
31 | extern void iscsit_free_queue_reqs_for_conn(struct iscsi_conn *); | ||
32 | extern void iscsit_release_cmd(struct iscsi_cmd *); | ||
33 | extern int iscsit_check_session_usage_count(struct iscsi_session *); | ||
34 | extern void iscsit_dec_session_usage_count(struct iscsi_session *); | ||
35 | extern void iscsit_inc_session_usage_count(struct iscsi_session *); | ||
36 | extern int iscsit_set_sync_and_steering_values(struct iscsi_conn *); | ||
37 | extern struct iscsi_conn *iscsit_get_conn_from_cid(struct iscsi_session *, u16); | ||
38 | extern struct iscsi_conn *iscsit_get_conn_from_cid_rcfr(struct iscsi_session *, u16); | ||
39 | extern void iscsit_check_conn_usage_count(struct iscsi_conn *); | ||
40 | extern void iscsit_dec_conn_usage_count(struct iscsi_conn *); | ||
41 | extern void iscsit_inc_conn_usage_count(struct iscsi_conn *); | ||
42 | extern void iscsit_mod_nopin_response_timer(struct iscsi_conn *); | ||
43 | extern void iscsit_start_nopin_response_timer(struct iscsi_conn *); | ||
44 | extern void iscsit_stop_nopin_response_timer(struct iscsi_conn *); | ||
45 | extern void __iscsit_start_nopin_timer(struct iscsi_conn *); | ||
46 | extern void iscsit_start_nopin_timer(struct iscsi_conn *); | ||
47 | extern void iscsit_stop_nopin_timer(struct iscsi_conn *); | ||
48 | extern int iscsit_send_tx_data(struct iscsi_cmd *, struct iscsi_conn *, int); | ||
49 | extern int iscsit_fe_sendpage_sg(struct iscsi_cmd *, struct iscsi_conn *); | ||
50 | extern int iscsit_tx_login_rsp(struct iscsi_conn *, u8, u8); | ||
51 | extern void iscsit_print_session_params(struct iscsi_session *); | ||
52 | extern int iscsit_print_dev_to_proc(char *, char **, off_t, int); | ||
53 | extern int iscsit_print_sessions_to_proc(char *, char **, off_t, int); | ||
54 | extern int iscsit_print_tpg_to_proc(char *, char **, off_t, int); | ||
55 | extern int rx_data(struct iscsi_conn *, struct kvec *, int, int); | ||
56 | extern int tx_data(struct iscsi_conn *, struct kvec *, int, int); | ||
57 | extern void iscsit_collect_login_stats(struct iscsi_conn *, u8, u8); | ||
58 | extern struct iscsi_tiqn *iscsit_snmp_get_tiqn(struct iscsi_conn *); | ||
59 | |||
60 | #endif /*** ISCSI_TARGET_UTIL_H ***/ | ||