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