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