1 /*
2 * IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
3 * By downloading, copying, installing or using the software you agree
4 * to this license. If you do not agree to this license, do not
5 * download, install, copy or use the software.
6 *
7 * Intel License Agreement
8 *
9 * Copyright (c) 2000, Intel Corporation
10 * All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 *
16 * -Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 *
19 * -Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in the
21 * documentation and/or other materials provided with the
22 * distribution.
23 *
24 * -The name of Intel Corporation may not be used to endorse or
25 * promote products derived from this software without specific prior
26 * written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
31 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL
32 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
35 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
36 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
37 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
38 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 * SUCH DAMAGE.
40 */
41 #include "config.h"
42
43 #include <sys/types.h>
44
45 #ifdef HAVE_SYS_TIME_H
46 #include <sys/time.h>
47 #endif
48
49 #ifdef HAVE_SYS_SOCKET_H
50 #include <sys/socket.h>
51 #endif
52
53 #ifdef HAVE_NETINET_IN_H
54 #include <netinet/in.h>
55 #endif
56
57 #ifdef HAVE_NETINET_TCP_H
58 #include <netinet/tcp.h>
59 #endif
60
61 #ifdef HAVE_INTTYPES_H
62 #include <inttypes.h>
63 #endif
64
65 #ifdef HAVE_SIGNAL_H
66 #include <signal.h>
67 #endif
68
69 #include <stdio.h>
70 #include <stdlib.h>
71
72 #ifdef HAVE_STRING_H
73 #include <string.h>
74 #endif
75
76 #include <unistd.h>
77
78 #include "iscsiprotocol.h"
79 #include "initiator.h"
80
81 #include "iscsi.h"
82
83 static initiator_target_t g_target[CONFIG_INITIATOR_NUM_TARGETS];
84
85 /*
86 * Globals
87 */
88 static uint32_t g_tag;
89 static iscsi_spin_t g_tag_spin;
90 static hash_t g_tag_hash;
91 static iscsi_worker_t g_enqueue_worker;
92 static iscsi_queue_t g_enqueue_q;
93 static iscsi_queue_t g_session_q;
94 static int g_initiator_state;
95
96 /* Testing of initiator_abort */
97
98 static initiator_cmd_t *g_cmd = NULL;
99
100 /*
101 * Enqueue worker functions. The enqueue worker is responsible for enqueing
102 * all iSCSI commands to one of the Tx workers. It is also the thread
103 * responsible for initializing sessions, discovering targets and getting
104 * each session into full feature phase.
105 */
106
107 static int enqueue_worker_proc(void *);
108 static int login_phase_i(initiator_session_t *, char *, int);
109 static int logout_phase_i(initiator_session_t *);
110
111 /*
112 * Tx functions. initiator_cmd_t pointers are enqueued to the Tx worker
113 * for a given session by the enqueue worker. The Tx worker will send out these
114 * commands and wait for the Rx worker to process the response. The pointer is
115 * inserted into the hashtable g_tag_hash, keyed by the initiator tag of the iSCSI
116 * commands.
117 */
118
119 static int tx_worker_proc_i(void *);
120 static int text_command_i(initiator_cmd_t *);
121 static int login_command_i(initiator_cmd_t *);
122 static int logout_command_i(initiator_cmd_t *);
123 static int scsi_command_i(initiator_cmd_t *);
124 static int nop_out_i(initiator_cmd_t *);
125
126
127 /*
128 * Rx functions. Upon receipt of an incoming PDU, the Rx worker will first
129 * extract the tag (if it exists for the PDU) and then the associated
130 * initiator_cmd_t pointer stored in the hash table. One of Rx functions
131 * will be called to processs the PDU. The Rx worker will invoke the callback
132 * function associated with the command once the command has been retired.
133 */
134
135 static int rx_worker_proc_i(void *);
136 static int login_response_i(initiator_session_t *, initiator_cmd_t *, uint8_t *);
137 static int text_response_i(initiator_session_t *, initiator_cmd_t *, uint8_t *);
138 static int logout_response_i(initiator_session_t *, initiator_cmd_t *, uint8_t *);
139 static int scsi_response_i(initiator_session_t *, initiator_cmd_t *, uint8_t *);
140 static int scsi_read_data_i(initiator_session_t *, initiator_cmd_t *, uint8_t *);
141 static int scsi_r2t_i(initiator_session_t *, initiator_cmd_t *, uint8_t *);
142 static int nop_in_i(initiator_session_t *, initiator_cmd_t *, uint8_t *);
143 static int reject_i(initiator_session_t *, uint8_t *);
144 static int async_msg_i(initiator_session_t *, uint8_t *);
145
146
147 /*
148 * Misc. Prototypes
149 */
150
151
152 static int session_init_i(initiator_session_t **, uint64_t );
153 static int session_destroy_i(initiator_session_t *);
154 static int wait_callback_i(void *);
155 static int discovery_phase(int, strv_t *);
156
157 /*
158 * Private Functions
159 */
160
161 #if 0
162 static void
163 dump_session(initiator_session_t * sess)
164 {
165 iscsi_parameter_value_t *vp;
166 iscsi_parameter_t *ip;
167
168 for (ip = sess->params ; ip ; ip = ip->next) {
169 printf("Key: %s Type: %d\n",ip->key,ip->type);
170 for (vp = ip->value_l ; vp ; vp = vp->next) {
171 printf("Value: %s\n",vp->value);
172 }
173 }
174 }
175 #endif
176
177 /* This function reads the target IP and target name information */
178 /* from the input configuration file, and populates the */
179 /* g_target data structure fields. */
180 static int
get_target_config(const char * hostname,int port)181 get_target_config(const char *hostname, int port)
182 {
183 int i;
184
185 for (i = 0 ; i < CONFIG_INITIATOR_NUM_TARGETS ; i++) {
186 (void) strlcpy(g_target[i].name, hostname,
187 sizeof(g_target[i].name));
188 g_target[i].port = port;
189 }
190 return 0;
191 }
192
193 static int
session_init_i(initiator_session_t ** sess,uint64_t isid)194 session_init_i(initiator_session_t ** sess, uint64_t isid)
195 {
196 initiator_session_t *s;
197 iscsi_parameter_t **l;
198 char *user;
199 int auth_type;
200 int mutual_auth;
201 int one = 1;
202
203 iscsi_trace(TRACE_ISCSI_DEBUG, "initializing session %" PRIu64 "\n", isid);
204
205 /* Get free session */
206 if ((*sess = iscsi_queue_remove(&g_session_q)) == NULL) {
207 iscsi_err(__FILE__, __LINE__, "iscsi_queue_remove() failed\n");
208 return -1;
209 }
210 s = *sess;
211 user = NULL;
212 auth_type = s->sess_params.auth_type;
213 if (s->sess_params.cred.user && auth_type != AuthNone) {
214 user = s->sess_params.cred.user;
215 }
216 mutual_auth = s->sess_params.mutual_auth;
217 (void) memset(s, 0x0, sizeof(*s));
218 s->state = INITIATOR_SESSION_STATE_INITIALIZING;
219 s->isid = s->tx_worker.id = s->rx_worker.id = (int)isid;
220 s->cmds = NULL;
221 s->sess_params.cred.user = user;
222 s->sess_params.auth_type = auth_type;
223 s->sess_params.mutual_auth = mutual_auth;
224
225 iscsi_spin_init(&s->cmds_spin);
226 g_target[(int)isid].has_session = 1;
227
228 /* Create socket */
229 if (!iscsi_sock_create(&s->sock)) {
230 iscsi_err(__FILE__, __LINE__, "iscsi_sock_create() failed\n");
231 return -1;
232 }
233 if (!iscsi_sock_setsockopt(&s->sock, SOL_TCP, TCP_NODELAY, &one, sizeof(one))) {
234 iscsi_err(__FILE__, __LINE__, "iscsi_sock_setsockopt() failed\n");
235 return -1;
236 }
237
238 /* Initialize wait queues */
239
240 ISCSI_MUTEX_INIT(&s->tx_worker.work_mutex, return -1);
241 ISCSI_COND_INIT(&s->tx_worker.work_cond, return -1);
242 ISCSI_MUTEX_INIT(&s->tx_worker.exit_mutex, return -1);
243 ISCSI_COND_INIT(&s->tx_worker.exit_cond, return -1);
244 ISCSI_MUTEX_INIT(&s->rx_worker.work_mutex, return -1);
245 ISCSI_COND_INIT(&s->rx_worker.work_cond, return -1);
246 ISCSI_MUTEX_INIT(&s->rx_worker.exit_mutex, return -1);
247 ISCSI_COND_INIT(&s->rx_worker.exit_cond, return -1);
248
249 /* Build parameter list */
250
251 /*
252 * ISCSI_PARAM_TYPE_LIST format: <type> <key> <dflt> <valid list values>
253 * ISCSI_PARAM_TYPE_BINARY format: <type> <key> <dflt> <valid binary values>
254 * ISCSI_PARAM_TYPE_NUMERICAL format: <type> <key> <dflt> <max>
255 * ISCSI_PARAM_TYPE_DECLARATIVE format: <type> <key> <dflt> ""
256 */
257
258 s->params = NULL;
259 l = &(s->params);
260 /* CHAP Support Parameters */
261 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_LIST, "AuthMethod", "None", "CHAP,None", return -1);
262 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_LIST, "CHAP_A", "None", "5", return -1);
263 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "CHAP_N", "", "", return -1);
264 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "CHAP_R", "", "", return -1);
265 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "CHAP_I", "", "", return -1);
266 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "CHAP_C", "", "", return -1);
267 /* CHAP Support Parameters */
268
269 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_LIST, "HeaderDigest", "None", "None", return -1);
270 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_LIST, "DataDigest", "None", "None", return -1);
271
272 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL, "MaxConnections", "1", "1", return -1);
273 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "SendTargets", "", "", return -1);
274 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARE_MULTI, "TargetName", "", "", return -1);
275 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "InitiatorName", "iqn.1994-04.org.NetBSD:iscsi-initiator", "", return -1);
276 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "TargetAlias", "", "", return -1);
277 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "InitiatorAlias", "", "", return -1);
278 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARE_MULTI, "TargetAddress", "", "", return -1);
279 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_BINARY_OR, "InitialR2T", "Yes", "Yes,No", return -1);
280
281 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_BINARY_AND, "ImmediateData", "Yes", "Yes,No", return -1);
282 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL_Z, "MaxRecvDataSegmentLength", "8192", "16777215", return -1);
283 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL_Z, "MaxBurstLength", "262144", "16777215", return -1);
284 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL_Z, "FirstBurstLength", "65536", "16777215", return -1);
285 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "TargetPortalGroupTag", "1", "65535", return -1);
286 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL, "DefaultTime2Wait", "2", "2", return -1);
287 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL, "DefaultTime2Retain", "20", "20", return -1);
288 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL, "MaxOutstandingR2T", "1", "1", return -1);
289 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_BINARY_OR, "DataPDUInOrder", "Yes", "Yes,No", return -1);
290 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_BINARY_OR, "DataSequenceInOrder", "Yes", "Yes,No", return -1);
291 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_NUMERICAL, "ErrorRecoveryLevel", "0", "0", return -1);
292 PARAM_LIST_ADD(l, ISCSI_PARAM_TYPE_DECLARATIVE, "SessionType", "Normal", "Normal,Discovery", return -1);
293
294 /* Start Tx worker */
295
296 iscsi_trace(TRACE_ISCSI_DEBUG, "starting Tx worker %" PRIu64 "\n", isid);
297 if (iscsi_queue_init(&s->tx_queue, CONFIG_INITIATOR_QUEUE_DEPTH) == -1) {
298 iscsi_err(__FILE__, __LINE__, "iscsi_queue_init() failed\n");
299 return -1;
300 }
301 ISCSI_LOCK(&s->tx_worker.exit_mutex, return -1);
302 if (iscsi_thread_create(&s->tx_worker.thread,
303 (void *) tx_worker_proc_i, &s->tx_worker) != 0) {
304 iscsi_err(__FILE__, __LINE__,
305 "iscsi_threads_create() failed\n");
306 return -1;
307 }
308 ISCSI_WAIT(&s->tx_worker.exit_cond, &s->tx_worker.exit_mutex,
309 return -1);
310 ISCSI_UNLOCK(&s->tx_worker.exit_mutex, return -1);
311 if (s->state == INITIATOR_SESSION_STATE_DESTROYING) {
312 iscsi_trace(TRACE_ISCSI_DEBUG,
313 "session %" PRIu64 " is being destroyed, exiting\n", isid);
314 return -1;
315 }
316 if (s->tx_worker.state & ISCSI_WORKER_STATE_ERROR) {
317 iscsi_err(__FILE__, __LINE__,
318 "Tx worker %" PRIu64 " started with an error\n", isid);
319 return -1;
320 }
321 iscsi_trace(TRACE_ISCSI_DEBUG, "got signal from Tx worker\n");
322 s->state = INITIATOR_SESSION_STATE_INITIALIZED;
323
324 return 0;
325 }
326
327 static int
session_destroy_i(initiator_session_t * sess)328 session_destroy_i(initiator_session_t * sess)
329 {
330 initiator_cmd_t *ptr;
331 uint64_t isid = sess->isid;
332
333 if (sess == NULL) {
334 iscsi_err(__FILE__, __LINE__, "session pointer is NULL\n");
335 return -1;
336 }
337 if (g_target[(int)sess->isid].has_session == 0) {
338 iscsi_err(__FILE__, __LINE__,
339 "g_target[%" PRIu64 "].has_session==0??\n", sess->isid);
340 return -1;
341 }
342 sess->state = INITIATOR_SESSION_STATE_DESTROYING;
343
344 /* Abort all outstanding commands */
345
346 for (ptr = sess->cmds; ptr != NULL; ptr = ptr->next) {
347 if (initiator_abort(ptr) != 0) {
348 iscsi_err(__FILE__, __LINE__, "initiator_abort() failed\n");
349 return -1;
350 }
351 }
352
353 if (sess->tx_worker.state & ISCSI_WORKER_STATE_STARTED) {
354 if (sess->tx_worker.state & ISCSI_WORKER_STATE_EXITING) {
355 iscsi_trace(TRACE_ISCSI_DEBUG,
356 "Tx worker %" PRIu64 " already signalled for exit\n",
357 sess->isid);
358 } else {
359 iscsi_trace(TRACE_ISCSI_DEBUG,
360 "signaling Tx worker %" PRIu64 " into exiting state\n",
361 sess->isid);
362 ISCSI_LOCK(&sess->tx_worker.work_mutex, return -1);
363 iscsi_trace(TRACE_ISCSI_DEBUG,
364 "signaling socket shutdown to Tx worker %" PRIu64 "\n", sess->isid);
365 if (iscsi_sock_shutdown(sess->sock, 1) != 0) {
366 iscsi_err(__FILE__, __LINE__,
367 "iscsi_sock_shutdown() failed\n");
368 }
369 ISCSI_SIGNAL(&sess->tx_worker.work_cond, return -1);
370 ISCSI_UNLOCK(&sess->tx_worker.work_mutex, return -1);
371 }
372 iscsi_trace(TRACE_ISCSI_DEBUG,
373 "Checking exit condition of Tx worker\n");
374 while ((sess->tx_worker.state & ISCSI_WORKER_STATE_EXITING) !=
375 ISCSI_WORKER_STATE_EXITING) {
376 ISCSI_SPIN;
377 }
378 iscsi_trace(TRACE_ISCSI_DEBUG, "Tx worker %" PRIu64 " has exited\n",
379 sess->isid);
380 } else {
381 iscsi_trace(TRACE_ISCSI_DEBUG,
382 "Tx worker was not started. Nothing to signal\n");
383 }
384
385 /* Destroy Tx state */
386 while ((ptr = iscsi_queue_remove(&sess->tx_queue)) != NULL) {
387 ptr->status = -1;
388 if (ptr->callback && ((*ptr->callback)(ptr) != 0)) {
389 iscsi_err(__FILE__, __LINE__, "callback() failed\n");
390 }
391 }
392 iscsi_queue_destroy(&sess->tx_queue);
393
394 if (sess->rx_worker.state & ISCSI_WORKER_STATE_STARTED) {
395 if (sess->rx_worker.state & ISCSI_WORKER_STATE_EXITING) {
396 iscsi_trace(TRACE_ISCSI_DEBUG,
397 "Rx worker %" PRIu64 " already signalled for exit\n",
398 sess->isid);
399 } else {
400 iscsi_trace(TRACE_ISCSI_DEBUG,
401 "signaling Rx worker %" PRIu64 " into exiting state\n", sess->isid);
402 if (iscsi_sock_shutdown(sess->sock, 0) != 0) {
403 iscsi_err(__FILE__, __LINE__,
404 "iscsi_sock_shutdown() failed\n");
405 }
406 }
407 iscsi_trace(TRACE_ISCSI_DEBUG,
408 "Checking exit condition of Rx worker\n");
409 while ((sess->rx_worker.state & ISCSI_WORKER_STATE_EXITING) !=
410 ISCSI_WORKER_STATE_EXITING) {
411 ISCSI_SPIN;
412 }
413 iscsi_trace(TRACE_ISCSI_DEBUG, "Rx worker %" PRIu64 " has exited\n",
414 sess->isid);
415 } else {
416 iscsi_trace(TRACE_ISCSI_DEBUG,
417 "Rx worker was not started. Nothing to signal\n");
418 }
419
420 /* Close socket */
421
422 if (iscsi_sock_close(sess->sock) != 0) {
423 iscsi_err(__FILE__, __LINE__, "iscsi_sock_close() failed\n");
424 return -1;
425 }
426 /* Destroy wait queues */
427
428 ISCSI_MUTEX_DESTROY(&sess->tx_worker.work_mutex, return -1);
429 ISCSI_COND_DESTROY(&sess->tx_worker.work_cond, return -1);
430 ISCSI_MUTEX_DESTROY(&sess->tx_worker.exit_mutex, return -1);
431 ISCSI_COND_DESTROY(&sess->tx_worker.exit_cond, return -1);
432 ISCSI_MUTEX_DESTROY(&sess->rx_worker.work_mutex, return -1);
433 ISCSI_COND_DESTROY(&sess->rx_worker.work_cond, return -1);
434 ISCSI_MUTEX_DESTROY(&sess->rx_worker.exit_mutex, return -1);
435 ISCSI_COND_DESTROY(&sess->rx_worker.exit_cond, return -1);
436
437 /* Destroy param list */
438
439 PARAM_LIST_DESTROY(sess->params, return -1);
440
441 /* Enqueue session to free list */
442 if (iscsi_queue_insert(&g_session_q, sess) == -1) {
443 iscsi_err(__FILE__, __LINE__, "iscsi_queue_insert() failed\n");
444 return -1;
445 }
446 iscsi_trace(TRACE_ISCSI_DEBUG, "session %p destroyed and requeued\n",
447 sess);
448
449 g_target[(int)isid].has_session = 0;
450
451 return 0;
452 }
453
454 #define IS_DISCOVERY 1
455 #define IS_SECURITY 1
456
457 enum {
458 SESS_TYPE_DISCOVERY = 1,
459 SESS_TYPE_NORMAL = 2,
460 SESS_TYPE_NONE = 3
461 };
462
463 static int
params_out(initiator_session_t * sess,char * text,int * len,int textsize,int sess_type,int security)464 params_out(initiator_session_t * sess, char *text, int *len, int textsize, int sess_type, int security)
465 {
466 if (security == IS_SECURITY) {
467 PARAM_TEXT_ADD(sess->params, "InitiatorName", "iqn.1994-04.org.NetBSD.iscsi-initiator:agc", text, len, textsize, 1, return -1);
468 PARAM_TEXT_ADD(sess->params, "InitiatorAlias", "NetBSD", text, len, textsize, 1, return -1);
469 if (sess->sess_params.auth_type != AuthNone) {
470 PARAM_TEXT_ADD(sess->params, "AuthMethod", "CHAP,None", text, len, textsize, 1, return -1);
471 } else {
472 PARAM_TEXT_ADD(sess->params, "AuthMethod", "None", text, len, textsize, 1, return -1);
473 }
474 } else {
475 PARAM_TEXT_ADD(sess->params, "HeaderDigest", "None", text, len, textsize, 1, return -1);
476 PARAM_TEXT_ADD(sess->params, "DataDigest", "None", text, len, textsize, 1, return -1);
477 PARAM_TEXT_ADD(sess->params, "MaxConnections", "1", text, len, textsize, 1, return -1);
478 PARAM_TEXT_ADD(sess->params, "InitialR2T", "Yes", text, len, textsize, 1, return -1);
479 PARAM_TEXT_ADD(sess->params, "ImmediateData", "Yes", text, len, textsize, 1, return -1);
480 PARAM_TEXT_ADD(sess->params, "MaxRecvDataSegmentLength", "8192", text, len, textsize, 1, return -1);
481 PARAM_TEXT_ADD(sess->params, "FirstBurstLength", "65536", text, len, textsize, 1, return -1);
482 PARAM_TEXT_ADD(sess->params, "MaxBurstLength", "262144", text, len, textsize, 1, return -1);
483 PARAM_TEXT_ADD(sess->params, "DefaultTime2Wait", "2", text, len, textsize, 1, return -1);
484 PARAM_TEXT_ADD(sess->params, "DefaultTime2Retain", "20", text, len, textsize, 1, return -1);
485 PARAM_TEXT_ADD(sess->params, "MaxOutstandingR2T", "1", text, len, textsize, 1, return -1);
486 PARAM_TEXT_ADD(sess->params, "DataPDUInOrder", "No", text, len, textsize, 1, return -1);
487 PARAM_TEXT_ADD(sess->params, "DataSequenceInOrder", "No", text, len, textsize, 1, return -1);
488 PARAM_TEXT_ADD(sess->params, "ErrorRecoveryLevel", "0", text, len, textsize, 1, return -1);
489 }
490 switch (sess_type) {
491 case SESS_TYPE_DISCOVERY:
492 PARAM_TEXT_ADD(sess->params, "SessionType", "Discovery", text, len, textsize, 1, return -1);
493 break;
494 case SESS_TYPE_NORMAL:
495 PARAM_TEXT_ADD(sess->params, "SessionType", "Normal", text, len, textsize, 1, return -1);
496 PARAM_TEXT_ADD(sess->params, "TargetName", g_target[(int)sess->isid].TargetName, text, len, textsize, 1, return -1);
497 break;
498 default:
499 break;
500 }
501 PARAM_TEXT_PARSE(sess->params, &sess->sess_params.cred, text, *len, NULL, NULL, 0, 1, return -1);
502 return 0;
503 }
504
505 static int
full_feature_negotiation_phase_i(initiator_session_t * sess,char * text,int text_len)506 full_feature_negotiation_phase_i(initiator_session_t * sess, char *text,
507 int text_len)
508 {
509 initiator_cmd_t *cmd = NULL;
510 iscsi_text_cmd_args_t *text_cmd = NULL;
511 initiator_wait_t iwait;
512
513 /* Allocate command pointers */
514
515 if ((cmd = iscsi_malloc_atomic(sizeof(initiator_cmd_t))) == NULL) {
516 iscsi_err(__FILE__, __LINE__, "iscsi_malloc() failed\n");
517 return -1;
518 }
519 (void) memset(cmd, 0x0, sizeof(*cmd));
520 text_cmd = iscsi_malloc_atomic(sizeof(iscsi_text_cmd_args_t));
521 if (text_cmd == NULL) {
522 iscsi_err(__FILE__, __LINE__, "iscsi_malloc() failed\n");
523 if (cmd != NULL)
524 iscsi_free_atomic(cmd); /* initiator command */
525 return -1;
526 }
527 #define FFN_ERROR {if (cmd != NULL) iscsi_free_atomic(cmd); if (text_cmd != NULL) iscsi_free_atomic(text_cmd); return -1;}
528 (void) memset(text_cmd, 0x0, sizeof(*text_cmd));
529
530 /*
531 * Note that <final>, <length> and <text> are updated
532 * by text_response_i when we receive offers from
533 * the target.
534 */
535 text_cmd->text = text;
536 text_cmd->length = text_len;
537
538 do {
539
540 /* Build text command */
541
542 text_cmd->final = 1;
543 text_cmd->cont = 0;
544 ISCSI_SET_TAG(&text_cmd->tag);
545 text_cmd->transfer_tag = 0xffffffff;
546
547 /* Build wait for callback */
548
549 ISCSI_MUTEX_INIT(&iwait.mutex, FFN_ERROR);
550 ISCSI_COND_INIT(&iwait.cond, FFN_ERROR);
551
552 /* Build initiator command */
553
554 cmd->type = ISCSI_TEXT_CMD;
555 cmd->ptr = text_cmd;
556 cmd->callback = wait_callback_i;
557 cmd->callback_arg = &iwait;
558 cmd->isid = sess->isid;
559
560 /* Enqueue initiator command to Tx worker */
561
562 iscsi_trace(TRACE_ISCSI_DEBUG,
563 "enqueing text command to tx worker %" PRIu64 "\n",
564 sess->isid);
565 ISCSI_LOCK(&iwait.mutex, FFN_ERROR);
566 ISCSI_LOCK(&sess->tx_worker.work_mutex, FFN_ERROR);
567 if (iscsi_queue_insert(&sess->tx_queue, cmd) == -1) {
568 ISCSI_UNLOCK(&iwait.mutex, );
569 iscsi_err(__FILE__, __LINE__,
570 "iscsi_queue_insert() failed\n");
571 FFN_ERROR;
572 }
573 ISCSI_SIGNAL(&sess->tx_worker.work_cond, FFN_ERROR);
574 ISCSI_UNLOCK(&sess->tx_worker.work_mutex, FFN_ERROR);
575 iscsi_trace(TRACE_ISCSI_DEBUG, "enqueued text command ok\n");
576
577 /* Wait for callback */
578
579 iscsi_trace(TRACE_ISCSI_DEBUG, "waiting on text callback\n");
580 ISCSI_WAIT(&iwait.cond, &iwait.mutex, FFN_ERROR);
581 ISCSI_UNLOCK(&iwait.mutex, FFN_ERROR);
582 ISCSI_COND_DESTROY(&iwait.cond, FFN_ERROR);
583 ISCSI_MUTEX_DESTROY(&iwait.mutex, FFN_ERROR);
584 iscsi_trace(TRACE_ISCSI_DEBUG, "received text callback ok\n");
585
586 /*
587 * See if we're done. text_response_i() overwrites
588 * text_cmd->final
589 */
590 /* with the final bit in the text response from the target. */
591
592 if (!text_cmd->final) {
593 iscsi_trace(TRACE_ISCSI_PARAM,
594 "more negotiation needed (sending %d bytes "
595 "response parameters)\n",
596 text_cmd->length);
597 }
598 } while (!text_cmd->final);
599
600 /* Free command pointers */
601
602 iscsi_free_atomic(cmd->ptr); /* text command */
603 iscsi_free_atomic(cmd); /* initiator command */
604
605 return 0;
606 }
607
608 #define DISCOVERY_PHASE_TEXT_LEN 1024
609 #define DP_CLEANUP {if (text != NULL) iscsi_free_atomic(text);}
610 #define DP_ERROR {DP_CLEANUP; return -1;}
611
612 int
initiator_set_target_name(int target,char * target_name)613 initiator_set_target_name(int target, char *target_name)
614 {
615 (void) strlcpy(g_target[target].iqnwanted, target_name,
616 sizeof(g_target[target].iqnwanted));
617 (void) strlcpy(g_target[target].TargetName, target_name,
618 sizeof(g_target[target].TargetName));
619 return 0;
620 }
621
622
623 int
iscsi_initiator_get_max_targets(void)624 iscsi_initiator_get_max_targets(void)
625 {
626 return CONFIG_INITIATOR_NUM_TARGETS;
627 }
628
629 #if 0
630 int
631 iscsi_initiator_get_targets(int target, strv_t *svp)
632 {
633 initiator_session_t *sess = g_target[target].sess;
634 iscsi_parameter_value_t *vp;
635 iscsi_parameter_t *ip;
636 char *text = NULL;
637 int text_len = 0;
638 int pos = 0;
639
640 if ((text = iscsi_malloc_atomic(DISCOVERY_PHASE_TEXT_LEN)) == NULL) {
641 iscsi_err(__FILE__, __LINE__, "iscsi_malloc_atomic() failed\n");
642 return -1;
643 }
644
645 text_len = 0;
646 text[0] = 0x0;
647
648 PARAM_TEXT_ADD(sess->params, "SendTargets", "All", text, &text_len,
649 DISCOVERY_PHASE_TEXT_LEN, 1, DP_ERROR);
650 PARAM_TEXT_PARSE(sess->params, &sess->sess_params.cred, text,
651 text_len, NULL, NULL, DISCOVERY_PHASE_TEXT_LEN, 1,
652 DP_ERROR);
653 if (full_feature_negotiation_phase_i(sess, text, text_len) != 0) {
654 iscsi_err(__FILE__, __LINE__,
655 "full_feature_negotiation_phase_i() failed\n");
656 DP_ERROR;
657 }
658 for (ip = sess->params ; ip ; ip = ip->next) {
659 if (strcmp(ip->key, "TargetName") == 0) {
660 pos = 0;
661 for (vp = ip->value_l ; vp ; vp = vp->next, pos++) {
662 /*
663 * Skip items which have no name,
664 * these have been blocked by the target
665 */
666 if (!strlen(vp->value))
667 continue;
668
669 ALLOC(char *, svp->v, svp->size, svp->c, 10,
670 10, "igt", return -1);
671 svp->v[svp->c++] = strdup(vp->value);
672 ALLOC(char *, svp->v, svp->size, svp->c, 10,
673 10, "igt2", return -1);
674 svp->v[svp->c++] =
675 strdup(param_val_which(sess->params,
676 "TargetAddress", pos));
677 }
678 }
679 }
680
681 return 1;
682 }
683 #else
684 /* SendTargets=All must be sent in discovery session. */
685 int
iscsi_initiator_get_targets(int target,strv_t * svp)686 iscsi_initiator_get_targets(int target, strv_t *svp)
687 {
688 initiator_session_t *sess = g_target[target].sess;
689 strv_t *tp = &g_target[target].all_targets;
690 uint32_t i;
691
692 if (sess == NULL)
693 return -1;
694
695 for (i = 0; i < tp->c; i++) {
696 ALLOC(char *, svp->v, svp->size, svp->c, 10,
697 10, "igt", return -1);
698 svp->v[svp->c++] = strdup(tp->v[i]);
699 }
700
701 return 1;
702 }
703 #endif
704
705 static int
discovery_phase(int target,strv_t * svp)706 discovery_phase(int target, strv_t *svp)
707 {
708 initiator_session_t *sess;
709 iscsi_parameter_value_t *vp;
710 iscsi_parameter_t *ip;
711 unsigned i;
712 char *ptr;
713 char *colon_ptr;
714 char *comma_ptr;
715 char port[64];
716 char *text = NULL;
717 int text_len = 0;
718
719 if (target >= CONFIG_INITIATOR_NUM_TARGETS) {
720 iscsi_err(__FILE__, __LINE__,
721 "target (%d) out of range [0..%d]\n", target,
722 CONFIG_INITIATOR_NUM_TARGETS);
723 return -1;
724 }
725 sess = g_target[target].sess;
726 if ((text = iscsi_malloc_atomic(DISCOVERY_PHASE_TEXT_LEN)) == NULL) {
727 iscsi_err(__FILE__, __LINE__, "iscsi_malloc_atomic() failed\n");
728 return -1;
729 }
730 /* Login to target */
731
732 iscsi_trace(TRACE_ISCSI_DEBUG,
733 "entering Discovery login phase with target %d (sock %#x)\n",
734 target, (int) sess->sock);
735 text[0] = 0x0;
736 if (params_out(sess, text, &text_len, DISCOVERY_PHASE_TEXT_LEN,
737 SESS_TYPE_DISCOVERY, IS_SECURITY) != 0) {
738 iscsi_err(__FILE__, __LINE__, "params_out() failed\n");
739 DP_ERROR;
740 }
741 if (login_phase_i(sess, text, text_len) != 0) {
742 iscsi_err(__FILE__, __LINE__, "login_phase_i() failed\n");
743 DP_ERROR;
744 }
745 iscsi_trace(TRACE_ISCSI_DEBUG,
746 "now full feature for Discovery with target %d\n", target);
747
748 /* Full Feature Phase Negotiation (for SendTargets) */
749 text_len = 0;
750 text[0] = 0x0;
751 PARAM_TEXT_ADD(sess->params, "SendTargets", "All", text, &text_len,
752 DISCOVERY_PHASE_TEXT_LEN, 1, DP_ERROR);
753 PARAM_TEXT_PARSE(sess->params, &sess->sess_params.cred, text,
754 text_len, NULL, NULL, DISCOVERY_PHASE_TEXT_LEN, 1, DP_ERROR);
755 if (full_feature_negotiation_phase_i(sess, text, text_len) != 0) {
756 iscsi_err(__FILE__, __LINE__,
757 "full_feature_negotiation_phase_i() failed\n");
758 DP_ERROR;
759 }
760
761 /* fill in information on the targets from the TargetName values */
762 (void) memset(svp, 0x0, sizeof(*svp));
763 for (ip = sess->params ; ip ; ip = ip->next) {
764 if (strcmp(ip->key, "TargetName") == 0) {
765 for (vp = ip->value_l ; vp ; vp = vp->next) {
766 ALLOC(char *, svp->v, svp->size, svp->c, 10,
767 10, "discovery_phase", return -1);
768 svp->v[svp->c++] = strdup(vp->value);
769 ALLOC(char *, svp->v, svp->size, svp->c, 10,
770 10, "discovery_phase2", return -1);
771 svp->v[svp->c++] = strdup(param_val(
772 sess->params, "TargetAddress"));
773 }
774 }
775 }
776
777 if (g_target[target].iqnwanted[0] == 0x0) {
778 /*
779 * Use the first TargetName and TargetAddress sent to
780 * us (all others are currently ignored)
781 */
782 if (param_val(sess->params, "TargetName") != NULL) {
783 strlcpy(g_target[target].TargetName,
784 param_val(sess->params, "TargetName"),
785 sizeof(g_target[target].TargetName));
786 } else {
787 iscsi_err(__FILE__, __LINE__, "SendTargets failed\n");
788 DP_ERROR;
789 }
790 if ((ptr = param_val(sess->params, "TargetAddress")) == NULL) {
791 iscsi_err(__FILE__, __LINE__, "SendTargets failed\n");
792 DP_ERROR;
793 }
794 } else {
795 /* the user has asked for a specific target - find it */
796 ptr = NULL;
797 for (i = 0 ; i < svp->c ; i += 2) {
798 if (strcmp(g_target[target].iqnwanted,
799 svp->v[i]) == 0) {
800 strlcpy(g_target[target].TargetName, svp->v[i],
801 sizeof(g_target[target].TargetName));
802 ptr = svp->v[i + 1];
803 break;
804 }
805 }
806 if (ptr == NULL) {
807 iscsi_err(__FILE__, __LINE__,
808 "SendTargets failed - target `%s' not found\n",
809 g_target[target].iqnwanted);
810 DP_ERROR;
811 }
812 }
813
814 if (*ptr == 0x0) {
815 iscsi_err(__FILE__, __LINE__,
816 "Target is not allowing access\n");
817 DP_ERROR;
818 }
819 colon_ptr = strchr(ptr, ':');
820 if ((comma_ptr = strchr(ptr, ',')) == NULL) {
821 iscsi_err(__FILE__, __LINE__,
822 "portal group tag is missing in \"%s\"\n",
823 param_val(sess->params, "TargetAddress"));
824 DP_ERROR;
825 }
826 if (colon_ptr) {
827 strncpy(g_target[target].ip, ptr, (size_t)(colon_ptr - ptr));
828 strncpy(port, colon_ptr + 1,
829 (size_t)(comma_ptr - colon_ptr - 1));
830 port[comma_ptr - colon_ptr - 1] = 0x0;
831 g_target[target].port = iscsi_atoi(port);
832 } else {
833 strncpy(g_target[target].ip, ptr, (size_t)(comma_ptr - ptr));
834 g_target[target].port = ISCSI_PORT;
835 }
836
837 iscsi_trace(TRACE_ISCSI_DEBUG, "Discovered \"%s\" at \"%s:%u\"\n",
838 g_target[target].TargetName, g_target[target].name,
839 g_target[target].port);
840
841 /* Logout from target */
842
843 iscsi_trace(TRACE_ISCSI_DEBUG,
844 "entering logout phase with target %d\n", target);
845 if (logout_phase_i(sess) != 0) {
846 iscsi_err(__FILE__, __LINE__, "logout_phase_i() failed\n");
847 DP_ERROR;
848 }
849 iscsi_trace(TRACE_ISCSI_DEBUG, "target %d logout phase complete\n",
850 target);
851 DP_CLEANUP;
852 return 0;
853 }
854
855 #define FULL_FEATURE_PHASE_TEXT_LEN 1024
856
857 static int
full_feature_phase(initiator_session_t * sess)858 full_feature_phase(initiator_session_t * sess)
859 {
860 char *text;
861 int text_len;
862
863 if ((text = iscsi_malloc_atomic(FULL_FEATURE_PHASE_TEXT_LEN)) == NULL) {
864 iscsi_err(__FILE__, __LINE__, "iscsi_malloc_atomic() failed\n");
865 return -1;
866 }
867 #define FFP_CLEANUP {if (text != NULL) iscsi_free_atomic(text);}
868 #define FFP_ERROR {FFP_CLEANUP; return -1;}
869 /* Set text parameters */
870
871 text[0] = 0x0;
872 text_len = 0;
873 if (params_out(sess, text, &text_len, FULL_FEATURE_PHASE_TEXT_LEN,
874 SESS_TYPE_NORMAL, IS_SECURITY) != 0) {
875 iscsi_err(__FILE__, __LINE__, "params_out() failed\n");
876 FFP_ERROR;
877 }
878 /* Send login command */
879
880 iscsi_trace(TRACE_ISCSI_DEBUG, "entering login phase\n");
881 if (login_phase_i(sess, text, text_len) != 0) {
882 iscsi_err(__FILE__, __LINE__, "login_phase_i() failed\n");
883 FFP_ERROR;
884 }
885 iscsi_trace(TRACE_ISCSI_DEBUG, "login phase successful\n");
886
887 FFP_CLEANUP;
888 return 0;
889 }
890
891 int
iscsi_initiator_start(iscsi_initiator_t * ini)892 iscsi_initiator_start(iscsi_initiator_t *ini)
893 {
894 initiator_session_t *sess = NULL;
895 char *dbg;
896 char *cp;
897 int port;
898 int i;
899
900 #define INIT_CLEANUP {if (sess != NULL) iscsi_free_atomic(sess);}
901 #define INIT_ERROR {INIT_CLEANUP; return -1;}
902
903 if ((dbg = iscsi_initiator_getvar(ini, "debug")) != NULL) {
904 set_debug(dbg);
905 }
906 iscsi_trace(TRACE_ISCSI_DEBUG, "initializing initiator\n");
907 port = atoi(iscsi_initiator_getvar(ini, "target port"));
908 if (get_target_config(iscsi_initiator_getvar(ini,
909 "target hostname"), port) != 0) {
910 iscsi_err(__FILE__, __LINE__,
911 "Error getting target configuration in config file\n");
912 return -1;
913 }
914 g_initiator_state = 0;
915 if (iscsi_queue_init(&g_session_q,
916 CONFIG_INITIATOR_MAX_SESSIONS) != 0) {
917 iscsi_err(__FILE__, __LINE__, "iscsi_queue_init() failed\n");
918 return -1;
919 }
920 for (i = 0; i < CONFIG_INITIATOR_MAX_SESSIONS; i++) {
921 sess = iscsi_malloc_atomic(sizeof(initiator_session_t));
922 if (sess == NULL) {
923 iscsi_err(__FILE__, __LINE__,
924 "iscsi_malloc_atomic() failed\n");
925 return -1;
926 }
927 if (iscsi_queue_insert(&g_session_q, sess) != 0) {
928 iscsi_err(__FILE__, __LINE__,
929 "iscsi_queue_init() failed\n");
930 INIT_CLEANUP;
931 return -1;
932 }
933 cp = iscsi_initiator_getvar(ini, "auth type");
934 if (strcmp(cp, "none") == 0) {
935 sess->sess_params.auth_type = AuthNone;
936 sess->sess_params.cred.user = NULL;
937 } else {
938 sess->sess_params.cred.user =
939 strdup(iscsi_initiator_getvar(ini, "user"));
940 }
941 cp = iscsi_initiator_getvar(ini, "mutual auth");
942 if (strcmp(cp, "none") == 0) {
943 sess->sess_params.mutual_auth = 0;
944 }
945 cp = iscsi_initiator_getvar(ini, "digest type");
946 if (strcmp(cp, "none") == 0) {
947 sess->sess_params.digest_wanted = DigestNone;
948 }
949 }
950 iscsi_trace(TRACE_ISCSI_DEBUG, "%d free sessions available\n",
951 CONFIG_INITIATOR_MAX_SESSIONS);
952
953 g_tag = 0xabc123;
954 if (hash_init(&g_tag_hash, CONFIG_INITIATOR_QUEUE_DEPTH) != 0) {
955 iscsi_err(__FILE__, __LINE__, "hash_init() failed\n");
956 INIT_CLEANUP;
957 return -1;
958 }
959 iscsi_spin_init(&g_tag_spin);
960 iscsi_trace(TRACE_ISCSI_DEBUG,
961 "tag hash table initialized with queue depth %d\n",
962 CONFIG_INITIATOR_QUEUE_DEPTH);
963
964 /*
965 * Start enqueue worker. This thread accepts scsi commands from
966 * initiator_enqueue()
967 */
968 /* and queues them onto one of the tx worker queues. */
969
970 iscsi_trace(TRACE_ISCSI_DEBUG, "starting enqueue worker\n");
971 if (iscsi_queue_init(&g_enqueue_q, CONFIG_INITIATOR_QUEUE_DEPTH) != 0) {
972 iscsi_err(__FILE__, __LINE__, "iscsi_queue_init() failed\n");
973 INIT_CLEANUP;
974 return -1;
975 }
976 iscsi_trace(TRACE_ISCSI_DEBUG, "about to initialize mutex\n");
977 ISCSI_MUTEX_INIT(&g_enqueue_worker.work_mutex, INIT_ERROR);
978 ISCSI_COND_INIT(&g_enqueue_worker.work_cond, INIT_ERROR);
979 ISCSI_MUTEX_INIT(&g_enqueue_worker.exit_mutex, INIT_ERROR);
980 ISCSI_COND_INIT(&g_enqueue_worker.exit_cond, INIT_ERROR);
981 ISCSI_LOCK(&g_enqueue_worker.exit_mutex, INIT_ERROR);
982
983 iscsi_trace(TRACE_ISCSI_DEBUG, "spawning thread for enqueue worker\n");
984 if (iscsi_thread_create(&g_enqueue_worker.thread,
985 (void *) &enqueue_worker_proc, &g_enqueue_worker) != 0) {
986 iscsi_err(__FILE__, __LINE__,
987 "iscsi_threads_create() failed\n");
988 INIT_CLEANUP;
989 return -1;
990 }
991 iscsi_trace(TRACE_ISCSI_DEBUG, "thread spawned, waiting for signal\n");
992 ISCSI_WAIT(&g_enqueue_worker.exit_cond, &g_enqueue_worker.exit_mutex,
993 INIT_ERROR);
994 ISCSI_UNLOCK(&g_enqueue_worker.exit_mutex, INIT_ERROR);
995 iscsi_trace(TRACE_ISCSI_DEBUG, "successfully started enqueue worker\n");
996
997 iscsi_trace(TRACE_ISCSI_DEBUG, "initiator initialization complete\n");
998 return 0;
999 }
1000
1001 int
iscsi_initiator_shutdown(void)1002 iscsi_initiator_shutdown(void)
1003 {
1004 initiator_session_t *sess;
1005 int i;
1006
1007 iscsi_trace(TRACE_ISCSI_DEBUG, "shutting down initiator\n");
1008 for (i = 0; i < CONFIG_INITIATOR_NUM_TARGETS; i++) {
1009 if (g_target[i].has_session) {
1010 iscsi_trace(TRACE_ISCSI_DEBUG,
1011 "entering logout phase for target %d\n", i);
1012 if (g_target[i].sess->rx_worker.state &
1013 ISCSI_WORKER_STATE_ERROR) {
1014 iscsi_warn(__FILE__, __LINE__,
1015 "rx worker exited abnormal, "
1016 "skipping logout phase\n");
1017 } else {
1018 if (logout_phase_i(g_target[i].sess) != 0) {
1019 iscsi_err(__FILE__, __LINE__,
1020 "logout_phase_i() failed "
1021 "for target %d\n", i);
1022 }
1023 iscsi_trace(TRACE_ISCSI_DEBUG,
1024 "logout phase complete for target "
1025 "%d (state %#x)\n",
1026 i, g_target[i].sess->state);
1027 }
1028 iscsi_trace(TRACE_ISCSI_DEBUG,
1029 "destroying session for target %d\n", i);
1030 if (session_destroy_i(g_target[i].sess) != 0) {
1031 iscsi_err(__FILE__, __LINE__,
1032 "session_destroy_i() failed for "
1033 "target %d\n", i);
1034 }
1035 iscsi_trace(TRACE_ISCSI_DEBUG,
1036 "session destroyed for target %d\n", i);
1037 }
1038 }
1039
1040 g_initiator_state = INITIATOR_STATE_SHUTDOWN;
1041 if (g_enqueue_worker.state & ISCSI_WORKER_STATE_EXITING) {
1042 iscsi_trace(TRACE_ISCSI_DEBUG, "enqueue already exiting\n");
1043 } else {
1044 iscsi_trace(TRACE_ISCSI_DEBUG,
1045 "signaling enqueue worker into exiting state\n");
1046 ISCSI_LOCK(&g_enqueue_worker.work_mutex, return -1);
1047 ISCSI_SIGNAL(&g_enqueue_worker.work_cond, return -1);
1048 ISCSI_UNLOCK(&g_enqueue_worker.work_mutex, return -1);
1049 }
1050 iscsi_trace(TRACE_ISCSI_DEBUG,
1051 "Checking exit condition of enqueue worker\n");
1052 while ((g_enqueue_worker.state & ISCSI_WORKER_STATE_EXITING) !=
1053 ISCSI_WORKER_STATE_EXITING) {
1054 ISCSI_SPIN;
1055 }
1056 iscsi_trace(TRACE_ISCSI_DEBUG, "enqueue worker has exited\n");
1057
1058 iscsi_queue_destroy(&g_enqueue_q);
1059 ISCSI_MUTEX_DESTROY(&g_enqueue_worker.work_mutex, return -1);
1060 ISCSI_COND_DESTROY(&g_enqueue_worker.work_cond, return -1);
1061 ISCSI_MUTEX_DESTROY(&g_enqueue_worker.exit_mutex, return -1);
1062 ISCSI_COND_DESTROY(&g_enqueue_worker.exit_cond, return -1);
1063
1064 while ((sess = iscsi_queue_remove(&g_session_q)) != NULL) {
1065 iscsi_free_atomic(sess);
1066 }
1067 iscsi_queue_destroy(&g_session_q);
1068 iscsi_spin_destroy(&g_tag_spin);
1069 hash_destroy(&g_tag_hash);
1070 iscsi_trace(TRACE_ISCSI_DEBUG, "initiator shutdown complete\n");
1071 return 0;
1072 }
1073
1074 static int
wait_callback_i(void * ptr)1075 wait_callback_i(void *ptr)
1076 {
1077 initiator_wait_t *iwait = (initiator_wait_t *) (((initiator_cmd_t *) ptr)->callback_arg);
1078
1079 iwait = (initiator_wait_t *) (((initiator_cmd_t *) ptr)->callback_arg);
1080 ISCSI_LOCK(&iwait->mutex, return -1);
1081 ISCSI_SIGNAL(&iwait->cond, return -1);
1082 ISCSI_UNLOCK(&iwait->mutex, return -1);
1083 return 0;
1084 }
1085
1086 int
initiator_abort(initiator_cmd_t * cmd)1087 initiator_abort(initiator_cmd_t * cmd)
1088 {
1089 initiator_cmd_t *ptr, *prev;
1090 initiator_session_t *sess;
1091
1092 iscsi_err(__FILE__, __LINE__, "aborting iSCSI cmd 0x%p (type %d, isid %" PRIu64 ")\n",
1093 cmd, cmd->type, cmd->isid);
1094
1095 hash_remove(&g_tag_hash, cmd->key);
1096 if (g_target[(int)cmd->isid].has_session) {
1097 sess = g_target[(int)cmd->isid].sess;
1098 iscsi_spin_lock(&sess->cmds_spin);
1099 prev = ptr = sess->cmds;
1100 while (ptr != NULL) {
1101 prev = ptr;
1102 if (ptr == cmd)
1103 break;
1104 ptr = ptr->next;
1105 }
1106 if (ptr != NULL) {
1107 if (prev == sess->cmds) {
1108 sess->cmds = cmd->next;
1109 } else {
1110 prev->next = cmd->next;
1111 }
1112 }
1113 iscsi_spin_unlock(&sess->cmds_spin);
1114 } else {
1115 iscsi_err(__FILE__, __LINE__, "cmd 0x%p has no session\n", cmd);
1116 }
1117 cmd->status = -1;
1118 if (cmd->callback) {
1119 if ((*cmd->callback)(cmd) != 0) {
1120 iscsi_err(__FILE__, __LINE__, "cmd->callback() failed\n");
1121 return -1;
1122 }
1123 }
1124 iscsi_err(__FILE__, __LINE__, "successfully aborted iSCSI cmd 0x%p (type %d, isid %" PRIu64 ")\n",
1125 cmd, cmd->type, cmd->isid);
1126 return 0;
1127 }
1128
1129 int
initiator_command(initiator_cmd_t * cmd)1130 initiator_command(initiator_cmd_t * cmd)
1131 {
1132 initiator_wait_t iwait;
1133
1134 ISCSI_MUTEX_INIT(&iwait.mutex, return -1);
1135 ISCSI_COND_INIT(&iwait.cond, return -1);
1136 ISCSI_LOCK(&iwait.mutex, return -1);
1137 cmd->callback = wait_callback_i;
1138 cmd->callback_arg = &iwait;
1139 cmd->status = -1;
1140 if (initiator_enqueue(cmd) != 0) {
1141 iscsi_err(__FILE__, __LINE__, "initiator_enqueue() failed\n");
1142 return -1;
1143 } else {
1144 iscsi_trace(TRACE_ISCSI_DEBUG, "command (type %d) enqueued, waiting on condition\n", cmd->type);
1145 ISCSI_WAIT(&iwait.cond, &iwait.mutex, return -1);
1146 iscsi_trace(TRACE_ISCSI_DEBUG, "condition signaled\n");
1147 }
1148 ISCSI_UNLOCK(&iwait.mutex, return -1);
1149 ISCSI_COND_DESTROY(&iwait.cond, return -1);
1150 ISCSI_MUTEX_DESTROY(&iwait.mutex, return -1);
1151
1152 return cmd->status;
1153 }
1154
1155 /*
1156 * initiator_enqueue() may be called from within interrupt context within
1157 * the midlayer. This function cannot block or be scheduled within.
1158 * All we do is enqueue the args ptr to g_enqueue_q. The thread in
1159 * enqueue_worker_proc will enqueue the ptr onto one of the tx queues.
1160 */
1161
1162 int
initiator_enqueue(initiator_cmd_t * cmd)1163 initiator_enqueue(initiator_cmd_t * cmd)
1164 {
1165 initiator_session_t *sess;
1166 iscsi_scsi_cmd_args_t *scsi_cmd;
1167 iscsi_nop_out_args_t *nop_out;
1168 uint64_t target;
1169 uint32_t tag;
1170
1171 if ((target = cmd->isid) >= CONFIG_INITIATOR_NUM_TARGETS) {
1172 iscsi_err(__FILE__, __LINE__, "target (%" PRIu64 ") out of range [0..%d]\n", target, CONFIG_INITIATOR_NUM_TARGETS);
1173 return -1;
1174 }
1175 sess = g_target[(int)target].sess;
1176 if (g_target[(int)target].has_session &&
1177 sess->state == INITIATOR_SESSION_STATE_LOGGED_IN_NORMAL) {
1178
1179 /* Give command directly to tx worker */
1180
1181 ISCSI_SET_TAG_IN_INTR(&tag);
1182 target = cmd->isid;
1183
1184 switch (cmd->type) {
1185 case ISCSI_SCSI_CMD:
1186 scsi_cmd = (iscsi_scsi_cmd_args_t *) cmd->ptr;
1187 scsi_cmd->tag = tag;
1188 break;
1189 case ISCSI_NOP_OUT:
1190 nop_out = (iscsi_nop_out_args_t *) cmd->ptr;
1191 if (nop_out->tag != 0xffffffff) {
1192 nop_out->tag = tag;
1193 }
1194 break;
1195 default:
1196 iscsi_err(__FILE__, __LINE__, "enqueue_worker: unknown command type %d\n", cmd->type);
1197 return -1;
1198 }
1199 if (iscsi_queue_insert(&sess->tx_queue, cmd) == -1) {
1200 iscsi_err(__FILE__, __LINE__, "iscsi_queue_insert() failed\n");
1201 return -1;
1202 }
1203 ISCSI_LOCK(&sess->tx_worker.work_mutex, return -1);
1204 ISCSI_SIGNAL(&sess->tx_worker.work_cond, return -1);
1205 ISCSI_UNLOCK(&sess->tx_worker.work_mutex, return -1);
1206 iscsi_trace(TRACE_ISCSI_DEBUG, "initiator_cmd_t 0x%p given to tx_worker[%" PRIu64 "]\n", cmd, cmd->isid);
1207 } else {
1208
1209 /*
1210 * Give command to enqueue worker to get us into full feature
1211 * and then issue the command
1212 */
1213 /* to one of the tx workers. */
1214
1215 if (iscsi_queue_insert(&g_enqueue_q, cmd) == -1) {
1216 iscsi_err(__FILE__, __LINE__, "iscsi_queue_insert() failed\n");
1217 return -1;
1218 }
1219 ISCSI_LOCK(&g_enqueue_worker.work_mutex, return -1);
1220 ISCSI_SIGNAL(&g_enqueue_worker.work_cond, return -1);
1221 ISCSI_UNLOCK(&g_enqueue_worker.work_mutex, return -1);
1222 iscsi_trace(TRACE_ISCSI_DEBUG, "initiator_cmd_t 0x%p given to enqueue worker\n", cmd);
1223 }
1224 return 0;
1225 }
1226
1227 static int
enqueue_worker_proc(void * arg)1228 enqueue_worker_proc(void *arg)
1229 {
1230 initiator_session_t *sess;
1231 initiator_cmd_t *cmd;
1232 iscsi_scsi_cmd_args_t *scsi_cmd;
1233 iscsi_nop_out_args_t *nop_out;
1234 iscsi_worker_t *me = (iscsi_worker_t *) arg;
1235 uint64_t target;
1236 uint32_t tag;
1237 int rc;
1238
1239
1240 ISCSI_THREAD_START("enqueue_worker");
1241 ISCSI_SET_THREAD(me)
1242 ISCSI_LOCK(&me->exit_mutex, goto done);
1243
1244 me->pid = getpid();
1245 me->state = ISCSI_WORKER_STATE_STARTED;
1246 ISCSI_SIGNAL(&me->exit_cond, goto done);
1247 ISCSI_UNLOCK(&me->exit_mutex, goto done);
1248 iscsi_trace(TRACE_ISCSI_DEBUG, "enqueue_worker: started\n");
1249 ISCSI_LOCK(&g_enqueue_worker.work_mutex, goto done);
1250 for (;;) {
1251 if (iscsi_queue_depth(&g_enqueue_q) || (g_initiator_state == INITIATOR_STATE_SHUTDOWN)) {
1252 iscsi_trace(TRACE_ISCSI_DEBUG, "enqueu, start to work\n");
1253 ISCSI_UNLOCK(&g_enqueue_worker.work_mutex, goto done);
1254 if (g_initiator_state == INITIATOR_STATE_SHUTDOWN) {
1255 iscsi_trace(TRACE_ISCSI_DEBUG, "got shutdown signal\n");
1256 goto done;
1257 }
1258 if ((cmd = iscsi_queue_remove(&g_enqueue_q)) == NULL) {
1259 iscsi_err(__FILE__, __LINE__, "enqueue_worker: iscsi_queue_remove() failed\n");
1260 goto done;
1261 }
1262 ISCSI_SET_TAG(&tag);
1263 target = cmd->isid;
1264 iscsi_trace(TRACE_ISCSI_CMD, "enqueue_worker: dequeued initiator_cmd_t 0x%p (type %d, target %" PRIu64 ")\n", cmd, cmd->type, target);
1265 switch (cmd->type) {
1266 case ISCSI_SCSI_CMD:
1267 scsi_cmd = (iscsi_scsi_cmd_args_t *) cmd->ptr;
1268 scsi_cmd->tag = tag;
1269 break;
1270 case ISCSI_NOP_OUT:
1271 nop_out = (iscsi_nop_out_args_t *) cmd->ptr;
1272 if (nop_out->tag != 0xffffffff) {
1273 nop_out->tag = tag;
1274 }
1275 break;
1276 default:
1277 iscsi_err(__FILE__, __LINE__, "enqueue_worker: unknown command type %d\n", cmd->type);
1278 goto done;
1279 }
1280
1281 /* Initialize session (if not already) */
1282 initialize:
1283 if (!g_target[(int)target].has_session) {
1284 iscsi_trace(TRACE_ISCSI_DEBUG, "enqueue_worker: initializing target %" PRIu64 " session\n", target);
1285 if (session_init_i(&g_target[(int)target].sess, target) != 0) {
1286 iscsi_err(__FILE__, __LINE__, "session_init_i() failed (ignoring command)\n");
1287 goto next;
1288 }
1289 iscsi_trace(TRACE_ISCSI_DEBUG, "enqueue_worker: target %" PRIu64 " session initialized\n", target);
1290 } else {
1291 iscsi_trace(TRACE_ISCSI_DEBUG, "enqueue_worker: target %" PRIu64 " session already initialized\n", target);
1292 }
1293 sess = g_target[(int)target].sess;
1294 iscsi_trace(TRACE_ISCSI_DEBUG, "enqueue_worker: session 0x%p\n", sess);
1295
1296 /* Discovery login if TargetName is zero length */
1297
1298 if (strlen(g_target[(int)target].TargetName) == 0) {
1299 iscsi_trace(TRACE_ISCSI_DEBUG, "enqueue_worker: entering Discovery phase with target %" PRIu64 "\n", target);
1300 rc = discovery_phase((int)target, &g_target[(int)target].all_targets);
1301 iscsi_trace(TRACE_ISCSI_DEBUG, "enqueue_worker: Discovery phase complete\n");
1302
1303 /* Destroy session */
1304
1305 if (sess->state != INITIATOR_SESSION_STATE_DESTROYING) {
1306 if (g_target[(int)target].has_session) {
1307 if (session_destroy_i(g_target[(int)target].sess) != 0) {
1308 iscsi_err(__FILE__, __LINE__, "enqueue_worker: session_destroy_i() failed\n");
1309 goto done;
1310 }
1311 }
1312 }
1313
1314 /*
1315 * If the Discovery phase was
1316 * successful, we re-initialize the
1317 * session, enter full feature phase
1318 * and then execute the command.
1319 */
1320
1321 if (rc == 0) {
1322 iscsi_trace(TRACE_ISCSI_DEBUG, "enqueue_worker: discovery_phase() succeeded, entering full feature\n");
1323 goto initialize;
1324 } else {
1325 iscsi_err(__FILE__, __LINE__, "enqueue_worker: discovery_phase() failed (ignoring command)\n");
1326 goto next;
1327 }
1328 }
1329 /* Get into full feature if we're not already */
1330
1331 if (sess->state != INITIATOR_SESSION_STATE_LOGGED_IN_NORMAL) {
1332 iscsi_trace(TRACE_ISCSI_DEBUG, "enqueue_worker: entering full feature with target %" PRIu64 " (sock %#x)\n", target, (int) sess->sock);
1333 if (full_feature_phase(sess) != 0) {
1334 iscsi_err(__FILE__, __LINE__, "enqueue_worker: full_feature_phase() failed (ignoring command)\n");
1335 goto next;
1336 } else {
1337 iscsi_trace(TRACE_ISCSI_DEBUG, "enqueue_worker: now full feature with target %" PRIu64 "\n", target);
1338 }
1339 }
1340 /*
1341 * Now we are in FPP, so set the mostly
1342 * accessed parameters for easy retrieval
1343 * during data transfer
1344 */
1345 set_session_parameters(sess->params, &sess->sess_params);
1346
1347 /* Add command to tx work queue and signal worker */
1348
1349 ISCSI_LOCK(&sess->tx_worker.work_mutex, goto done);
1350 if (iscsi_queue_insert(&sess->tx_queue, cmd) == -1) {
1351 ISCSI_UNLOCK(&sess->tx_worker.work_mutex, goto done);
1352 iscsi_err(__FILE__, __LINE__, "iscsi_queue_insert() failed\n");
1353 goto done;
1354 }
1355 ISCSI_SIGNAL(&sess->tx_worker.work_cond, goto done);
1356 ISCSI_UNLOCK(&sess->tx_worker.work_mutex, goto done);
1357 iscsi_trace(TRACE_ISCSI_CMD, "enqueue_worker: gave initiator_cmd_t 0x%p to tx_worker[%" PRIu64 "]\n", cmd, cmd->isid);
1358 next:
1359 ISCSI_LOCK(&g_enqueue_worker.work_mutex, goto done);
1360 } else {
1361 iscsi_trace(TRACE_ISCSI_DEBUG, "enqueue_worker: queue empty, awaiting condition\n");
1362 ISCSI_WAIT(&g_enqueue_worker.work_cond, &g_enqueue_worker.work_mutex, goto done);
1363 iscsi_trace(TRACE_ISCSI_DEBUG, "enqueue_worker: condition signaled\n");
1364 }
1365 }
1366 done:
1367 ISCSI_WORKER_EXIT(me);
1368 return 0;
1369 }
1370
1371
1372 /***********
1373 * Private *
1374 ***********/
1375
1376
1377 /*
1378 * Tx Worker (one per connection)
1379 */
1380
1381 static int
tx_worker_proc_i(void * arg)1382 tx_worker_proc_i(void *arg)
1383 {
1384 iscsi_worker_t *me = (iscsi_worker_t *) arg;
1385 initiator_cmd_t *cmd, *ptr;
1386 initiator_session_t *sess = g_target[me->id].sess;
1387
1388 ISCSI_THREAD_START("tx_worker");
1389
1390 ISCSI_SET_THREAD(me)
1391 me->pid = getpid();
1392 me->state = ISCSI_WORKER_STATE_STARTED;
1393
1394 /* Connect to target */
1395 iscsi_trace(TRACE_ISCSI_DEBUG, "tx_worker[%d]: connecting to %s:%d\n",
1396 me->id, g_target[me->id].name, g_target[me->id].port);
1397 sess->state = INITIATOR_SESSION_STATE_CONNECTING;
1398 if (iscsi_sock_connect(sess->sock, g_target[me->id].name,
1399 g_target[me->id].port) != 0) {
1400 iscsi_err(__FILE__, __LINE__, "iscsi_sock_connect() failed\n");
1401
1402 ISCSI_LOCK(&me->exit_mutex, return -1);
1403 me->state |= ISCSI_WORKER_STATE_ERROR;
1404 ISCSI_SIGNAL(&me->exit_cond, return -1);
1405 ISCSI_UNLOCK(&me->exit_mutex, return -1);
1406 goto done;
1407
1408 }
1409 sess->state = INITIATOR_SESSION_STATE_CONNECTED;
1410 iscsi_trace(TRACE_ISCSI_DEBUG, "tx_worker[%d]: connected to %s:%d\n",
1411 me->id, g_target[me->id].name, g_target[me->id].port);
1412
1413 /* Start Rx worker */
1414
1415 iscsi_trace(TRACE_ISCSI_DEBUG, "tx_worker[%d]: starting Rx worker\n",
1416 me->id);
1417 ISCSI_LOCK(&sess->rx_worker.exit_mutex, return -1);
1418 if (iscsi_thread_create(&sess->rx_worker.thread,
1419 (void *) rx_worker_proc_i, sess) != 0) {
1420 iscsi_err(__FILE__, __LINE__, "iscsi_thread_create() failed\n");
1421 goto done;
1422 }
1423 ISCSI_WAIT(&sess->rx_worker.exit_cond, &sess->rx_worker.exit_mutex,
1424 return -1);
1425 ISCSI_UNLOCK(&sess->rx_worker.exit_mutex, return -1);
1426 iscsi_trace(TRACE_ISCSI_DEBUG, "tx_worker[%d]: Rx worker started\n",
1427 me->id);
1428
1429 /* Signal that we've started */
1430 ISCSI_LOCK(&me->exit_mutex, return -1);
1431 ISCSI_SIGNAL(&me->exit_cond, return -1);
1432 ISCSI_UNLOCK(&me->exit_mutex, return -1);
1433 iscsi_trace(TRACE_ISCSI_DEBUG, "tx_worker[%d]: successfully started\n",
1434 me->id);
1435
1436 /* This Tx loop will exit when both the g_tx_queue is empty and */
1437 /* sess->state != INITIATOR_SESSION_STATE_DESTROYING */
1438
1439 ISCSI_LOCK(&me->work_mutex, return -1);
1440 for (;;) {
1441
1442 if (iscsi_queue_depth(&g_target[me->id].sess->tx_queue) ||
1443 sess->state == INITIATOR_SESSION_STATE_DESTROYING) {
1444
1445 if (sess->state == INITIATOR_SESSION_STATE_DESTROYING) {
1446 iscsi_trace(TRACE_ISCSI_DEBUG,
1447 "tx_worker[%d]: session is being "
1448 "destroyed, exiting\n", me->id);
1449 ISCSI_UNLOCK(&me->work_mutex, return -1);
1450 goto done;
1451 }
1452 /* Get initiator command */
1453
1454 cmd = iscsi_queue_remove(
1455 &g_target[me->id].sess->tx_queue);
1456 if (cmd == NULL) {
1457 iscsi_err(__FILE__, __LINE__,
1458 "tx_worker[%d]: iscsi_queue_remove "
1459 "failed\n", me->id);
1460 ISCSI_UNLOCK(&me->work_mutex, return -1);
1461 goto done;
1462 }
1463 ISCSI_UNLOCK(&me->work_mutex, return -1);
1464 iscsi_trace(TRACE_ISCSI_CMD,
1465 "tx_worker[%d]: dequeued initiator_cmd_t 0x%p "
1466 "(type %d, target %" PRIu64 ")\n",
1467 me->id, cmd, cmd->type, cmd->isid);
1468
1469 /* Make sure we've got the right command */
1470 if (cmd->isid != (unsigned)me->id) {
1471 iscsi_err(__FILE__, __LINE__,
1472 "got command %#x for target %" PRIu64 ", "
1473 "expected %d\n", cmd->type,
1474 cmd->isid, me->id);
1475 goto done;
1476 }
1477 /*
1478 * Add to list of oustanding commands in session
1479 * (unless NOP_OUT without ping)
1480 */
1481 if (!((cmd->type == ISCSI_NOP_OUT) &&
1482 (((iscsi_nop_out_args_t *)(cmd->ptr))->tag ==
1483 0xffffffff))) {
1484 cmd->next = NULL;
1485 iscsi_spin_lock(&sess->cmds_spin);
1486 for (ptr = sess->cmds;
1487 ptr && ptr->next != NULL;
1488 ptr = ptr->next) {
1489 }
1490 if (ptr) {
1491 ptr->next = cmd;
1492 } else {
1493 sess->cmds = cmd;
1494 }
1495 iscsi_spin_unlock(&sess->cmds_spin);
1496 }
1497 cmd->tx_done = 0;
1498 switch (cmd->type) {
1499 case ISCSI_LOGIN_CMD:
1500 iscsi_trace(TRACE_ISCSI_CMD,
1501 "tx_worker[%d]: ISCSI_LOGIN_CMD\n",
1502 me->id);
1503 if (login_command_i(cmd) != 0) {
1504 iscsi_err(__FILE__, __LINE__,
1505 "tx_worker[%d]: "
1506 "login_command_i() failed\n",
1507 me->id);
1508 goto done;
1509 }
1510 break;
1511 case ISCSI_TEXT_CMD:
1512 iscsi_trace(TRACE_ISCSI_CMD,
1513 "tx_worker[%d]: ISCSI_TEXT_CMD\n",
1514 me->id);
1515 if (text_command_i(cmd) != 0) {
1516 iscsi_err(__FILE__, __LINE__,
1517 "tx_worker[%d]: text_command_i "
1518 "failed\n", me->id);
1519 goto done;
1520 }
1521 break;
1522 case ISCSI_SCSI_CMD:
1523 iscsi_trace(TRACE_ISCSI_CMD,
1524 "tx_worker[%d]: ISCSI_SCSI_CMD\n",
1525 me->id);
1526 if (scsi_command_i(cmd) != 0) {
1527 iscsi_err(__FILE__, __LINE__,
1528 "tx_worker[%d]: scsi_command_i"
1529 " failed\n", me->id);
1530 goto done;
1531 }
1532 break;
1533 case ISCSI_NOP_OUT:
1534 iscsi_trace(TRACE_ISCSI_CMD,
1535 "tx_worker[%d]: ISCSI_NOP_OUT\n",
1536 me->id);
1537 if (nop_out_i(cmd) != 0) {
1538 iscsi_err(__FILE__, __LINE__,
1539 "tx_worker[%d]: nop_out_i "
1540 "failed\n", me->id);
1541 goto done;
1542 }
1543 break;
1544 case ISCSI_LOGOUT_CMD:
1545 iscsi_trace(TRACE_ISCSI_CMD,
1546 "tx_worker[%d]: ISCSI_LOGOUT_CMD\n",
1547 me->id);
1548 if (logout_command_i(cmd) != 0) {
1549 iscsi_err(__FILE__, __LINE__,
1550 "tx_worker[%d]: "
1551 "logout_command_i() failed\n",
1552 me->id);
1553 goto done;
1554 }
1555 break;
1556 default:
1557 iscsi_err(__FILE__, __LINE__,
1558 "tx_worker[%d]: unknown iSCSI command"
1559 " %#x\n", me->id, cmd->type);
1560 cmd->status = -1;
1561 break;
1562 }
1563
1564 /* Get lock for next iteration */
1565
1566 ISCSI_LOCK(&me->work_mutex, return -1);
1567
1568 /*
1569 * The Rx thread will receive a response for
1570 * the command and execute the callback. We
1571 * need to make sure the callback function is
1572 * not executed before the Tx thread has
1573 * completed sending the command. This is
1574 * what tx_done is used for. The last step is
1575 * to set tx_done and signal the Rx thread,
1576 * which may be block on the condition.
1577 * NOP_OUT (without ping) will have no
1578 * response for the Rx thread to process - so
1579 * we execute the callback directly. */
1580
1581 if ((cmd->type == ISCSI_NOP_OUT) &&
1582 (((iscsi_nop_out_args_t *)(cmd->ptr))->tag ==
1583 0xffffffff)) {
1584 iscsi_trace(TRACE_ISCSI_DEBUG,
1585 "executing callback() function "
1586 "directly for NOP_OUT (no NOP_IN)\n");
1587 if (cmd->callback(cmd) != 0) {
1588 iscsi_err(__FILE__, __LINE__,
1589 "cmd->callback() failed\n");
1590 return -1;
1591 }
1592 } else {
1593 ISCSI_LOCK(&sess->rx_worker.work_mutex,
1594 return -1);
1595 cmd->tx_done = 1;
1596 ISCSI_SIGNAL(&sess->rx_worker.work_cond,
1597 return -1);
1598 ISCSI_UNLOCK(&sess->rx_worker.work_mutex,
1599 return -1);
1600 }
1601 } else {
1602 iscsi_trace(TRACE_ISCSI_DEBUG,
1603 "tx_worker[%d]: awaiting condition\n", me->id);
1604 ISCSI_WAIT(&me->work_cond, &me->work_mutex, return -1);
1605 iscsi_trace(TRACE_ISCSI_DEBUG,
1606 "tx_worker[%d]: condition signaled\n", me->id);
1607 }
1608 }
1609 done:
1610 if (sess->state != INITIATOR_SESSION_STATE_DESTROYING) {
1611 iscsi_err(__FILE__, __LINE__,
1612 "tx_worker[%d]: session exited prematurely "
1613 "(state %#x)\n", me->id, sess->state);
1614 me->state |= ISCSI_WORKER_STATE_ERROR;
1615 }
1616 ISCSI_WORKER_EXIT(me);
1617 return 0;
1618 }
1619
1620 /*
1621 * There is one Rx worker per connection.
1622 */
1623
1624 static int
rx_worker_proc_i(void * arg)1625 rx_worker_proc_i(void *arg)
1626 {
1627 uint8_t header[ISCSI_HEADER_LEN];
1628 initiator_session_t *sess = (initiator_session_t *) arg;
1629 iscsi_worker_t *me = &sess->rx_worker;
1630 initiator_cmd_t *cmd = NULL;
1631 initiator_cmd_t *prev, *ptr;
1632 uint32_t tag;
1633
1634 ISCSI_THREAD_START("rx_worker");
1635 ISCSI_SET_THREAD(me)
1636 me->state = ISCSI_WORKER_STATE_STARTED;
1637 me->pid = getpid();
1638 ISCSI_LOCK(&me->exit_mutex, return -1);
1639 ISCSI_SIGNAL(&me->exit_cond, return -1);
1640 ISCSI_UNLOCK(&me->exit_mutex, return -1);
1641
1642 iscsi_trace(TRACE_ISCSI_DEBUG, "rx_worker[%d]: started (sess %p)\n", me->id, sess);
1643
1644 for (;;) {
1645 iscsi_trace(TRACE_ISCSI_DEBUG, "rx_worker[%d]: reading iscsi header (sock %#x) \n",
1646 me->id, (int) sess->sock);
1647 if (iscsi_sock_msg(sess->sock, 0, ISCSI_HEADER_LEN, header, 0) != ISCSI_HEADER_LEN) {
1648 iscsi_trace(TRACE_ISCSI_DEBUG, "rx_worker[%d]: iscsi_sock_msg() failed\n", me->id);
1649 goto done;
1650 }
1651 if (sess->state == INITIATOR_SESSION_STATE_DESTROYING) {
1652 iscsi_err(__FILE__, __LINE__, "rx_worker[%d]: session is being destroyed\n", me->id);
1653 goto done;
1654 }
1655 /* Get cmd ptr from hash table */
1656
1657 if ((ISCSI_OPCODE(header) != ISCSI_REJECT) && (ISCSI_OPCODE(header) != ISCSI_ASYNC)) {
1658 (void) memcpy(&tag, header + 16, sizeof(tag));
1659 tag = ISCSI_NTOHL(tag);
1660 if (tag != 0xffffffff) {
1661
1662 /*
1663 * remove command from g_tag_hash, cmd is
1664 * local so we only need to lock the queue
1665 * remove operation
1666 */
1667
1668 if ((cmd = hash_remove(&g_tag_hash, tag)) == NULL) {
1669 iscsi_err(__FILE__, __LINE__, "hash_remove() failed\n");
1670 iscsi_trace(TRACE_ISCSI_DEBUG, "no cmd ptr associated with tag %#x\n", tag);
1671 } else {
1672 iscsi_trace(TRACE_ISCSI_DEBUG, "cmd ptr %p associated with tag %#x\n", cmd, tag);
1673
1674 ISCSI_LOCK(&sess->rx_worker.work_mutex, return -1);
1675 if (!cmd->tx_done) {
1676 ISCSI_WAIT(&sess->rx_worker.work_cond, &sess->rx_worker.work_mutex, return -1);
1677 }
1678 ISCSI_UNLOCK(&sess->rx_worker.work_mutex, return -1);
1679 }
1680 } else {
1681 iscsi_trace(TRACE_ISCSI_DEBUG, "no command associated with tag %#x\n", tag);
1682 }
1683 }
1684 /* Remove cmd ptr from outstanding list */
1685 iscsi_spin_lock(&sess->cmds_spin);
1686 prev = ptr = sess->cmds;
1687 while (ptr != NULL) {
1688 prev = ptr;
1689 if (ptr == cmd)
1690 break;
1691 ptr = ptr->next;
1692 }
1693 if (ptr != NULL) {
1694 if (prev == sess->cmds) {
1695 sess->cmds = cmd->next;
1696 } else {
1697 prev->next = cmd->next;
1698 }
1699 }
1700 iscsi_spin_unlock(&sess->cmds_spin);
1701 switch (ISCSI_OPCODE(header)) {
1702 case ISCSI_SCSI_RSP:
1703 iscsi_trace(TRACE_ISCSI_DEBUG, "rx_worker[%d]: ISCSI_SCSI_RSP\n", me->id);
1704 if (scsi_response_i(sess, cmd, header) != 0) {
1705 iscsi_err(__FILE__, __LINE__, "rx_worker[%d]: scsi_response_i() failed\n", me->id);
1706 goto done;
1707 }
1708 break;
1709 case ISCSI_READ_DATA:
1710 iscsi_trace(TRACE_ISCSI_DEBUG, "rx_worker[%d]: ISCSI_READ_DATA\n", me->id);
1711 if (scsi_read_data_i(sess, cmd, header) != 0) {
1712 iscsi_err(__FILE__, __LINE__, "rx_worker[%d]: scsi_read_data_i() failed\n", me->id);
1713 goto done;
1714 }
1715 break;
1716 case ISCSI_R2T:
1717 iscsi_trace(TRACE_ISCSI_DEBUG, "rx_worker[%d]: ISCSI_R2T\n", me->id);
1718 if (scsi_r2t_i(sess, cmd, header) != 0) {
1719 iscsi_err(__FILE__, __LINE__, "rx_worker[%d]: scsi_r2t_i() failed\n", me->id);
1720 goto done;
1721 }
1722 break;
1723 case ISCSI_NOP_IN:
1724 iscsi_trace(TRACE_ISCSI_DEBUG, "rx_worker[%d]: ISCSI_NOP_IN\n", me->id);
1725 if (nop_in_i(sess, cmd, header) != 0) {
1726 iscsi_err(__FILE__, __LINE__, "nop_in_i() failed\n");
1727 return -1;
1728 }
1729 break;
1730 case ISCSI_LOGIN_RSP:
1731 iscsi_trace(TRACE_ISCSI_DEBUG, "rx_worker[%d]: ISCSI_LOGIN_RSP\n", me->id);
1732 if (login_response_i(sess, cmd, header) != 0) {
1733 iscsi_err(__FILE__, __LINE__, "rx_worker[%d]: login_response_i() failed\n", me->id);
1734 goto done;
1735 }
1736 break;
1737 case ISCSI_TEXT_RSP:
1738 iscsi_trace(TRACE_ISCSI_DEBUG, "rx_worker[%d]: ISCSI_TEXT_RSP\n", me->id);
1739 if (text_response_i(sess, cmd, header) != 0) {
1740 iscsi_err(__FILE__, __LINE__, "rx_worker[%d]: text_response_i() failed\n", me->id);
1741 goto done;
1742 }
1743 break;
1744 case ISCSI_LOGOUT_RSP:
1745 iscsi_trace(TRACE_ISCSI_DEBUG, "rx_worker[%d]: ISCSI_LOGOUT_RSP\n", me->id);
1746 if (logout_response_i(sess, cmd, header) != 0) {
1747 iscsi_err(__FILE__, __LINE__, "rx_worker[%d]: logout_response_i() failed\n", me->id);
1748 goto done;
1749 }
1750 break;
1751 case ISCSI_REJECT:
1752 iscsi_trace(TRACE_ISCSI_DEBUG, "rx_worker[%d]: ISCSI_REJECT\n", me->id);
1753 if (reject_i(sess, header) != 0) {
1754 iscsi_err(__FILE__, __LINE__, "reject_i() failed\n");
1755 return -1;
1756 }
1757 break;
1758 case ISCSI_ASYNC:
1759 iscsi_trace(TRACE_ISCSI_DEBUG, "rx_worker[%d]: ISCSI_ASYNC\n", me->id);
1760 if (async_msg_i(sess, header) != 0) {
1761 iscsi_err(__FILE__, __LINE__, "async_msg_i() failed\n");
1762 goto done;
1763 }
1764 break;
1765 default:
1766 iscsi_err(__FILE__, __LINE__, "rx_worker[%d]: unexpected iSCSI op %#x\n", me->id, ISCSI_OPCODE(header));
1767 goto done;
1768 }
1769 }
1770 done:
1771 if (sess->state != INITIATOR_SESSION_STATE_DESTROYING) {
1772
1773 iscsi_trace(TRACE_ISCSI_DEBUG, "rx_worker[%d]: session exited prematurely (state %#x)\n", me->id, sess->state);
1774 me->state |= ISCSI_WORKER_STATE_ERROR;
1775 }
1776 ISCSI_WORKER_EXIT(me);
1777 return 0;
1778 }
1779
1780 static int
text_command_i(initiator_cmd_t * cmd)1781 text_command_i(initiator_cmd_t * cmd)
1782 {
1783 iscsi_text_cmd_args_t *text_cmd;
1784 initiator_session_t *sess;
1785 uint8_t header[ISCSI_HEADER_LEN];
1786
1787 text_cmd = (iscsi_text_cmd_args_t *) cmd->ptr;
1788 sess = g_target[(int)cmd->isid].sess;
1789 /*
1790 * Insert cmd into the hash table, keyed by the tag. The Rx thread
1791 * will
1792 */
1793 /* retrieve the cmd ptr using the tag from the response PDU. */
1794
1795 if (hash_insert(&g_tag_hash, cmd, text_cmd->tag) != 0) {
1796 iscsi_err(__FILE__, __LINE__, "hash_insert() failed\n");
1797 return -1;
1798 }
1799 /* Send text command PDU */
1800
1801 text_cmd->ExpStatSN = sess->ExpStatSN;
1802 text_cmd->CmdSN = sess->CmdSN++;
1803 iscsi_trace(TRACE_ISCSI_DEBUG, "sending text command\n");
1804 if (iscsi_text_cmd_encap(header, text_cmd) != 0) {
1805 iscsi_err(__FILE__, __LINE__, "(iscsi_text_cmd_encap() failed\n");
1806 return -1;
1807 }
1808 if ((unsigned)iscsi_sock_send_header_and_data(sess->sock, header, ISCSI_HEADER_LEN, text_cmd->text, text_cmd->length, 0)
1809 != ISCSI_HEADER_LEN + text_cmd->length) {
1810 iscsi_err(__FILE__, __LINE__, "iscsi_sock_send_header_and_data() failed.\n");
1811 return -1;
1812 }
1813 iscsi_trace(TRACE_ISCSI_DEBUG, "text command sent ok\n");
1814
1815 return 0;
1816 }
1817
1818 static int
login_command_i(initiator_cmd_t * cmd)1819 login_command_i(initiator_cmd_t * cmd)
1820 {
1821 iscsi_login_cmd_args_t *login_cmd;
1822 initiator_session_t *sess;
1823 uint8_t header[ISCSI_HEADER_LEN];
1824
1825 login_cmd = (iscsi_login_cmd_args_t *) cmd->ptr;
1826 sess = g_target[(int)cmd->isid].sess;
1827 /*
1828 * Insert cmd into the hash table, keyed by the tag. The Rx thread
1829 * will
1830 */
1831 /* retrieve the cmd ptr using the tag from the response PDU. */
1832
1833 if (hash_insert(&g_tag_hash, cmd, login_cmd->tag) != 0) {
1834 iscsi_err(__FILE__, __LINE__, "hash_insert() failed\n");
1835 return -1;
1836 }
1837 /* Send login command PDU */
1838 login_cmd->ExpStatSN = sess->ExpStatSN;
1839 iscsi_trace(TRACE_ISCSI_DEBUG, "sending login command\n");
1840 if (iscsi_login_cmd_encap(header, login_cmd) != 0) {
1841 iscsi_err(__FILE__, __LINE__, "(iscsi_login_cmd_encap() failed\n");
1842 return -1;
1843 }
1844 if ((unsigned)iscsi_sock_send_header_and_data(sess->sock, header, ISCSI_HEADER_LEN, login_cmd->text, login_cmd->length, 0)
1845 != ISCSI_HEADER_LEN + login_cmd->length) {
1846 iscsi_err(__FILE__, __LINE__, "iscsi_sock_send_header_and_data() failed.\n");
1847 return -1;
1848 }
1849 iscsi_trace(TRACE_ISCSI_DEBUG, "login command sent ok\n");
1850
1851 return 0;
1852 }
1853
1854 static int
logout_phase_i(initiator_session_t * sess)1855 logout_phase_i(initiator_session_t * sess)
1856 {
1857 initiator_cmd_t *cmd = NULL;
1858 iscsi_logout_cmd_args_t *logout_cmd = NULL;
1859 initiator_wait_t iwait;
1860
1861 sess->state = INITIATOR_SESSION_STATE_LOGGING_OUT;
1862
1863 /* Allocate command pointers */
1864
1865 if ((cmd = iscsi_malloc_atomic(sizeof(initiator_cmd_t))) == NULL) {
1866 iscsi_err(__FILE__, __LINE__, "iscsi_malloc() failed\n");
1867 return -1;
1868 }
1869 (void) memset(cmd, 0x0, sizeof(*cmd));
1870 if ((logout_cmd = iscsi_malloc_atomic(sizeof(iscsi_logout_cmd_args_t))) == NULL) {
1871 iscsi_err(__FILE__, __LINE__, "iscsi_malloc() failed\n");
1872 if (cmd != NULL)
1873 iscsi_free_atomic(cmd);
1874 return -1;
1875 }
1876 #define LO_CLEANUP {if (cmd != NULL) iscsi_free_atomic(cmd); if (logout_cmd != NULL) iscsi_free_atomic(logout_cmd); }
1877 #define LO_ERROR {LO_CLEANUP; return -1;}
1878 (void) memset(logout_cmd, 0x0, sizeof(*logout_cmd));
1879
1880 /* Build logout command */
1881
1882 logout_cmd->cid = sess->cid;
1883 logout_cmd->reason = ISCSI_LOGOUT_CLOSE_SESSION;
1884 ISCSI_SET_TAG(&logout_cmd->tag);
1885 logout_cmd->ExpStatSN = sess->ExpStatSN;
1886 logout_cmd->CmdSN = sess->CmdSN++;
1887
1888 /* Build wait for callback */
1889
1890 ISCSI_MUTEX_INIT(&iwait.mutex, LO_ERROR);
1891 ISCSI_COND_INIT(&iwait.cond, LO_ERROR);
1892
1893 /* Build initiator command */
1894
1895 cmd->type = ISCSI_LOGOUT_CMD;
1896 cmd->ptr = logout_cmd;
1897 cmd->callback = wait_callback_i;
1898 cmd->callback_arg = &iwait;
1899 cmd->isid = sess->isid;
1900
1901 /* Enqueue to Tx worker */
1902
1903 iscsi_trace(TRACE_ISCSI_DEBUG, "enqueing logout command to tx worker %" PRIu64 "\n", sess->isid);
1904 ISCSI_LOCK(&iwait.mutex, LO_ERROR);
1905 ISCSI_LOCK(&sess->tx_worker.work_mutex, LO_ERROR);
1906 if (iscsi_queue_insert(&sess->tx_queue, cmd) == -1) {
1907 ISCSI_UNLOCK(&sess->tx_worker.work_mutex, LO_ERROR);
1908 iscsi_err(__FILE__, __LINE__, "iscsi_queue_insert() failed\n");
1909 LO_ERROR;
1910 }
1911 ISCSI_SIGNAL(&sess->tx_worker.work_cond, LO_ERROR);
1912 ISCSI_UNLOCK(&sess->tx_worker.work_mutex, LO_ERROR);
1913 iscsi_trace(TRACE_ISCSI_DEBUG, "enqueued logout command ok\n");
1914
1915 /* Wait for callback */
1916
1917 iscsi_trace(TRACE_ISCSI_DEBUG, "waiting on logout callback\n");
1918 ISCSI_WAIT(&iwait.cond, &iwait.mutex, LO_ERROR);
1919 ISCSI_UNLOCK(&iwait.mutex, LO_ERROR);
1920 ISCSI_COND_DESTROY(&iwait.cond, LO_ERROR);
1921 ISCSI_MUTEX_DESTROY(&iwait.mutex, LO_ERROR);
1922 iscsi_trace(TRACE_ISCSI_DEBUG, "received logout callback ok\n");
1923
1924 sess->state = INITIATOR_SESSION_STATE_LOGGED_OUT;
1925
1926 LO_CLEANUP;
1927 return 0;
1928 }
1929
1930 static void
alarm_handler(int arg)1931 alarm_handler(int arg)
1932 {
1933 USE_ARG(arg);
1934 iscsi_err(__FILE__, __LINE__, "***aborting cmd 0x%p***\n", g_cmd);
1935 if (initiator_abort(g_cmd) != 0) {
1936 iscsi_err(__FILE__, __LINE__, "initiator_abort() failed\n");
1937 }
1938 }
1939
1940 static int
login_phase_i(initiator_session_t * sess,char * text,int text_len)1941 login_phase_i(initiator_session_t * sess, char *text, int text_len)
1942 {
1943 initiator_cmd_t *cmd = NULL;
1944 initiator_wait_t iwait;
1945 iscsi_login_cmd_args_t *login_cmd = NULL;
1946 struct sigaction act;
1947
1948 sess->state = INITIATOR_SESSION_STATE_LOGGING_IN;
1949
1950 /* Allocate command pointers */
1951
1952 if ((cmd = iscsi_malloc_atomic(sizeof(initiator_cmd_t))) == NULL) {
1953 iscsi_err(__FILE__, __LINE__, "iscsi_malloc() failed\n");
1954 return -1;
1955 }
1956 (void) memset(cmd, 0x0, sizeof(*cmd));
1957 if ((login_cmd = iscsi_malloc_atomic(sizeof(iscsi_login_cmd_args_t))) == NULL) {
1958 iscsi_err(__FILE__, __LINE__, "iscsi_malloc() failed\n");
1959 if (cmd != NULL)
1960 iscsi_free_atomic(cmd);
1961 return -1;
1962 }
1963 #define LI_CLEANUP {if (cmd != NULL) iscsi_free_atomic(cmd); if (login_cmd != NULL) iscsi_free_atomic(login_cmd); }
1964 #define LI_ERROR {LI_CLEANUP; return -1;}
1965 (void) memset(login_cmd, 0x0, sizeof(*login_cmd));
1966
1967 /* This is the length of our original offer. */
1968
1969 login_cmd->text = text;
1970 login_cmd->length = text_len;
1971 login_cmd->transit = 1;
1972 login_cmd->csg = ISCSI_LOGIN_STAGE_SECURITY;
1973 login_cmd->nsg = ISCSI_LOGIN_STAGE_NEGOTIATE;
1974 ISCSI_SET_TAG(&login_cmd->tag);
1975 login_cmd->CmdSN = sess->CmdSN = 0;
1976
1977 do {
1978
1979 /*
1980 * Build login command. Note that the <length> and
1981 * <text> fields may get updated by login_response_i.
1982 * Such is the case when we receive offers from the
1983 * target. The new <length> and <text> fields will
1984 * represent the response that we need to send to the
1985 * target on the next login.
1986 */
1987
1988 login_cmd->cont = 0;
1989 login_cmd->version_min = ISCSI_VERSION;
1990 login_cmd->version_max = ISCSI_VERSION;
1991 login_cmd->cid = sess->cid = (int)sess->isid;
1992 login_cmd->isid = sess->isid = sess->isid;
1993 login_cmd->tsih = 0;
1994
1995 /* Build wait for callback */
1996
1997 ISCSI_MUTEX_INIT(&iwait.mutex, LI_ERROR);
1998 ISCSI_COND_INIT(&iwait.cond, LI_ERROR);
1999
2000 /* Build initiator command */
2001
2002 cmd->type = ISCSI_LOGIN_CMD;
2003 cmd->ptr = login_cmd;
2004 cmd->callback = wait_callback_i;
2005 cmd->callback_arg = &iwait;
2006 cmd->isid = sess->isid;
2007
2008 /* Set Alarm */
2009
2010 g_cmd = cmd;
2011 act.sa_handler = alarm_handler;
2012 sigaction(SIGALRM, &act, NULL);
2013 alarm(5);
2014
2015 /* Enqueue initiator command to Tx worker */
2016
2017 iscsi_trace(TRACE_ISCSI_DEBUG, "enqueing login command to tx worker %" PRIu64 "\n", sess->isid);
2018 ISCSI_LOCK(&iwait.mutex, LI_ERROR);
2019 ISCSI_LOCK(&sess->tx_worker.work_mutex, LI_ERROR);
2020 if (iscsi_queue_insert(&sess->tx_queue, cmd) == -1) {
2021 ISCSI_UNLOCK(&sess->tx_worker.work_mutex, LI_ERROR);
2022 iscsi_err(__FILE__, __LINE__, "iscsi_queue_insert() failed\n");
2023 LI_ERROR;
2024
2025 }
2026 ISCSI_SIGNAL(&sess->tx_worker.work_cond, LI_ERROR);
2027 ISCSI_UNLOCK(&sess->tx_worker.work_mutex, LI_ERROR);
2028 iscsi_trace(TRACE_ISCSI_DEBUG, "enqueued login command ok\n");
2029
2030 /* Wait for callback */
2031
2032 iscsi_trace(TRACE_ISCSI_DEBUG, "waiting on login callback\n");
2033 ISCSI_WAIT(&iwait.cond, &iwait.mutex, LI_ERROR);
2034 ISCSI_UNLOCK(&iwait.mutex, LI_ERROR);
2035 ISCSI_COND_DESTROY(&iwait.cond, LI_ERROR);
2036 ISCSI_MUTEX_DESTROY(&iwait.mutex, LI_ERROR);
2037 iscsi_trace(TRACE_ISCSI_DEBUG, "received login callback ok\n");
2038
2039 alarm(0);
2040
2041 if (cmd->status != 0) {
2042 iscsi_err(__FILE__, __LINE__, "initiator_cmd_t failed\n");
2043 LI_ERROR;
2044 }
2045 if (sess->state == INITIATOR_SESSION_STATE_LOGGING_IN) {
2046 iscsi_trace(TRACE_ISCSI_PARAM, "more negotiation needed (sending %d bytes response parameters)\n",
2047 login_cmd->length);
2048 }
2049 } while (sess->state == INITIATOR_SESSION_STATE_LOGGING_IN);
2050 iscsi_trace(TRACE_ISCSI_DEBUG, "login phase completed successfully\n");
2051
2052 LI_CLEANUP;
2053 return 0;
2054 }
2055
2056
2057 #define TEXT_RESPONSE_TEXT_LEN 2048
2058
2059 static int
text_response_i(initiator_session_t * sess,initiator_cmd_t * cmd,uint8_t * header)2060 text_response_i(initiator_session_t * sess, initiator_cmd_t * cmd, uint8_t *header)
2061 {
2062 iscsi_text_cmd_args_t *text_cmd;
2063 iscsi_text_rsp_args_t text_rsp;
2064 iscsi_parameter_t *l = sess->params;
2065 char *text_in = NULL;
2066 char *text_out = NULL;
2067 int len_in = 0;
2068 int len_out = 0;
2069 int ret = 0;
2070
2071 #define TI_CLEANUP {if (text_in != NULL) iscsi_free_atomic(text_in); if (text_out != NULL) iscsi_free_atomic(text_out);}
2072 #define TI_ERROR {cmd->status=-1; goto callback;}
2073 if (cmd) {
2074 text_cmd = (iscsi_text_cmd_args_t *) cmd->ptr;
2075 } else {
2076 iscsi_err(__FILE__, __LINE__, "no initiator_cmd_t specified for iscsi_text_cmd_args_t??\n");
2077 return -1;
2078 }
2079
2080 /* Check arguments & update numbering */
2081
2082 if (iscsi_text_rsp_decap(header, &text_rsp) != 0) {
2083 iscsi_err(__FILE__, __LINE__, "text_response_decap() failed\n");
2084 TI_ERROR;
2085 }
2086 if (text_rsp.tag != text_cmd->tag) {
2087 iscsi_err(__FILE__, __LINE__,
2088 "Bad \"Tag\": %u != %u.\n",
2089 text_rsp.tag, text_cmd->tag);
2090 TI_ERROR;
2091 }
2092 if (text_rsp.transfer_tag != 0xffffffff) {
2093 iscsi_err(__FILE__, __LINE__,
2094 "Bad \"Transfer Tag\": %u != %u.\n",
2095 text_rsp.transfer_tag, 0xffffffff);
2096 TI_ERROR;
2097 }
2098 if (text_rsp.StatSN != sess->ExpStatSN) {
2099 iscsi_err(__FILE__, __LINE__,
2100 "Bad \"StatSN\": %u != %u.\n",
2101 text_rsp.StatSN, sess->ExpStatSN);
2102 TI_ERROR;
2103 }
2104 if (text_rsp.ExpCmdSN != sess->CmdSN) {
2105 iscsi_err(__FILE__, __LINE__,
2106 "Bad \"ExpCmdSN\": %u != %u.\n",
2107 text_rsp.ExpCmdSN, sess->CmdSN);
2108 TI_ERROR;
2109 }
2110 sess->ExpStatSN = text_rsp.StatSN + 1;
2111
2112 /* Parse input text parameters and generate any response */
2113
2114 if ((len_in = text_rsp.length) != 0) {
2115 iscsi_trace(TRACE_ISCSI_PARAM, "allocating %d bytes input parameters\n", len_in);
2116 if ((text_in = iscsi_malloc_atomic((unsigned)len_in + 1)) == NULL) {
2117 iscsi_err(__FILE__, __LINE__, "iscsi_malloc_atomic() failed\n");
2118 TI_ERROR;
2119 }
2120 if ((text_out = iscsi_malloc_atomic(TEXT_RESPONSE_TEXT_LEN)) == NULL) {
2121 iscsi_err(__FILE__, __LINE__, "iscsi_malloc_atomic() failed\n");
2122 if (text_in != NULL)
2123 iscsi_free_atomic(text_in);
2124 TI_ERROR;
2125 }
2126 if (iscsi_sock_msg(sess->sock, 0, (unsigned)len_in, text_in, 0) != len_in) {
2127 iscsi_err(__FILE__, __LINE__, "iscsi_sock_msg() failed\n");
2128 TI_ERROR;
2129 }
2130 text_in[len_in] = 0x0;
2131 iscsi_trace(TRACE_ISCSI_PARAM, "read %d bytes input parameters ok\n", len_in);
2132
2133 /* Reset the value lists for TargetName and TargetAddress */
2134
2135 if (param_val_reset(sess->params, "TargetName") != 0) {
2136 iscsi_err(__FILE__, __LINE__, "parm_val_reset() failed\n");
2137 TI_ERROR;
2138 }
2139 if (param_val_reset(sess->params, "TargetAddress") != 0) {
2140 iscsi_err(__FILE__, __LINE__, "parm_val_reset() failed\n");
2141 TI_ERROR;
2142 }
2143 /* Parse the incoming answer */
2144
2145 PARAM_TEXT_PARSE(l, &sess->sess_params.cred, text_in, len_in, text_out, &len_out, TEXT_RESPONSE_TEXT_LEN, 0, TI_ERROR);
2146
2147 if (len_out) {
2148 if (text_rsp.final != 0) {
2149 iscsi_err(__FILE__, __LINE__,
2150 "Bad \"text_rsp.final\": %u != 0.\n",
2151 text_rsp.final);
2152 TI_ERROR;
2153 }
2154 /*
2155 * Copy response text into text_cmd->text and
2156 * update the length text_cmd->length. This
2157 * will be sent out on the next text command.
2158 * */
2159
2160 PARAM_TEXT_PARSE(l, &sess->sess_params.cred, text_out, len_out, NULL, NULL, TEXT_RESPONSE_TEXT_LEN, 1, TI_ERROR);
2161
2162 iscsi_trace(TRACE_ISCSI_PARAM, "need to send %d bytes response back to target\n", len_out);
2163 text_cmd->length = len_out;
2164 memcpy(text_cmd->text, text_out, (size_t)len_out);
2165 } else {
2166 text_cmd->length = 0;
2167 }
2168 }
2169 text_cmd->final = text_rsp.final;
2170
2171 /* Issue callback */
2172
2173 iscsi_trace(TRACE_ISCSI_DEBUG, "iscsi_text_cmd_args_t done\n");
2174 callback:
2175 if (cmd->status == -1)
2176 ret = -1;
2177 if (cmd->callback(cmd) != 0) {
2178 ret = -1;
2179 iscsi_err(__FILE__, __LINE__, "callback() failed\n");
2180 }
2181 TI_CLEANUP;
2182 return ret;
2183 }
2184
2185 #define LOGIN_RESPONSE_TEXT_LEN 2048
2186
2187 static int
login_response_i(initiator_session_t * sess,initiator_cmd_t * cmd,uint8_t * header)2188 login_response_i(initiator_session_t * sess, initiator_cmd_t * cmd, uint8_t *header)
2189 {
2190 iscsi_login_cmd_args_t *login_cmd;
2191 iscsi_login_rsp_args_t login_rsp;
2192 iscsi_parameter_t *l = sess->params;
2193 char *text_in = NULL;
2194 char *text_out = NULL;
2195 int len_in = 0;
2196 int len_out = 0;
2197
2198 if ((text_out = iscsi_malloc_atomic(LOGIN_RESPONSE_TEXT_LEN)) == NULL) {
2199 iscsi_err(__FILE__, __LINE__, "iscsi_malloc_atomic() failed\n");
2200 cmd->status = -1;
2201 goto callback;
2202 }
2203 #define LIR_CLEANUP {if (text_in != NULL) iscsi_free_atomic(text_in); if (text_out != NULL) iscsi_free_atomic(text_out);}
2204 #define LIR_ERROR {cmd->status=-1; goto callback;}
2205 if (cmd) {
2206 login_cmd = (iscsi_login_cmd_args_t *) cmd->ptr;
2207 } else {
2208 iscsi_err(__FILE__, __LINE__, "no initiator_cmd_t specified for iscsi_login_cmd_args_t??\n");
2209 LIR_ERROR;
2210 }
2211
2212 /* Read login response */
2213
2214 if (iscsi_login_rsp_decap(header, &login_rsp) != 0) {
2215 iscsi_err(__FILE__, __LINE__, "login_response_decap() failed\n");
2216 LIR_ERROR;
2217 }
2218 if (login_rsp.length > 8192) {
2219 iscsi_err(__FILE__, __LINE__, "login_rsp.length %u\n",
2220 login_rsp.length);
2221 LIR_CLEANUP;
2222 return -1;
2223 }
2224
2225 /* Read & parse text response */
2226 if ((len_in = login_rsp.length) != 0) {
2227 if ((text_in = iscsi_malloc_atomic((unsigned)len_in + 1)) == NULL) {
2228 iscsi_err(__FILE__, __LINE__, "iscsi_malloc_atomic() failed\n");
2229 LIR_ERROR;
2230 }
2231 if (iscsi_sock_msg(sess->sock, 0, (unsigned)len_in, text_in, 0) != len_in) {
2232 iscsi_err(__FILE__, __LINE__, "iscsi_sock_msg() failed\n");
2233 LIR_ERROR;
2234 }
2235 text_in[len_in] = 0x0;
2236 PARAM_TEXT_PARSE(l, &sess->sess_params.cred, text_in, len_in, text_out, &len_out, LOGIN_RESPONSE_TEXT_LEN, 0, LIR_ERROR);
2237 if (login_rsp.transit && len_out != 0) {
2238 iscsi_warn(__FILE__, __LINE__,
2239 "Bad \"len_out\": Got %u expected %u.\n",
2240 len_out, 0);
2241 }
2242 }
2243
2244 /* Check args */
2245 if (login_rsp.status_class != 0) {
2246 iscsi_err(__FILE__, __LINE__, "Bad Status-Class: got %d, expected %d\n", login_rsp.status_class, 0);
2247 LIR_ERROR;
2248 }
2249 if (login_rsp.tag != login_cmd->tag) {
2250 iscsi_err(__FILE__, __LINE__, "Bad Tag: got %x, expected %x\n", login_rsp.tag, login_cmd->tag);
2251 LIR_ERROR;
2252 }
2253 sess->ExpStatSN = login_rsp.StatSN + 1;
2254
2255
2256 if (login_rsp.transit) {
2257
2258 if (login_cmd->transit != 1)
2259 iscsi_warn(__FILE__, __LINE__, "incoming packet transit bit not set, csg = %d, nsg = %d\n",
2260 login_cmd->csg, login_cmd->nsg);
2261
2262 switch (login_rsp.nsg) {
2263 case ISCSI_LOGIN_STAGE_NEGOTIATE:
2264 login_cmd->csg = login_cmd->nsg;
2265 login_cmd->nsg = ISCSI_LOGIN_STAGE_FULL_FEATURE;
2266 if (params_out(sess, text_out, &len_out, LOGIN_RESPONSE_TEXT_LEN, SESS_TYPE_NONE, /*LINTED*/!IS_SECURITY) != 0) {
2267 iscsi_err(__FILE__, __LINE__, "params_out() failed\n");
2268 LIR_ERROR;
2269 }
2270 login_cmd->length = len_out;
2271 (void) memcpy(login_cmd->text, text_out,
2272 (size_t)len_out);
2273 break;
2274
2275 case ISCSI_LOGIN_STAGE_FULL_FEATURE:
2276 /* Check post conditions */
2277
2278 if (login_rsp.tsih == 0) {
2279 iscsi_err(__FILE__, __LINE__,
2280 "Bad \"TSIH\": %u == 0.\n", login_rsp.tsih);
2281 LIR_ERROR;
2282 }
2283 if (login_rsp.isid != login_cmd->isid) {
2284 iscsi_err(__FILE__, __LINE__,
2285 "Bad \"ISID\": %uu != %uu.\n",
2286 (unsigned)login_rsp.isid,
2287 (unsigned)login_cmd->isid);
2288 LIR_ERROR;
2289 }
2290 if (login_rsp.ExpCmdSN != login_cmd->CmdSN) {
2291 iscsi_err(__FILE__, __LINE__,
2292 "Bad \"ExpCmdSN\": %u != %u.\n",
2293 (unsigned)login_rsp.ExpCmdSN,
2294 (unsigned)login_cmd->CmdSN);
2295 LIR_ERROR;
2296 }
2297 if (login_rsp.ExpCmdSN > login_rsp.MaxCmdSN) {
2298 iscsi_err(__FILE__, __LINE__,
2299 "Bad \"MaxCmdSN\": %u > %u.\n",
2300 (unsigned)login_rsp.ExpCmdSN,
2301 (unsigned)login_rsp.MaxCmdSN);
2302 LIR_ERROR;
2303 }
2304
2305 /* Set remaining session parameters */
2306
2307 sess->CmdSN = login_rsp.ExpCmdSN;
2308 sess->MaxCmdSN = login_rsp.MaxCmdSN;
2309 sess->tsih = login_rsp.tsih;
2310 sess->isid = login_rsp.isid;
2311
2312 if (param_equiv(sess->params, "SessionType", "Normal")) {
2313 sess->state = INITIATOR_SESSION_STATE_LOGGED_IN_NORMAL;
2314 } else if (param_equiv(sess->params, "SessionType", "Discovery")) {
2315 sess->state = INITIATOR_SESSION_STATE_LOGGED_IN_DISCOVERY;
2316 } else {
2317 iscsi_err(__FILE__, __LINE__, "Unknown SessionType \"%s\"\n", param_val(sess->params, "SessionType"));
2318 LIR_ERROR;
2319 }
2320
2321 iscsi_trace(TRACE_ISCSI_DEBUG, "*********************************************\n");
2322 iscsi_trace(TRACE_ISCSI_DEBUG, "* LOGIN SUCCESSFUL *\n");
2323 iscsi_trace(TRACE_ISCSI_DEBUG, "* %20s:%20u *\n", "CID", sess->cid);
2324 iscsi_trace(TRACE_ISCSI_DEBUG, "* %20s:%20" PRIu64 " *\n", "ISID", sess->isid);
2325 iscsi_trace(TRACE_ISCSI_DEBUG, "* %20s:%20u *\n", "TSIH", sess->tsih);
2326 iscsi_trace(TRACE_ISCSI_DEBUG, "* %20s:%20u *\n", "CmdSN", sess->CmdSN);
2327 iscsi_trace(TRACE_ISCSI_DEBUG, "* %20s:%20u *\n", "MaxCmdSN", sess->MaxCmdSN);
2328 iscsi_trace(TRACE_ISCSI_DEBUG, "* %20s:%20u *\n", "ExpStatSN", sess->ExpStatSN);
2329 iscsi_trace(TRACE_ISCSI_DEBUG, "*********************************************\n");
2330 break;
2331 default:
2332 LIR_ERROR;
2333 }
2334 } else {
2335 iscsi_trace(TRACE_ISCSI_DEBUG, "received partial login response\n");
2336
2337 /* Copy response text into login_cmd->text and update the */
2338 /* length login_cmd->length. This will be sent out on the */
2339 /* next login command. */
2340
2341 if (len_out) {
2342 PARAM_TEXT_PARSE(l, &sess->sess_params.cred, text_out, len_out, NULL, NULL, 0, 1, LIR_ERROR);
2343 iscsi_trace(TRACE_ISCSI_PARAM, "need to send %d bytes response back to target\n", len_out);
2344
2345 login_cmd->length = len_out;
2346 memcpy(login_cmd->text, text_out, (size_t)len_out);
2347 if (strncmp(text_out, "CHAP_N=", strlen("CHAP_N=")) == 0) {
2348 login_cmd->nsg = ISCSI_LOGIN_STAGE_NEGOTIATE;
2349 login_cmd->transit = 1;
2350 }
2351 } else {
2352 login_cmd->length = 0;
2353 }
2354 }
2355
2356 /* Callback */
2357
2358 callback:
2359 iscsi_trace(TRACE_ISCSI_DEBUG, "iscsi_login_cmd_args_t done (cmd status %d, iscsi status %d)\n",
2360 cmd->status, login_rsp.status_class);
2361 if ((*cmd->callback)(cmd) != 0) {
2362 iscsi_err(__FILE__, __LINE__, "callback() failed\n");
2363 LIR_CLEANUP;
2364 return -1;
2365 }
2366 LIR_CLEANUP;
2367 return 0;
2368 }
2369
2370 static int
logout_command_i(initiator_cmd_t * cmd)2371 logout_command_i(initiator_cmd_t * cmd)
2372 {
2373 iscsi_logout_cmd_args_t *logout_cmd;
2374 initiator_session_t *sess;
2375 uint8_t header[ISCSI_HEADER_LEN];
2376
2377 logout_cmd = (iscsi_logout_cmd_args_t *) cmd->ptr;
2378 sess = g_target[(int)cmd->isid].sess;
2379 /*
2380 * Insert cmd into the hash table, keyed by the tag. The Rx thread
2381 * will
2382 */
2383 /* retrieve the cmd ptr using the tag from the response PDU. */
2384
2385 if (hash_insert(&g_tag_hash, cmd, logout_cmd->tag) != 0) {
2386 iscsi_err(__FILE__, __LINE__, "hash_insert() failed\n");
2387 return -1;
2388 }
2389 /* Send logout command PDU */
2390
2391 iscsi_trace(TRACE_ISCSI_DEBUG, "sending logout command\n");
2392 if (iscsi_logout_cmd_encap(header, logout_cmd) != 0) {
2393 iscsi_err(__FILE__, __LINE__, "iscsi_logout_cmd_encap() failed\n");
2394 return -1;
2395 }
2396 if (iscsi_sock_msg(sess->sock, 1, ISCSI_HEADER_LEN, header, 0) != ISCSI_HEADER_LEN) {
2397 iscsi_err(__FILE__, __LINE__, "iscsi_sock_msg() failed.\n");
2398 return -1;
2399 }
2400 iscsi_trace(TRACE_ISCSI_DEBUG, "logout command sent ok\n");
2401
2402 return 0;
2403 }
2404
2405
2406 static int
logout_response_i(initiator_session_t * sess,initiator_cmd_t * cmd,uint8_t * header)2407 logout_response_i(initiator_session_t * sess, initiator_cmd_t * cmd, uint8_t *header)
2408 {
2409 iscsi_logout_cmd_args_t *logout_cmd;
2410 iscsi_logout_rsp_args_t logout_rsp;
2411
2412 #define LOR_ERROR {cmd->status=-1; goto callback;}
2413 if (cmd) {
2414 if (cmd->ptr) {
2415 logout_cmd = (iscsi_logout_cmd_args_t *) cmd->ptr;
2416 } else {
2417 iscsi_err(__FILE__, __LINE__, "no iscsi_logout_cmd_args_t specified for initiator_cmd_t??\n");
2418 LOR_ERROR;
2419 }
2420 } else {
2421 iscsi_err(__FILE__, __LINE__, "no initiator_cmd_t specified for iscsi_logout_cmd_args_t??\n");
2422 return -1;
2423 }
2424 if (iscsi_logout_rsp_decap(header, &logout_rsp) != 0) {
2425 iscsi_err(__FILE__, __LINE__, "iscsi_logout_rsp_decap() failed\n");
2426 LOR_ERROR;
2427 }
2428 if (logout_rsp.response != ISCSI_LOGOUT_STATUS_SUCCESS) {
2429 iscsi_err(__FILE__, __LINE__, "Bad \"Response\": Got %u\n",
2430 logout_rsp.response);
2431 LOR_ERROR;
2432 }
2433 if (logout_rsp.tag != logout_cmd->tag) {
2434 iscsi_err(__FILE__, __LINE__, "Bad \"Tag\": Got %u\n",
2435 logout_rsp.tag);
2436 LOR_ERROR;
2437 }
2438
2439 /* Check and update numbering */
2440 if (logout_rsp.StatSN != sess->ExpStatSN) {
2441 iscsi_err(__FILE__, __LINE__,
2442 "Bad \"StatSN\": Got %u, needed %u\n",
2443 logout_rsp.StatSN, sess->ExpStatSN);
2444 LOR_ERROR;
2445 }
2446 sess->ExpStatSN += 1;
2447 if (logout_rsp.ExpCmdSN != sess->CmdSN) {
2448 iscsi_err(__FILE__, __LINE__,
2449 "Bad \"ExpCmdSN\": Got %u, needed %u\n",
2450 logout_rsp.ExpCmdSN, sess->CmdSN);
2451 LOR_ERROR;
2452 }
2453 sess->MaxCmdSN = logout_rsp.MaxCmdSN;
2454
2455 /* Callback */
2456 cmd->status = 0;
2457 iscsi_trace(TRACE_ISCSI_DEBUG,
2458 "LOGOUT_CMD_T done (cmd status %d, iscsi status %d)\n",
2459 cmd->status, logout_rsp.response);
2460 callback:
2461 if ((*cmd->callback)(cmd) != 0) {
2462 iscsi_err(__FILE__, __LINE__, "callback() failed\n");
2463 return -1;
2464 }
2465
2466 iscsi_trace(TRACE_ISCSI_DEBUG, "*********************************************\n");
2467 iscsi_trace(TRACE_ISCSI_DEBUG, "* LOGOUT SUCCESSFUL *\n");
2468 iscsi_trace(TRACE_ISCSI_DEBUG, "* %20s:%20u *\n", "CID", sess->cid);
2469 iscsi_trace(TRACE_ISCSI_DEBUG, "* %20s:%20" PRIu64 " *\n", "ISID", sess->isid);
2470 iscsi_trace(TRACE_ISCSI_DEBUG, "* %20s:%20u *\n", "TSIH", sess->tsih);
2471 iscsi_trace(TRACE_ISCSI_DEBUG, "*********************************************\n");
2472
2473 return 0;
2474 }
2475
2476 static int
nop_out_i(initiator_cmd_t * cmd)2477 nop_out_i(initiator_cmd_t * cmd)
2478 {
2479 uint8_t header[ISCSI_HEADER_LEN];
2480 iscsi_nop_out_args_t *nop_out;
2481 initiator_session_t *sess;
2482 int rc, length;
2483
2484 nop_out = cmd->ptr;
2485 sess = g_target[(int)cmd->isid].sess;
2486 length = nop_out->length;
2487 if (nop_out->tag != 0xffffffff) {
2488
2489 /*
2490 * Insert cmd into the hash table, keyed by
2491 * nop_out->tag. Upon receipt of the NOP_IN_T, the Rx
2492 * thread will retrieve the cmd ptr using the tag from
2493 * the NOP_IN_T PDU. */
2494
2495 if (hash_insert(&g_tag_hash, cmd, nop_out->tag) != 0) {
2496 iscsi_err(__FILE__, __LINE__, "hash_insert() failed\n");
2497 return -1;
2498 }
2499 }
2500 /* Encapsulate and send NOP */
2501
2502 nop_out->ExpStatSN = sess->ExpStatSN;
2503 nop_out->immediate = 1;
2504 nop_out->CmdSN = sess->CmdSN;
2505 nop_out->transfer_tag = 0xffffffff;
2506 if (iscsi_nop_out_encap(header, nop_out) != 0) {
2507 iscsi_err(__FILE__, __LINE__, "iscsi_nop_out_encap() failed\n");
2508 return -1;
2509 }
2510 /*
2511 * We need to make a copy of nop_out->length and save in the
2512 * variable length. Otherwise, we may get a seg fault - as if
2513 * this is a NOP_OUT without ping, the Tx thread will issue
2514 * the callback function immediately after we return - thereby
2515 * de-allocating the NOP_OUT and initiator command structures.
2516 * */
2517
2518 if ((rc = iscsi_sock_send_header_and_data(sess->sock, header,
2519 ISCSI_HEADER_LEN, nop_out->data, (unsigned)length,
2520 0)) != ISCSI_HEADER_LEN + length) {
2521 iscsi_err(__FILE__, __LINE__, "iscsi_sock_msg() failed: got %d expected %d\n", rc, ISCSI_HEADER_LEN + length);
2522 return -1;
2523 }
2524 cmd->status = 0;
2525 return 0;
2526 }
2527
2528 static int
scsi_command_i(initiator_cmd_t * cmd)2529 scsi_command_i(initiator_cmd_t * cmd)
2530 {
2531 iscsi_scsi_cmd_args_t *scsi_cmd;
2532 uint8_t header[ISCSI_HEADER_LEN];
2533 uint64_t target;
2534 initiator_session_t *sess;
2535 iscsi_write_data_t data;
2536 struct iovec sg_singleton;
2537 struct iovec *sg, *sg_copy, *sg_copy_orig, *sg_which;
2538 int sg_len, sg_len_copy, sg_len_which;
2539 int fragment_flag;
2540
2541 scsi_cmd = (iscsi_scsi_cmd_args_t *) cmd->ptr;
2542 target = cmd->isid;
2543 sess = g_target[(int)target].sess;
2544 fragment_flag = 0;
2545 sg = sg_copy = sg_copy_orig = sg_which = NULL;
2546 sg_len = sg_len_copy = sg_len_which = 0;
2547 scsi_cmd->status = 0;
2548
2549 iscsi_trace(TRACE_ISCSI_DEBUG, "tx_worker[%" PRIu64 "]: scsi op %#x lun %" PRIu64 " trans_len %d length %d send_sg_len %d recv_sg_len %d\n", target, scsi_cmd->cdb[0], scsi_cmd->lun, scsi_cmd->trans_len, scsi_cmd->length, scsi_cmd->send_sg_len, scsi_cmd->recv_sg_len);
2550
2551 if ((uint32_t)target > CONFIG_INITIATOR_NUM_TARGETS) {
2552 iscsi_err(__FILE__, __LINE__, "target %u\n",
2553 (uint32_t)target);
2554 NO_CLEANUP;
2555 return -1;
2556 }
2557
2558 /* Set and check scsi_cmd */
2559 if (scsi_cmd->trans_len > sess->sess_params.max_burst_length) {
2560 iscsi_err(__FILE__, __LINE__, "scsi_cmd->trans_len (%u) > MaxBurstLength (%u)\n",
2561 scsi_cmd->trans_len, sess->sess_params.max_burst_length);
2562 return -1;
2563 }
2564 if (scsi_cmd->length > scsi_cmd->trans_len) {
2565 iscsi_err(__FILE__, __LINE__, "scsi_cmd->length (%u) > scsi_cmd->trans_len (%u)\n",
2566 scsi_cmd->length, scsi_cmd->trans_len);
2567 return -1;
2568 }
2569 scsi_cmd->ExpStatSN = sess->ExpStatSN;
2570 scsi_cmd->CmdSN = sess->CmdSN;
2571 scsi_cmd->bytes_sent = scsi_cmd->bytes_recv = 0;
2572
2573 /* Always use iovec for data */
2574
2575 if (scsi_cmd->output) {
2576 if (scsi_cmd->send_sg_len) { /* Data already an iovec */
2577 sg = (struct iovec *)(void *)scsi_cmd->send_data;
2578 sg_len = scsi_cmd->send_sg_len;
2579 } else { /* Make iovec for data */
2580 sg_singleton.iov_base = scsi_cmd->send_data;
2581 sg_singleton.iov_len = scsi_cmd->trans_len;
2582 sg = &sg_singleton;
2583 sg_len = 1;
2584 }
2585 }
2586 /*
2587 * Insert cmd into the hash table, keyed by scsi_cmd->tag. The Rx
2588 * thread will
2589 */
2590 /* retrieve the cmd ptr using the tag from the response PDU. */
2591
2592 if (hash_insert(&g_tag_hash, cmd, scsi_cmd->tag) != 0) {
2593 iscsi_err(__FILE__, __LINE__, "hash_insert() failed\n");
2594 goto error;
2595 }
2596 /* Send command PDU */
2597
2598 if (scsi_cmd->output && sess->sess_params.immediate_data) {
2599 if (sess->sess_params.max_dataseg_len) {
2600 scsi_cmd->length = MIN(sess->sess_params.max_dataseg_len,
2601 scsi_cmd->trans_len);
2602 } else {
2603 scsi_cmd->length = scsi_cmd->trans_len;
2604 }
2605 if (scsi_cmd->length == scsi_cmd->trans_len)
2606 scsi_cmd->final = 1;
2607 } else {
2608 scsi_cmd->length = 0;
2609 scsi_cmd->final = 1;
2610 }
2611 if (iscsi_scsi_cmd_encap(header, scsi_cmd) != 0) {
2612 iscsi_err(__FILE__, __LINE__, "iscsi_scsi_cmd_encap() failed\n");
2613 goto error;
2614 }
2615 /*
2616 * If we're sending any immediate data, we need to make a new
2617 * iovec that contains only the immediata data (a subset of
2618 * the original iovec). */
2619 iscsi_trace(TRACE_ISCSI_DEBUG, "sending command PDU with %u bytes immediate data\n", scsi_cmd->length);
2620 if (scsi_cmd->length && sess->sess_params.immediate_data) {
2621 if ((sg_copy = iscsi_malloc_atomic(sg_len * sizeof(struct iovec))) == NULL) {
2622 iscsi_err(__FILE__, __LINE__, "iscsi_malloc_atomic() failed\n");
2623 goto error;
2624 }
2625 fragment_flag++;
2626 sg_copy_orig = sg_copy;
2627 memcpy(sg_copy, sg, sizeof(struct iovec) * sg_len);
2628 sg_len_copy = sg_len;
2629 if (modify_iov(&sg_copy, &sg_len_copy, 0, scsi_cmd->length) != 0) {
2630 iscsi_err(__FILE__, __LINE__, "modify_iov() failed\n");
2631 goto error;
2632 }
2633 if (scsi_cmd->ahs) {
2634 if (iscsi_sock_msg(sess->sock, 1, ISCSI_HEADER_LEN, header, 0) != ISCSI_HEADER_LEN) {
2635 iscsi_err(__FILE__, __LINE__, "iscsi_sock_msg() failed\n");
2636 goto error;
2637 }
2638 if (iscsi_sock_msg(sess->sock, 1, (unsigned)scsi_cmd->ahs_len, scsi_cmd->ahs, 0) != scsi_cmd->ahs_len) {
2639 iscsi_err(__FILE__, __LINE__, "iscsi_sock_msg() failed\n");
2640 goto error;
2641 }
2642 if ((unsigned)iscsi_sock_msg(sess->sock, 1, scsi_cmd->length, sg_copy, sg_len_copy) != scsi_cmd->length) {
2643 iscsi_err(__FILE__, __LINE__, "iscsi_sock_msg() failed\n");
2644 goto error;
2645 }
2646 } else {
2647 if ((unsigned)iscsi_sock_send_header_and_data(sess->sock, header, ISCSI_HEADER_LEN, sg_copy, scsi_cmd->length, sg_len_copy)
2648 != ISCSI_HEADER_LEN + scsi_cmd->length) {
2649 iscsi_err(__FILE__, __LINE__, "iscsi_sock_send_header_and_data() failed\n");
2650 goto error;
2651 }
2652 }
2653 scsi_cmd->bytes_sent += scsi_cmd->length;
2654 } else {
2655 if (iscsi_sock_msg(sess->sock, 1, ISCSI_HEADER_LEN, header, 0) != ISCSI_HEADER_LEN) {
2656 iscsi_err(__FILE__, __LINE__, "iscsi_sock_msg() failed\n");
2657 goto error;
2658 }
2659 if (scsi_cmd->ahs_len) {
2660 if (iscsi_sock_msg(sess->sock, 1, (unsigned)scsi_cmd->ahs_len, scsi_cmd->ahs, 0) != scsi_cmd->ahs_len) {
2661 iscsi_err(__FILE__, __LINE__, "iscsi_sock_msg() failed\n");
2662 goto error;
2663 }
2664 }
2665 }
2666 iscsi_trace(TRACE_ISCSI_DEBUG, "command PDU sent with %u bytes immediate data (%u bytes AHS)\n", scsi_cmd->length, scsi_cmd->ahs_len);
2667
2668 /*
2669 * Send data PDUS if 1) we're not in R2T mode and 2) we
2670 * haven't sent everything as immediate data and 3) we have
2671 * not reached the first burst when sending immediate data
2672 */
2673 if (scsi_cmd->output
2674 && (!sess->sess_params.initial_r2t)
2675 && (scsi_cmd->bytes_sent != scsi_cmd->trans_len)
2676 && ((!sess->sess_params.first_burst_length)
2677 || (scsi_cmd->bytes_sent < sess->sess_params.first_burst_length))) {
2678
2679 uint32_t DataSN = 0;
2680
2681 iscsi_trace(TRACE_ISCSI_DEBUG, "preparing to send %d bytes write data\n", scsi_cmd->trans_len - scsi_cmd->bytes_sent);
2682
2683 do {
2684 (void) memset(&data, 0x0, sizeof(data));
2685
2686 /*
2687 * Take into account that MaxRecvPDULength and
2688 * FirstBurstLength could both be "0" (no limit)
2689 */
2690 if (sess->sess_params.max_dataseg_len) {
2691 if (sess->sess_params.first_burst_length) {
2692 data.length = MIN_3(
2693 sess->sess_params.first_burst_length - scsi_cmd->bytes_sent,
2694 sess->sess_params.max_dataseg_len,
2695 scsi_cmd->trans_len - scsi_cmd->bytes_sent);
2696 } else {
2697 data.length = MIN(
2698 sess->sess_params.max_dataseg_len,
2699 scsi_cmd->trans_len - scsi_cmd->bytes_sent);
2700 }
2701 } else {
2702 if (sess->sess_params.first_burst_length) {
2703 data.length = MIN(
2704 sess->sess_params.first_burst_length - scsi_cmd->bytes_sent,
2705 scsi_cmd->trans_len - scsi_cmd->bytes_sent);
2706 } else {
2707 data.length = scsi_cmd->trans_len - scsi_cmd->bytes_sent;
2708 }
2709 }
2710 #define FRAG_CLEANUP {if (fragment_flag) iscsi_free_atomic(sg_copy);}
2711
2712 if (data.length == 0) {
2713 iscsi_err(__FILE__, __LINE__,
2714 "Zero data.length\n");
2715 FRAG_CLEANUP;
2716 return -1;
2717 }
2718
2719 if (scsi_cmd->bytes_sent + data.length ==
2720 scsi_cmd->trans_len) {
2721 data.final = 1;
2722 }
2723 data.tag = scsi_cmd->tag;
2724 data.transfer_tag = 0xffffffff;
2725 data.ExpStatSN = sess->ExpStatSN;
2726 data.DataSN = DataSN++;
2727 data.offset = scsi_cmd->bytes_sent;
2728
2729 if (iscsi_write_data_encap(header, &data) != 0) {
2730 iscsi_err(__FILE__, __LINE__, "iscsi_write_data_encap() failed\n");
2731 goto error;
2732 }
2733 if (data.length != scsi_cmd->trans_len) {
2734
2735 /*
2736 * Make copy of iovec and modify with offset
2737 * and length
2738 */
2739
2740 if (!fragment_flag) {
2741 if ((sg_copy = iscsi_malloc_atomic(sg_len * sizeof(struct iovec))) == NULL) {
2742 iscsi_err(__FILE__, __LINE__, "iscsi_malloc_atomic() failed\n");
2743 goto error;
2744 }
2745 sg_copy_orig = sg_copy;
2746 fragment_flag++;
2747 }
2748 sg_copy = sg_copy_orig;
2749 memcpy(sg_copy, sg, sizeof(struct iovec) * sg_len);
2750 sg_len_copy = sg_len;
2751 if (modify_iov(&sg_copy, &sg_len_copy, scsi_cmd->bytes_sent, data.length) != 0) {
2752 iscsi_err(__FILE__, __LINE__, "modify_iov() failed\n");
2753 goto error;
2754 }
2755 sg_which = sg_copy;
2756 sg_len_which = sg_len_copy;
2757
2758 } else {
2759
2760 /*
2761 * Data was not fragmented; use the original
2762 * iovec.
2763 */
2764
2765 sg_which = sg;
2766 sg_len_which = sg_len;
2767 }
2768
2769 iscsi_trace(TRACE_ISCSI_DEBUG, "sending write data PDU (offset %u, len %u, sg_len %u)\n",
2770 data.offset, data.length, sg_len_which);
2771
2772 if ((unsigned)iscsi_sock_send_header_and_data(sess->sock, header, ISCSI_HEADER_LEN, sg_which, data.length, sg_len_which)
2773 != ISCSI_HEADER_LEN + data.length) {
2774 iscsi_err(__FILE__, __LINE__, "iscsi_sock_send_header_and_data() failed\n");
2775 goto error;
2776 }
2777 iscsi_trace(TRACE_ISCSI_DEBUG, "sent write data PDU (offset %u, len %u)\n", data.offset, data.length);
2778 scsi_cmd->bytes_sent += data.length;
2779 } while ((scsi_cmd->bytes_sent < scsi_cmd->trans_len)
2780 && ((scsi_cmd->bytes_sent < sess->sess_params.first_burst_length)
2781 || (!sess->sess_params.first_burst_length)));
2782 if (scsi_cmd->trans_len - scsi_cmd->bytes_sent) {
2783 iscsi_trace(TRACE_ISCSI_DEBUG, "REACHED FIRST BURST\n");
2784 }
2785 iscsi_trace(TRACE_ISCSI_DEBUG, "successfully sent %u of %u bytes write data\n", scsi_cmd->bytes_sent, scsi_cmd->trans_len);
2786 }
2787 if (scsi_cmd->output && (scsi_cmd->trans_len - scsi_cmd->bytes_sent)) {
2788 iscsi_trace(TRACE_ISCSI_DEBUG, "expecting R2T for remaining %u bytes write data\n", scsi_cmd->trans_len - scsi_cmd->bytes_sent);
2789 }
2790 if (fragment_flag)
2791 iscsi_free_atomic(sg_copy_orig);
2792 sess->CmdSN++;
2793
2794 return 0;
2795
2796 error:
2797 if (fragment_flag)
2798 iscsi_free_atomic(sg_copy);
2799 return -1;
2800 }
2801
2802 static int
reject_i(initiator_session_t * sess,uint8_t * header)2803 reject_i(initiator_session_t * sess, uint8_t *header)
2804 {
2805 initiator_cmd_t *cmd = NULL;
2806 iscsi_reject_t reject;
2807 uint8_t bad_header[ISCSI_HEADER_LEN];
2808 uint32_t tag;
2809
2810 /* Get & check args */
2811
2812 if (iscsi_reject_decap(header, &reject) != 0) {
2813 iscsi_err(__FILE__, __LINE__, "iscsi_reject_decap() failed\n");
2814 return -1;
2815 }
2816 if (reject.length != ISCSI_HEADER_LEN) {
2817 iscsi_err(__FILE__, __LINE__, "reject.length %u\n",
2818 reject.length);
2819 NO_CLEANUP;
2820 return -1;
2821 }
2822
2823 /* Read bad header, extract tag, and get cmd from hash table */
2824
2825 if (iscsi_sock_msg(sess->sock, 0, ISCSI_HEADER_LEN, bad_header, 0) != ISCSI_HEADER_LEN) {
2826 iscsi_err(__FILE__, __LINE__, "iscsi_sock_msg() failed\n");
2827 return -1;
2828 }
2829 (void) memcpy(&tag, bad_header + 16, sizeof(tag));
2830 tag = ISCSI_NTOHL(tag);
2831 iscsi_err(__FILE__, __LINE__, "REJECT PDU: tag %#x (reason %#x)\n", tag, reject.reason);
2832 if (tag != 0xffffffff) {
2833 if ((cmd = hash_remove(&g_tag_hash, tag)) == NULL) {
2834 iscsi_trace(TRACE_ISCSI_DEBUG, "no cmd ptr associated with tag %#x\n", tag);
2835 } else {
2836 iscsi_trace(TRACE_ISCSI_DEBUG, "cmd %p associated with tag %#x\n", cmd, tag);
2837 ISCSI_LOCK(&sess->rx_worker.work_mutex, return -1);
2838 if (!cmd->tx_done)
2839 ISCSI_WAIT(&sess->rx_worker.work_cond, &sess->rx_worker.work_mutex, return -1);
2840 ISCSI_UNLOCK(&sess->rx_worker.work_mutex, return -1);
2841 }
2842 } else {
2843 iscsi_err(__FILE__, __LINE__, "no command associated with tag %#x\n", tag);
2844 }
2845
2846 /* Execute callback to complete initiator_cmd_t */
2847
2848 if (cmd) {
2849 cmd->status = -1;
2850 if (cmd->callback) {
2851 iscsi_trace(TRACE_ISCSI_DEBUG, "issuing callback for cmd associated with tag %#x\n", tag);
2852 if ((*cmd->callback)(cmd) != 0) {
2853 iscsi_err(__FILE__, __LINE__, "callback() failed\n");
2854 return -1;
2855 }
2856 } else {
2857 iscsi_err(__FILE__, __LINE__, "no callback associated with tag %#x\n", tag);
2858 }
2859 }
2860 return 0;
2861 }
2862
2863 static int
async_msg_i(initiator_session_t * sess,uint8_t * header)2864 async_msg_i(initiator_session_t * sess, uint8_t *header)
2865 {
2866 iscsi_async_msg_t msg;
2867
2868 /* Get & check args */
2869 if (iscsi_amsg_decap(header, &msg) != 0) {
2870 iscsi_err(__FILE__, __LINE__, "iscsi_amsg_decap() failed\n");
2871 return -1;
2872 }
2873 sess->CmdSN = msg.ExpCmdSN;
2874 sess->MaxCmdSN = msg.MaxCmdSN;
2875 sess->ExpStatSN = msg.StatSN + 1;
2876
2877 /* Read Sense Data */
2878 if (msg.length) {
2879 uint8_t *sense_data = NULL;
2880 if ((sense_data = iscsi_malloc(msg.length)) == NULL) {
2881 iscsi_err(__FILE__, __LINE__, "iscsi_malloc() failed\n");
2882 return -1;
2883 }
2884 iscsi_trace(TRACE_ISCSI_DEBUG, "reading %d bytes sense data \n", msg.length);
2885 if ((unsigned)iscsi_sock_msg(sess->sock, 0, msg.length, sense_data, 0) != msg.length) {
2886 iscsi_err(__FILE__, __LINE__, "iscsi_sock_msg() failed\n");
2887 if (sense_data != NULL)
2888 iscsi_free(sense_data);
2889 return -1;
2890 }
2891 iscsi_trace(TRACE_ISCSI_DEBUG, "read %d bytes sense data ok (currently discarding)\n", msg.length);
2892 if (sense_data != NULL)
2893 iscsi_free(sense_data);
2894 } else {
2895 iscsi_trace(TRACE_ISCSI_DEBUG, "no sense data available\n");
2896 }
2897
2898 switch (msg.AsyncEvent) {
2899 case 0:
2900 /* Ignore SCSI asyn messages for now */
2901 break;
2902 case 1:
2903 case 4:
2904 /* Ignore Parameter Negotiation. Send Logout */
2905 logout_phase_i(sess);
2906 /* FALLTHROUGH */
2907 case 2:
2908 case 3:
2909 if (iscsi_sock_shutdown(sess->sock, 1) != 0) {
2910 iscsi_err(__FILE__, __LINE__, "iscsi_sock_shutdown() failed\n");
2911 }
2912 return -1;
2913 case 255:
2914 break;
2915 default:
2916 break;
2917 }
2918
2919 return 0;
2920 }
2921
2922 static int
nop_in_i(initiator_session_t * sess,initiator_cmd_t * cmd,uint8_t * header)2923 nop_in_i(initiator_session_t * sess, initiator_cmd_t * cmd, uint8_t *header)
2924 {
2925 iscsi_nop_out_args_t *nop_out = NULL;
2926 iscsi_nop_in_args_t nop_in;
2927 uint8_t *ping_data = NULL;
2928 unsigned i;
2929
2930 if (cmd) {
2931 nop_out = (iscsi_nop_out_args_t *) cmd->ptr;
2932 } else {
2933 iscsi_err(__FILE__, __LINE__, "no initiator_cmd_t associated with this NOP_IN\n");
2934 }
2935 if (iscsi_nop_in_decap(header, &nop_in) != 0) {
2936 iscsi_err(__FILE__, __LINE__, "iscsi_nop_in() failed\n");
2937 return -1;
2938 }
2939 if (nop_in.transfer_tag == 0xffffffff) {
2940 if (nop_in.length != 0) {
2941 iscsi_err(__FILE__, __LINE__,
2942 "nop_in.length %u not 0\n",
2943 nop_in.length);
2944 NO_CLEANUP;
2945 return -1;
2946 }
2947 return 0;
2948 }
2949 if (cmd) {
2950 #if 0
2951 RETURN_NOT_EQUAL("nop_in.length", nop_in.length, nop_out->length, NO_CLEANUP, -1);
2952 #else
2953 if (nop_in.length != nop_out->length) {
2954 iscsi_err(__FILE__, __LINE__,
2955 "nop_in.length %u, nopout->length %u\n",
2956 nop_in.length, nop_out->length);
2957 NO_CLEANUP;
2958 return -1;
2959 }
2960 #endif
2961 }
2962 if (nop_in.length) {
2963 iscsi_trace(TRACE_ISCSI_DEBUG,
2964 "reading %d bytes ping data\n", nop_in.length);
2965 if ((ping_data = iscsi_malloc_atomic(nop_in.length)) == NULL) {
2966 iscsi_err(__FILE__, __LINE__,
2967 "iscsi_malloc_atomic() failed\n");
2968 return -1;
2969 }
2970 #define NOI_CLEANUP {if (ping_data) iscsi_free_atomic(ping_data);}
2971 #define NOI_ERROR {NOI_CLEANUP; return -1;}
2972 if ((unsigned)iscsi_sock_msg(sess->sock, 0, nop_in.length,
2973 ping_data, 0) != nop_in.length) {
2974 iscsi_err(__FILE__, __LINE__,
2975 "iscsi_sock_msg() failed\n");
2976 NOI_ERROR;
2977 }
2978 iscsi_trace(TRACE_ISCSI_DEBUG,
2979 "successfully read %d bytes ping data\n",
2980 nop_in.length);
2981 if (cmd) {
2982 for (i = 0; i < nop_in.length; i++) {
2983 if (nop_out->data[i] != ping_data[i]) {
2984 iscsi_err(__FILE__, __LINE__,
2985 "Bad ping data[%d]. "
2986 "Got %#x, expected %#x\n",
2987 i, ping_data[i],
2988 nop_out->data[i]);
2989 NOI_ERROR;
2990 }
2991 }
2992 }
2993 }
2994
2995 /* Send ping response (if initiated by target) */
2996 if (nop_in.transfer_tag != 0xffffffff) {
2997 uint8_t nop_header[ISCSI_HEADER_LEN];
2998 iscsi_nop_out_args_t nop_out_args;
2999
3000 iscsi_trace(TRACE_ISCSI_DEBUG,
3001 "sending %d byte ping response\n", nop_in.length);
3002 (void) memset(&nop_out_args, 0x0, sizeof(nop_out_args));
3003 nop_out_args.tag = 0xffffffff;
3004 nop_out_args.immediate = 0x40;
3005 nop_out_args.transfer_tag = nop_in.transfer_tag;
3006 nop_out_args.length = nop_in.length;
3007 nop_out_args.lun = nop_in.lun;
3008 nop_out_args.ExpStatSN = sess->ExpStatSN;
3009 nop_out_args.CmdSN = sess->CmdSN;
3010 if (iscsi_nop_out_encap(nop_header, &nop_out_args) != 0) {
3011 iscsi_err(__FILE__, __LINE__,
3012 "iscsi_nop_out_encap() failed\n");
3013 NOI_ERROR;
3014 }
3015 if ((unsigned)iscsi_sock_send_header_and_data(sess->sock,
3016 nop_header, nop_out_args.length, ping_data,
3017 nop_in.length, 0) != nop_in.length) {
3018 iscsi_err(__FILE__, __LINE__,
3019 "iscsi_sock_msg() failed\n");
3020 NOI_ERROR;
3021 }
3022 iscsi_trace(TRACE_ISCSI_DEBUG,
3023 "successfully sent %d byte ping response\n",
3024 nop_in.length);
3025 }
3026 NOI_CLEANUP;
3027 /* Check and update numbering */
3028 sess->ExpStatSN = nop_in.StatSN + 1;
3029 /*
3030 * RETURN_NOT_EQUAL("StatSN", nop_in.StatSN, sess->ExpStatSN++,
3031 * NO_CLEANUP, -1);
3032 */
3033 sess->CmdSN = nop_in.ExpCmdSN;
3034 /*
3035 * RETURN_NOT_EQUAL("ExpCmdSN", nop_in.ExpCmdSN, sess->CmdSN,
3036 * NO_CLEANUP, -1);
3037 */
3038 sess->MaxCmdSN = nop_in.MaxCmdSN;
3039
3040 /* Callback */
3041
3042 if (cmd) {
3043 cmd->status = 0;
3044 if (cmd->callback) {
3045 iscsi_trace(TRACE_ISCSI_DEBUG, "NOP_OUT_T done (cmd status %d)\n", cmd->status);
3046 if ((*cmd->callback)(cmd) != 0) {
3047 iscsi_err(__FILE__, __LINE__, "callback() failed\n");
3048 return -1;
3049 }
3050 } else {
3051 iscsi_trace(TRACE_ISCSI_DEBUG, "no callback associated with NOP_IN_T??\n");
3052 return -1;
3053 }
3054 }
3055 return 0;
3056 }
3057
3058 static int
scsi_r2t_i(initiator_session_t * sess,initiator_cmd_t * cmd,uint8_t * header)3059 scsi_r2t_i(initiator_session_t * sess, initiator_cmd_t * cmd, uint8_t *header)
3060 {
3061 iscsi_r2t_t r2t;
3062 iscsi_scsi_cmd_args_t *scsi_cmd;
3063 iscsi_write_data_t data;
3064 uint32_t bytes_sent;
3065 uint32_t DataSN;
3066 struct iovec sg_singleton;
3067 struct iovec *sg, *sg_copy, *sg_copy_orig, *sg_which;
3068 int sg_len, sg_len_copy, sg_len_which;
3069 int fragment_flag;
3070
3071 /* Make sure an initiator_cmd_t was specified, that it has a
3072 * callback function specified and that it also has a
3073 * iscsi_scsi_cmd_args_t associated with it. */
3074
3075 if (cmd) {
3076 if ((scsi_cmd = (iscsi_scsi_cmd_args_t *) cmd->ptr) == NULL) {
3077 iscsi_err(__FILE__, __LINE__, "no iscsi_scsi_cmd_args_t associated with this initiator_cmd_t??\n");
3078 return -1;
3079 } else if (cmd->callback == NULL) {
3080 iscsi_err(__FILE__, __LINE__, "no callback associated with this initiator_cmd_t??\n");
3081 return -1;
3082 }
3083 } else {
3084 iscsi_err(__FILE__, __LINE__, "no initiator_cmd_t associated with this iscsi_r2t_t??\n");
3085 return -1;
3086 }
3087
3088 sg = sg_copy = sg_copy_orig = sg_which = NULL;
3089 sg_len = sg_len_copy = sg_len_which = 0;
3090 if (iscsi_r2t_decap(header, &r2t) != 0) {
3091 iscsi_err(__FILE__, __LINE__, "iscsi_r2t_decap() failed\n");
3092 return -1;
3093 }
3094
3095 /* Check args */
3096 if (r2t.length == 0) {
3097 iscsi_err(__FILE__, __LINE__, "Zero r2t.length\n");
3098 NO_CLEANUP;
3099 return -1;
3100 }
3101
3102 /* Check and update numbering */
3103 #if 0
3104 RETURN_NOT_EQUAL("StatSN", r2t.StatSN, sess->ExpStatSN, NO_CLEANUP, -1);
3105 RETURN_NOT_EQUAL("ExpCmdSN", r2t.ExpCmdSN, sess->CmdSN, NO_CLEANUP, -1);
3106 #else
3107 if (r2t.StatSN != sess->ExpStatSN) {
3108 iscsi_err(__FILE__, __LINE__,
3109 "r2t.StatSN %u, sess->ExpStatSN %u\n",
3110 r2t.StatSN, sess->ExpStatSN);
3111 NO_CLEANUP;
3112 return -1;
3113 }
3114 if (r2t.ExpCmdSN != sess->CmdSN) {
3115 iscsi_err(__FILE__, __LINE__,
3116 "r2t.ExpCmdSN %u, sess->CmdSN %u\n",
3117 r2t.ExpCmdSN, sess->CmdSN);
3118 NO_CLEANUP;
3119 return -1;
3120 }
3121 #endif
3122 sess->MaxCmdSN = r2t.MaxCmdSN;
3123
3124 /* Send back requested data */
3125 iscsi_trace(TRACE_ISCSI_DEBUG,
3126 "sending %d bytes R2T write data (offset %u)\n",
3127 r2t.length, r2t.offset);
3128 if (scsi_cmd->send_sg_len) {
3129 sg = (struct iovec *)(void *)scsi_cmd->send_data;
3130 sg_len = scsi_cmd->send_sg_len;
3131 } else {
3132 sg_singleton.iov_base = scsi_cmd->send_data;
3133 sg_singleton.iov_len = scsi_cmd->trans_len;
3134 sg = &sg_singleton;
3135 sg_len = 1;
3136 }
3137 fragment_flag = 0;
3138 bytes_sent = 0;
3139 DataSN = 0;
3140 #define FF_CLEANUP {if (fragment_flag) iscsi_free_atomic(sg_copy_orig);}
3141 do {
3142 (void) memset(&data, 0x0, sizeof(data));
3143 if (sess->sess_params.max_dataseg_len) {
3144 data.length = MIN(sess->sess_params.max_dataseg_len,
3145 r2t.length - bytes_sent);
3146 } else {
3147 data.length = r2t.length - bytes_sent;
3148 }
3149 if (bytes_sent + data.length == r2t.length) {
3150 data.final = 1;
3151 }
3152 data.tag = r2t.tag;
3153 data.transfer_tag = r2t.transfer_tag;
3154 data.ExpStatSN = sess->ExpStatSN;
3155 data.DataSN = DataSN++;
3156 data.offset = r2t.offset + bytes_sent;
3157 data.lun = scsi_cmd->lun;
3158 if (iscsi_write_data_encap(header, &data) != 0) {
3159 iscsi_err(__FILE__, __LINE__, "iscsi_write_data_encap() failed\n");
3160 FF_CLEANUP;
3161 return -1;
3162 }
3163 if ((data.length < r2t.length) || (r2t.offset)) {
3164 if (data.length < r2t.length) {
3165 iscsi_trace(TRACE_ISCSI_DEBUG, "R2T data is being fragmented: sending %u bytes of %u requested\n",
3166 data.length, r2t.length);
3167 } else {
3168 iscsi_trace(TRACE_ISCSI_DEBUG, "R2T data starts at offset %u, desired length %u\n",
3169 r2t.offset, r2t.length);
3170 }
3171
3172 /* Allocate space for a copy of the original iovec */
3173
3174 if (!fragment_flag) {
3175 if ((sg_copy_orig = iscsi_malloc_atomic(sg_len * sizeof(struct iovec))) == NULL) {
3176 iscsi_err(__FILE__, __LINE__, "iscsi_malloc_atomic() failed\n");
3177 return -1;
3178 }
3179 fragment_flag++;
3180 }
3181 /*
3182 * Copy and modify original iovec with new offset and
3183 * length
3184 */
3185
3186 iscsi_trace(TRACE_ISCSI_DEBUG, "modifying original iovec with offset %u length %u\n",
3187 r2t.offset + bytes_sent, data.length);
3188 sg_copy = sg_copy_orig;
3189 sg_len_copy = sg_len;
3190 memcpy(sg_copy, sg, sizeof(struct iovec) * sg_len);
3191 if (modify_iov(&sg_copy, &sg_len_copy, r2t.offset + bytes_sent, data.length) != 0) {
3192 iscsi_err(__FILE__, __LINE__, "modify_iov() failed\n");
3193 FF_CLEANUP;
3194 return -1;
3195 }
3196 sg_which = sg_copy;
3197 sg_len_which = sg_len_copy;
3198 } else {
3199 iscsi_trace(TRACE_ISCSI_DEBUG, "using original iovec for R2T transfer (offset %u, length %u)\n",
3200 r2t.offset, r2t.length);
3201 sg_which = sg;
3202 sg_len_which = sg_len;
3203 }
3204 iscsi_trace(TRACE_ISCSI_DEBUG, "sending R2T write data PDU (offset %u, len %u, sg_len %u)\n",
3205 data.offset, data.length, sg_len_which);
3206 if ((unsigned)iscsi_sock_send_header_and_data(sess->sock, header, ISCSI_HEADER_LEN, sg_which, data.length, sg_len_which)
3207 != ISCSI_HEADER_LEN + data.length) {
3208 iscsi_err(__FILE__, __LINE__, "iscsi_sock_send_header_and_data() failed\n");
3209 FF_CLEANUP;
3210 return -1;
3211 }
3212 iscsi_trace(TRACE_ISCSI_DEBUG, "sent write data PDU OK (offset %u, len %u)\n", data.offset, data.length);
3213 bytes_sent += data.length;
3214 scsi_cmd->bytes_sent += data.length;
3215 } while (bytes_sent < r2t.length);
3216 FF_CLEANUP;
3217 if (hash_insert(&g_tag_hash, cmd, scsi_cmd->tag) != 0) {
3218 iscsi_err(__FILE__, __LINE__, "hash_insert() failed\n");
3219 return -1;
3220 }
3221 return 0;
3222 }
3223
3224 static int
scsi_response_i(initiator_session_t * sess,initiator_cmd_t * cmd,uint8_t * header)3225 scsi_response_i(initiator_session_t * sess, initiator_cmd_t * cmd, uint8_t *header)
3226 {
3227 iscsi_scsi_cmd_args_t *scsi_cmd;
3228 iscsi_scsi_rsp_t scsi_rsp;
3229 const char *errmsg;
3230
3231 /* Make sure an initiator_cmd_t was specified, that it has a
3232 * callback function specified and that it also has a
3233 * iscsi_scsi_cmd_args_t associated with it. */
3234
3235 if (cmd) {
3236 if ((scsi_cmd = (iscsi_scsi_cmd_args_t *) cmd->ptr) == NULL) {
3237 iscsi_err(__FILE__, __LINE__, "no iscsi_scsi_cmd_args_t associated with this initiator_cmd_t??\n");
3238 return -1;
3239 } else if (cmd->callback == NULL) {
3240 iscsi_err(__FILE__, __LINE__, "no callback associated with this initiator_cmd_t??\n");
3241 return -1;
3242 }
3243 } else {
3244 iscsi_err(__FILE__, __LINE__, "no initiator_cmd_t associated with this iscsi_scsi_rsp_t??\n");
3245 return -1;
3246 }
3247
3248 /*
3249 * Read SCSI response and check return args. Those marked
3250 * "FIX ME" are not yet implemented. */
3251
3252 if (iscsi_scsi_rsp_decap(header, &scsi_rsp) != 0) {
3253 iscsi_err(__FILE__, __LINE__, "iscsi_scsi_rsp_decap() failed\n");
3254 return -1;
3255 }
3256 #if 0
3257 RETURN_NOT_EQUAL("o bit (FIX ME)", scsi_rsp.bidi_overflow, 0, NO_CLEANUP, -1);
3258 RETURN_NOT_EQUAL("u bit (FIX ME)", scsi_rsp.bidi_underflow, 0, NO_CLEANUP, -1);
3259 RETURN_NOT_EQUAL("O bit (FIX ME)", scsi_rsp.overflow, 0, NO_CLEANUP, -1);
3260 RETURN_NOT_EQUAL("iSCSI Response (FIX ME)", scsi_rsp.response, 0, NO_CLEANUP, -1);
3261 RETURN_NOT_EQUAL("Tag", scsi_rsp.tag, scsi_cmd->tag, NO_CLEANUP, -1);
3262 RETURN_NOT_EQUAL("Bidi Residual Count", scsi_rsp.bidi_res_cnt, 0, NO_CLEANUP, -1);
3263 RETURN_NOT_EQUAL("StatSN", scsi_rsp.StatSN, sess->ExpStatSN, NO_CLEANUP, -1);
3264 #else
3265 errmsg = NULL;
3266 if (scsi_rsp.bidi_overflow != 0) {
3267 errmsg = "o bit (FIX ME)\n";
3268 } else if (scsi_rsp.bidi_underflow != 0) {
3269 errmsg = "u bit (FIX ME)\n";
3270 } else if (scsi_rsp.overflow != 0) {
3271 errmsg = "O bit (FIX ME)\n";
3272 } else if (scsi_rsp.response != 0) {
3273 errmsg = "Response (FIX ME)\n";
3274 } else if (scsi_rsp.tag != scsi_cmd->tag) {
3275 errmsg = "Tags don't match\n";
3276 } else if (scsi_rsp.bidi_res_cnt != 0) {
3277 errmsg = "Bidi Residual Count";
3278 } else if (scsi_rsp.StatSN != sess->ExpStatSN) {
3279 errmsg = "StatSN";
3280 }
3281 if (errmsg) {
3282 iscsi_err(__FILE__, __LINE__, "%s", errmsg);
3283 NO_CLEANUP;
3284 return -1;
3285 }
3286 #endif
3287 sess->ExpStatSN = scsi_rsp.StatSN + 1;
3288
3289 if (sess->sess_params.max_dataseg_len &&
3290 scsi_rsp.length > sess->sess_params.max_dataseg_len) {
3291 iscsi_err(__FILE__, __LINE__,
3292 "scsi_rsp.length %u\n", scsi_rsp.length);
3293 NO_CLEANUP;
3294 return -1;
3295 }
3296 if ((scsi_rsp.status == 0) && (scsi_rsp.length != 0)) {
3297 iscsi_err(__FILE__, __LINE__,
3298 "Unexpected DataSegmentLength %u "
3299 "with GOOD SCSI status\n", scsi_rsp.length);
3300 return -1;
3301 }
3302 /*
3303 * Make sure all data was successfully transferred if command
3304 * completed successfully, otherwise read sense data. */
3305
3306 if (scsi_rsp.status == 0) {
3307 if (scsi_cmd->output) {
3308 #if 0
3309 RETURN_NOT_EQUAL("scsi_cmd->bytes_sent", scsi_cmd->bytes_sent, scsi_cmd->trans_len, NO_CLEANUP, -1);
3310 #else
3311 if (scsi_cmd->bytes_sent != scsi_cmd->trans_len) {
3312 iscsi_err(__FILE__, __LINE__,
3313 "scsi_cmd->bytes_sent\n");
3314 NO_CLEANUP;
3315 return -1;
3316 }
3317 #endif
3318 if (scsi_cmd->input) {
3319
3320 #if 0
3321 RETURN_NOT_EQUAL("scsi_cmd->bytes_recv", scsi_cmd->bytes_recv, scsi_cmd->bidi_trans_len, NO_CLEANUP, -1);
3322 #else
3323 if (scsi_cmd->bytes_recv != scsi_cmd->bidi_trans_len) {
3324 iscsi_err(__FILE__, __LINE__,
3325 "scsi_cmd->bytes_recv\n");
3326 NO_CLEANUP;
3327 return -1;
3328 }
3329 #endif
3330 }
3331 } else if (scsi_cmd->input) {
3332
3333
3334 }
3335 } else if (scsi_rsp.length) {
3336 uint8_t *sense_data = NULL;
3337
3338 if ((sense_data = iscsi_malloc(scsi_rsp.length)) == NULL) {
3339 iscsi_err(__FILE__, __LINE__,
3340 "iscsi_malloc() failed\n");
3341 return -1;
3342 }
3343 iscsi_err(__FILE__, __LINE__,
3344 "reading %d bytes sense data (recv_sg_len %u)\n",
3345 scsi_rsp.length, scsi_cmd->recv_sg_len);
3346 if ((unsigned)iscsi_sock_msg(sess->sock, 0, scsi_rsp.length,
3347 sense_data, 0) != scsi_rsp.length) {
3348 iscsi_err(__FILE__, __LINE__,
3349 "iscsi_sock_msg() failed\n");
3350 if (sense_data != NULL) {
3351 iscsi_free(sense_data);
3352 }
3353 return -1;
3354 }
3355 iscsi_err(__FILE__, __LINE__,
3356 "read %d bytes sense data ok (currently discarding)\n",
3357 scsi_rsp.length);
3358 if (sense_data != NULL) {
3359 iscsi_free(sense_data);
3360 }
3361 } else {
3362 iscsi_trace(TRACE_ISCSI_DEBUG, "no sense data available\n");
3363 }
3364
3365 /* Check and update numbering */
3366
3367 /*
3368 * RETURN_NOT_EQUAL("ExpCmdSN", scsi_rsp.ExpCmdSN, sess->CmdSN,
3369 * NO_CLEANUP, -1);
3370 */
3371 sess->MaxCmdSN = scsi_rsp.MaxCmdSN;
3372
3373 /* Set initiator_cmd_t status, iscsi_scsi_cmd_args_t status */
3374 /* and execute callback function */
3375
3376 cmd->status = 0;
3377 scsi_cmd->status = scsi_rsp.status;
3378 iscsi_trace(TRACE_ISCSI_DEBUG,
3379 "iscsi_scsi_cmd_args_t done (cmd status %d, iscsi status %d, "
3380 "scsi status %d)\n",
3381 cmd->status, scsi_rsp.response, scsi_rsp.status);
3382 if ((*cmd->callback)(cmd) != 0) {
3383 iscsi_err(__FILE__, __LINE__, "callback() failed\n");
3384 return -1;
3385
3386 }
3387 return 0;
3388 }
3389
3390 static int
scsi_read_data_i(initiator_session_t * sess,initiator_cmd_t * cmd,uint8_t * header)3391 scsi_read_data_i(initiator_session_t * sess, initiator_cmd_t * cmd, uint8_t *header)
3392 {
3393 iscsi_scsi_cmd_args_t *scsi_cmd;
3394 iscsi_read_data_t data;
3395 const char *errmsg;
3396 int rc;
3397
3398 iscsi_trace(TRACE_ISCSI_DEBUG, "processing read data\n");
3399
3400 /* Make sure an initiator_cmd_t was specified, that it has a
3401 * callback function specified and that it also has a
3402 * iscsi_scsi_cmd_args_t associated with it. */
3403
3404 if (cmd) {
3405 if (cmd->type != ISCSI_SCSI_CMD) {
3406 iscsi_err(__FILE__, __LINE__,
3407 "Invalid response from target for cmd "
3408 "type (%#x)\n", cmd->type);
3409 cmd->status = -1;
3410 if (cmd->callback) {
3411 (*cmd->callback)(cmd);
3412 }
3413 return -1;
3414 }
3415 if ((scsi_cmd = (iscsi_scsi_cmd_args_t *) cmd->ptr) == NULL) {
3416 iscsi_err(__FILE__, __LINE__,
3417 "no iscsi_scsi_cmd_args_t associated with "
3418 "this initiator_cmd_t??\n");
3419 return -1;
3420 } else if (cmd->callback == NULL) {
3421 iscsi_err(__FILE__, __LINE__,
3422 "no callback associated with this "
3423 "initiator_cmd_t??\n");
3424 return -1;
3425 }
3426 } else {
3427 iscsi_err(__FILE__, __LINE__,
3428 "no initiator_cmd_t associated with this "
3429 "iscsi_read_data_t??\n");
3430 return -1;
3431 }
3432 if (iscsi_read_data_decap(header, &data) != 0) {
3433 iscsi_err(__FILE__, __LINE__,
3434 "iscsi_scsi_rsp_decap() failed\n");
3435 return -1;
3436 }
3437
3438 /* Check args */
3439 #if 0
3440 RETURN_NOT_EQUAL("Overflow bit", data.overflow, 0, NO_CLEANUP, -1);
3441 RETURN_NOT_EQUAL("Underflow bit", data.underflow, 0, NO_CLEANUP, -1);
3442 RETURN_NOT_EQUAL("Tag", data.task_tag, scsi_cmd->tag, NO_CLEANUP, -1);
3443 RETURN_NOT_EQUAL("Residual Count", data.res_count, 0, NO_CLEANUP, -1);
3444 #else
3445 errmsg = NULL;
3446 if (data.overflow != 0) {
3447 errmsg = "Overflow bit";
3448 } else if (data.task_tag != scsi_cmd->tag) {
3449 errmsg = "Tag";
3450 } else if (!data.underflow) {
3451 if (data.res_count != 0) {
3452 errmsg = "Residual Count";
3453 }
3454 } else {
3455 iscsi_warn(__FILE__, __LINE__, "Underflow %" PRIu32 "\n", data.res_count);
3456 }
3457 if (errmsg) {
3458 iscsi_err(__FILE__, __LINE__, "%s", errmsg);
3459 NO_CLEANUP;
3460 return -1;
3461 }
3462 #endif
3463
3464 if (sess->sess_params.max_dataseg_len) {
3465 if (data.length > sess->sess_params.max_dataseg_len) {
3466 iscsi_err(__FILE__, __LINE__,
3467 "data.length %u\n", data.length);
3468 NO_CLEANUP;
3469 return -1;
3470 }
3471 }
3472
3473 /* Check and update numbering */
3474 if (data.ExpCmdSN != sess->CmdSN) {
3475 iscsi_warn(__FILE__, __LINE__,
3476 "Bad \"ExpCmdSN\": Got %u expected %u.\n",
3477 data.ExpCmdSN, sess->CmdSN);
3478 }
3479 sess->MaxCmdSN = data.MaxCmdSN;
3480
3481 /* Need to optimize this section */
3482
3483 if (scsi_cmd->recv_sg_len) {
3484 int sg_len = scsi_cmd->recv_sg_len;
3485 struct iovec *sg;
3486 struct iovec *sg_orig = NULL;
3487 char *sgp;
3488 uint32_t total_len, disp;
3489 int i;
3490
3491 if (data.length != scsi_cmd->trans_len) {
3492
3493 /* Make a copy of the iovec */
3494
3495 sg_orig = sg = iscsi_malloc_atomic(sizeof(struct iovec)
3496 * sg_len);
3497 if (sg_orig == NULL) {
3498 iscsi_err(__FILE__, __LINE__,
3499 "iscsi_malloc_atomic() failed\n");
3500 return -1;
3501
3502 }
3503 (void) memcpy(sg, scsi_cmd->recv_data,
3504 sizeof(struct iovec) * sg_len);
3505
3506 /* Find offset in iovecs */
3507 total_len = 0;
3508 disp = data.offset;
3509 for (i = 0; i < sg_len; i++) {
3510 total_len += sg[i].iov_len;
3511 if (total_len > data.offset) {
3512 break;
3513 }
3514 disp -= sg[i].iov_len;
3515 }
3516 sg[i].iov_len -= disp;
3517 sgp = sg[i].iov_base;
3518 sgp += disp;
3519 sg[i].iov_base = sgp;
3520 sg_len -= i;
3521 sg = &sg[i];
3522
3523 /* Find last iovec needed for read */
3524
3525 total_len = 0;
3526 for (i = 0; i < sg_len; i++) {
3527 total_len += sg[i].iov_len;
3528 if (total_len >= data.length) {
3529 break;
3530 }
3531 }
3532 sg[i].iov_len -= (total_len - data.length);
3533 sg_len = i + 1;
3534 } else {
3535 sg = (struct iovec *)(void *)scsi_cmd->recv_data;
3536 }
3537 iscsi_trace(TRACE_ISCSI_DEBUG, "reading %d bytes into sg buffer (total offset %u)\n", data.length, data.offset);
3538 if ((rc = iscsi_sock_msg(sess->sock, 0, data.length, (uint8_t *)(void *) sg, sg_len)) != (int)data.length) {
3539 iscsi_err(__FILE__, __LINE__, "iscsi_sock_msg() failed: got %u, expected %u\n", rc, data.length);
3540 if (sg_orig)
3541 iscsi_free_atomic(sg_orig);
3542 return -1;
3543 }
3544 scsi_cmd->bytes_recv += data.length;
3545 if (sg_orig)
3546 iscsi_free_atomic(sg_orig);
3547 } else {
3548 if (data.length) {
3549 iscsi_trace(TRACE_ISCSI_DEBUG, "reading %d bytes into dest buffer (offset %u)\n", data.length, data.offset);
3550 if (iscsi_sock_msg(sess->sock, 0, data.length, scsi_cmd->recv_data + data.offset, 0) != (int)data.length) {
3551 iscsi_err(__FILE__, __LINE__, "iscsi_sock_msg() failed\n");
3552 return -1;
3553 }
3554 scsi_cmd->bytes_recv += data.length;
3555 }
3556 }
3557
3558
3559 /* Check for status */
3560
3561 if (data.S_bit) {
3562 iscsi_trace(TRACE_ISCSI_DEBUG,
3563 "received status with final PDU\n");
3564 #if 0
3565 RETURN_NOT_EQUAL("Final Bit", data.final, 1, NO_CLEANUP, -1);
3566 RETURN_NOT_EQUAL("StatSN", data.StatSN, sess->ExpStatSN++, NO_CLEANUP, -1);
3567 /* XXX - agc - increment in macro !!! */
3568 #else
3569 if (data.final != 1) {
3570 iscsi_err(__FILE__, __LINE__, "Final Bit");
3571 NO_CLEANUP;
3572 return -1;
3573 }
3574 if (data.StatSN != sess->ExpStatSN++) {
3575 iscsi_err(__FILE__, __LINE__, "StatSN");
3576 NO_CLEANUP;
3577 return -1;
3578 }
3579 #endif
3580 scsi_cmd->status = data.status = 0;
3581 cmd->status = 0;
3582 iscsi_trace(TRACE_ISCSI_DEBUG,
3583 "scsi op %#x done (tag %u, status %d)\n",
3584 scsi_cmd->cdb[0], scsi_cmd->tag, scsi_cmd->status);
3585 if ((*cmd->callback)(cmd) != 0) {
3586 iscsi_err(__FILE__, __LINE__,
3587 "callback() failed\n");
3588 return -1;
3589 }
3590 } else {
3591 if (hash_insert(&g_tag_hash, cmd, scsi_cmd->tag) != 0) {
3592 iscsi_err(__FILE__, __LINE__,
3593 "hash_insert() failed\n");
3594 return -1;
3595 }
3596 }
3597 iscsi_trace(TRACE_ISCSI_DEBUG, "read data processed\n");
3598 return 0;
3599 }
3600
3601 int
iscsi_initiator_info(char * ptr,int size,int len)3602 iscsi_initiator_info(char *ptr, int size, int len)
3603 {
3604 initiator_session_t *sess;
3605 int i;
3606
3607 ptr[0] = 0x0;
3608 len += snprintf(ptr, (size_t)(size - len),
3609 " %3s %30s %25s\n\n", "TID", "TargetName", "TargetAddress");
3610 for (i = 0; i < CONFIG_INITIATOR_NUM_TARGETS; i++) {
3611 len += snprintf(ptr + len, (size_t)(size - len),
3612 " %3i %30s %20s:%d (",
3613 i, g_target[i].TargetName,
3614 g_target[i].ip, g_target[i].port);
3615 if (g_target[i].has_session) {
3616 sess = g_target[i].sess;
3617 if (sess->state & INITIATOR_SESSION_STATE_INITIALIZING)
3618 len += snprintf(ptr + len,
3619 (size_t)(size - len), "%s",
3620 "initializing");
3621 if (sess->state & INITIATOR_SESSION_STATE_INITIALIZED)
3622 len += snprintf(ptr + len,
3623 (size_t)(size - len), "%s",
3624 "initialized");
3625 if (sess->state & INITIATOR_SESSION_STATE_CONNECTING)
3626 len += snprintf(ptr + len,
3627 (size_t)(size - len),
3628 "%s", "connecting");
3629 if (sess->state & INITIATOR_SESSION_STATE_CONNECTED)
3630 len += snprintf(ptr + len,
3631 (size_t)(size - len), "%s",
3632 "connected");
3633 if (sess->state & INITIATOR_SESSION_STATE_LOGGING_IN)
3634 len += snprintf(ptr + len,
3635 (size_t)(size - len), "%s",
3636 "logging in");
3637 if (sess->state &
3638 INITIATOR_SESSION_STATE_LOGGED_IN_NORMAL)
3639 len += snprintf(ptr + len,
3640 (size_t)(size - len), "%s",
3641 "Normal session");
3642 if (sess->state &
3643 INITIATOR_SESSION_STATE_LOGGED_IN_DISCOVERY)
3644 len += snprintf(ptr + len,
3645 (size_t)(size - len), "%s",
3646 "Discovery session");
3647 if (sess->state & INITIATOR_SESSION_STATE_LOGGING_OUT)
3648 len += snprintf(ptr + len,
3649 (size_t)(size - len), "%s",
3650 "logging out");
3651 if (sess->state & INITIATOR_SESSION_STATE_LOGGED_OUT)
3652 len += snprintf(ptr + len,
3653 (size_t)(size - len), "%s",
3654 "logged out");
3655 if (sess->state & INITIATOR_SESSION_STATE_DESTROYING)
3656 len += snprintf(ptr + len,
3657 (size_t)(size - len), "%s",
3658 "destroying");
3659 if (sess->tx_worker.state & ISCSI_WORKER_STATE_ERROR)
3660 len += snprintf(ptr + len,
3661 (size_t)(size - len), "%s",
3662 " **Tx Error** ");
3663 if (sess->rx_worker.state & ISCSI_WORKER_STATE_ERROR)
3664 len += snprintf(ptr + len,
3665 (size_t)(size - len), "%s",
3666 " **Rx Error** ");
3667 } else {
3668 len += snprintf(ptr + len, (size_t)(size - len), "%s",
3669 "No Session");
3670 }
3671 len += snprintf(ptr + len, (size_t)(size - len), ")\n");
3672 }
3673 return len;
3674 }
3675
3676 int
iscsi_initiator_discover(char * host,uint64_t target,int lun)3677 iscsi_initiator_discover(char *host, uint64_t target, int lun)
3678 {
3679 iscsi_nop_out_args_t discover_cmd;
3680 initiator_cmd_t cmd;
3681
3682 cmd.type = ISCSI_NOP_OUT;
3683 cmd.ptr = &discover_cmd;
3684 cmd.isid = target;
3685 (void) strlcpy(cmd.targetname, host, sizeof(cmd.targetname));
3686 (void) memset(&discover_cmd, 0x0, sizeof(iscsi_nop_out_args_t));
3687 discover_cmd.length = 1;
3688 discover_cmd.data = (const uint8_t *) "";
3689 discover_cmd.lun = lun;
3690 discover_cmd.tag = 0xffffffff;
3691 if (initiator_command(&cmd) != 0) {
3692 iscsi_err(__FILE__, __LINE__, "initiator_command() failed\n");
3693 return -1;
3694 }
3695 return 0;
3696 }
3697
3698 void
get_target_info(uint64_t target,initiator_target_t * ip)3699 get_target_info(uint64_t target, initiator_target_t *ip)
3700 {
3701 (void) memcpy(ip, &g_target[(int)target], sizeof(*ip));
3702 }
3703
3704 int
ii_initiator_init(const char * hostname,int port,int address_family,const char * user,char * lun,int auth_type,int mutual_auth,int digest_type)3705 ii_initiator_init(const char *hostname, int port, int address_family, const char *user, char *lun, int auth_type, int mutual_auth, int digest_type)
3706 {
3707 initiator_session_t *sess = NULL;
3708
3709 #define INIT_CLEANUP {if (sess != NULL) iscsi_free_atomic(sess);}
3710 #define INIT_ERROR {INIT_CLEANUP; return -1;}
3711
3712 USE_ARG(address_family);
3713 iscsi_trace(TRACE_ISCSI_DEBUG, "initializing initiator\n");
3714 if (get_target_config(hostname, port) != 0) {
3715 iscsi_err(__FILE__, __LINE__, "Error getting target configuration from config file\n");
3716 return -1;
3717 }
3718 (void) strlcpy(g_target[0].iqnwanted, lun, sizeof(g_target[0].iqnwanted));
3719 g_initiator_state = 0;
3720 if (iscsi_queue_init(&g_session_q, CONFIG_INITIATOR_MAX_SESSIONS) != 0) {
3721 iscsi_err(__FILE__, __LINE__, "iscsi_queue_init() failed\n");
3722 return -1;
3723 }
3724 if ((sess = iscsi_malloc_atomic(sizeof(initiator_session_t))) == NULL) {
3725 iscsi_err(__FILE__, __LINE__, "iscsi_malloc_atomic() failed\n");
3726 return -1;
3727 }
3728 if (iscsi_queue_insert(&g_session_q, sess) != 0) {
3729 iscsi_err(__FILE__, __LINE__, "iscsi_queue_init() failed\n");
3730 INIT_CLEANUP;
3731 return -1;
3732 }
3733 if (user)
3734 sess->sess_params.cred.user = strdup(user);
3735 else
3736 sess->sess_params.cred.user = NULL;
3737
3738 sess->sess_params.auth_type = auth_type;
3739 sess->sess_params.mutual_auth = mutual_auth;
3740 sess->sess_params.digest_wanted = digest_type;
3741 iscsi_trace(TRACE_ISCSI_DEBUG, "%d free sessions available\n",
3742 CONFIG_INITIATOR_MAX_SESSIONS);
3743
3744 g_tag = 0xabc123;
3745 if (hash_init(&g_tag_hash, CONFIG_INITIATOR_QUEUE_DEPTH) != 0) {
3746 iscsi_err(__FILE__, __LINE__, "hash_init() failed\n");
3747 INIT_CLEANUP;
3748 return -1;
3749 }
3750 iscsi_spin_init(&g_tag_spin);
3751 iscsi_trace(TRACE_ISCSI_DEBUG,
3752 "tag hash table initialized with queue depth %d\n",
3753 CONFIG_INITIATOR_QUEUE_DEPTH);
3754
3755 /*
3756 * Start enqueue worker. This thread accepts scsi commands
3757 * from initiator_enqueue() and queues them onto one of the tx
3758 * worker queues.
3759 */
3760 iscsi_trace(TRACE_ISCSI_DEBUG, "starting enqueue worker\n");
3761 if (iscsi_queue_init(&g_enqueue_q, CONFIG_INITIATOR_QUEUE_DEPTH) != 0) {
3762 iscsi_err(__FILE__, __LINE__, "iscsi_queue_init() failed\n");
3763 INIT_CLEANUP;
3764 return -1;
3765 }
3766 iscsi_trace(TRACE_ISCSI_DEBUG, "about to initialize mutex\n");
3767 ISCSI_MUTEX_INIT(&g_enqueue_worker.work_mutex, INIT_ERROR);
3768 ISCSI_COND_INIT(&g_enqueue_worker.work_cond, INIT_ERROR);
3769 ISCSI_MUTEX_INIT(&g_enqueue_worker.exit_mutex, INIT_ERROR);
3770 ISCSI_COND_INIT(&g_enqueue_worker.exit_cond, INIT_ERROR);
3771 ISCSI_LOCK(&g_enqueue_worker.exit_mutex, INIT_ERROR);
3772
3773 iscsi_trace(TRACE_ISCSI_DEBUG, "spawning thread for enqueue worker\n");
3774 if (iscsi_thread_create(&g_enqueue_worker.thread,
3775 (void *) &enqueue_worker_proc, &g_enqueue_worker) != 0) {
3776 iscsi_err(__FILE__, __LINE__,
3777 "iscsi_threads_create() failed\n");
3778 INIT_CLEANUP;
3779 return -1;
3780 }
3781 iscsi_trace(TRACE_ISCSI_DEBUG, "thread spawned, waiting for signal\n");
3782 ISCSI_WAIT(&g_enqueue_worker.exit_cond, &g_enqueue_worker.exit_mutex,
3783 INIT_ERROR);
3784 ISCSI_UNLOCK(&g_enqueue_worker.exit_mutex, INIT_ERROR);
3785 iscsi_trace(TRACE_ISCSI_DEBUG, "successfully started enqueue worker\n");
3786
3787 iscsi_trace(TRACE_ISCSI_DEBUG, "initiator initialization complete\n");
3788 return 0;
3789 }
3790
3791 int
iscsi_initiator_set_defaults(iscsi_initiator_t * ini)3792 iscsi_initiator_set_defaults(iscsi_initiator_t *ini)
3793 {
3794 char buf[32];
3795
3796 /* set defaults */
3797 (void) memset(ini, 0x0, sizeof(*ini));
3798 iscsi_initiator_setvar(ini, "address family", "unspec");
3799 iscsi_initiator_setvar(ini, "digest type", "none");
3800 iscsi_initiator_setvar(ini, "auth type", "none");
3801 iscsi_initiator_setvar(ini, "mutual auth", "none");
3802 iscsi_initiator_setvar(ini, "target hostname", "localhost");
3803 (void) snprintf(buf, sizeof(buf), "%d", ISCSI_PORT);
3804 iscsi_initiator_setvar(ini, "target port", buf);
3805 return 1;
3806 }
3807
3808 /* check there's enough space in the arrays */
3809 static void
size_arrays(iscsi_initiator_t * ini,unsigned needed)3810 size_arrays(iscsi_initiator_t *ini, unsigned needed)
3811 {
3812 if (ini->size == 0) {
3813 /* only get here first time around */
3814 ini->size = needed;
3815 ini->name = calloc(sizeof(char *), needed);
3816 ini->value = calloc(sizeof(char *), needed);
3817 } else if (ini->c == ini->size) {
3818 /* only uses 'needed' when filled array */
3819 ini->size += needed;
3820 ini->name = realloc(ini->name, sizeof(char *) * needed);
3821 ini->value = realloc(ini->value, sizeof(char *) * needed);
3822 }
3823 }
3824
3825 /* find the name in the array */
3826 static int
findvar(iscsi_initiator_t * ini,const char * name)3827 findvar(iscsi_initiator_t *ini, const char *name)
3828 {
3829 unsigned i;
3830
3831 for (i = 0 ; i < ini->c && strcmp(ini->name[i], name) != 0; i++) {
3832 }
3833 return (i == ini->c) ? -1 : (int)i;
3834 }
3835
3836 /* set a variable */
3837 int
iscsi_initiator_setvar(iscsi_initiator_t * ini,const char * name,const char * value)3838 iscsi_initiator_setvar(iscsi_initiator_t *ini, const char *name,
3839 const char *value)
3840 {
3841 int i;
3842
3843 if ((i = findvar(ini, name)) < 0) {
3844 /* add the element to the array */
3845 size_arrays(ini, ini->size + 15);
3846 ini->name[i = ini->c++] = strdup(name);
3847 } else {
3848 /* replace the element in the array */
3849 if (ini->value[i]) {
3850 (void) free(ini->value[i]);
3851 ini->value[i] = NULL;
3852 }
3853 }
3854 /* sanity checks for range of values would go here */
3855 ini->value[i] = strdup(value);
3856 return 1;
3857 }
3858
3859 /* get a variable's value (NULL if not set) */
3860 char *
iscsi_initiator_getvar(iscsi_initiator_t * ini,const char * name)3861 iscsi_initiator_getvar(iscsi_initiator_t *ini, const char *name)
3862 {
3863 int i;
3864
3865 return ((i = findvar(ini, name)) < 0) ? NULL : ini->value[i];
3866 }
3867