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