1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * 25 * iSCSI connection interfaces 26 */ 27 28 #include "iscsi.h" 29 #include "persistent.h" 30 #include <sys/bootprops.h> 31 32 extern ib_boot_prop_t *iscsiboot_prop; 33 34 /* interface connection interfaces */ 35 static iscsi_status_t iscsi_conn_state_free(iscsi_conn_t *icp, 36 iscsi_conn_event_t event); 37 static void iscsi_conn_state_in_login(iscsi_conn_t *icp, 38 iscsi_conn_event_t event); 39 static void iscsi_conn_state_logged_in(iscsi_conn_t *icp, 40 iscsi_conn_event_t event); 41 static void iscsi_conn_state_in_logout(iscsi_conn_t *icp, 42 iscsi_conn_event_t event); 43 static void iscsi_conn_state_failed(iscsi_conn_t *icp, 44 iscsi_conn_event_t event); 45 static void iscsi_conn_state_polling(iscsi_conn_t *icp, 46 iscsi_conn_event_t event); 47 static char *iscsi_conn_event_str(iscsi_conn_event_t event); 48 static void iscsi_conn_flush_active_cmds(iscsi_conn_t *icp); 49 50 static void iscsi_conn_logged_in(iscsi_sess_t *isp, 51 iscsi_conn_t *icp); 52 static void iscsi_conn_retry(iscsi_sess_t *isp, 53 iscsi_conn_t *icp); 54 55 #define SHUTDOWN_TIMEOUT 180 /* seconds */ 56 57 extern int modrootloaded; 58 /* 59 * +--------------------------------------------------------------------+ 60 * | External Connection Interfaces | 61 * +--------------------------------------------------------------------+ 62 */ 63 64 /* 65 * iscsi_conn_create - This creates an iscsi connection structure and 66 * associates it with a session structure. The session's sess_conn_list_rwlock 67 * should be held as a writer before calling this function. 68 */ 69 iscsi_status_t 70 iscsi_conn_create(struct sockaddr *addr, iscsi_sess_t *isp, iscsi_conn_t **icpp) 71 { 72 iscsi_conn_t *icp = NULL; 73 char th_name[ISCSI_TH_MAX_NAME_LEN]; 74 75 /* See if this connection already exists */ 76 for (icp = isp->sess_conn_list; icp; icp = icp->conn_next) { 77 78 /* 79 * Compare the ioctl information to see if 80 * its a match for this connection. (This 81 * is done by making sure the IPs are of 82 * the same size and then they are the 83 * same value. 84 */ 85 if (bcmp(&icp->conn_base_addr, addr, 86 SIZEOF_SOCKADDR(addr)) == 0) { 87 /* It's a match, record this connection */ 88 break; 89 } 90 } 91 92 /* If icp is found return it */ 93 if (icp != NULL) { 94 *icpp = icp; 95 return (ISCSI_STATUS_SUCCESS); 96 } 97 98 /* We are creating the connection, allocate, and setup */ 99 icp = (iscsi_conn_t *)kmem_zalloc(sizeof (iscsi_conn_t), KM_SLEEP); 100 101 /* 102 * Setup connection 103 */ 104 icp->conn_sig = ISCSI_SIG_CONN; 105 icp->conn_state = ISCSI_CONN_STATE_FREE; 106 mutex_init(&icp->conn_state_mutex, NULL, MUTEX_DRIVER, NULL); 107 cv_init(&icp->conn_state_change, NULL, CV_DRIVER, NULL); 108 icp->conn_state_destroy = B_FALSE; 109 icp->conn_sess = isp; 110 icp->conn_state_lbolt = ddi_get_lbolt(); 111 112 mutex_enter(&iscsi_oid_mutex); 113 icp->conn_oid = iscsi_oid++; 114 mutex_exit(&iscsi_oid_mutex); 115 116 /* Creation of the receive thread */ 117 if (snprintf(th_name, sizeof (th_name) - 1, ISCSI_CONN_RXTH_NAME_FORMAT, 118 icp->conn_sess->sess_hba->hba_oid, icp->conn_sess->sess_oid, 119 icp->conn_oid) >= sizeof (th_name)) { 120 cv_destroy(&icp->conn_state_change); 121 mutex_destroy(&icp->conn_state_mutex); 122 kmem_free(icp, sizeof (iscsi_conn_t)); 123 *icpp = NULL; 124 return (ISCSI_STATUS_INTERNAL_ERROR); 125 } 126 127 icp->conn_rx_thread = iscsi_thread_create(isp->sess_hba->hba_dip, 128 th_name, iscsi_rx_thread, icp); 129 130 /* Creation of the transfer thread */ 131 if (snprintf(th_name, sizeof (th_name) - 1, ISCSI_CONN_TXTH_NAME_FORMAT, 132 icp->conn_sess->sess_hba->hba_oid, icp->conn_sess->sess_oid, 133 icp->conn_oid) >= sizeof (th_name)) { 134 iscsi_thread_destroy(icp->conn_rx_thread); 135 cv_destroy(&icp->conn_state_change); 136 mutex_destroy(&icp->conn_state_mutex); 137 kmem_free(icp, sizeof (iscsi_conn_t)); 138 *icpp = NULL; 139 return (ISCSI_STATUS_INTERNAL_ERROR); 140 } 141 142 icp->conn_tx_thread = iscsi_thread_create(isp->sess_hba->hba_dip, 143 th_name, iscsi_tx_thread, icp); 144 145 /* setup connection queues */ 146 iscsi_init_queue(&icp->conn_queue_active); 147 148 bcopy(addr, &icp->conn_base_addr, sizeof (icp->conn_base_addr)); 149 150 /* Add new connection to the session connection list */ 151 icp->conn_cid = isp->sess_conn_next_cid++; 152 if (isp->sess_conn_list == NULL) { 153 isp->sess_conn_list = isp->sess_conn_list_last_ptr = icp; 154 } else { 155 isp->sess_conn_list_last_ptr->conn_next = icp; 156 isp->sess_conn_list_last_ptr = icp; 157 } 158 159 KSTAT_INC_SESS_CNTR_CONN(isp); 160 (void) iscsi_conn_kstat_init(icp); 161 162 *icpp = icp; 163 164 return (ISCSI_STATUS_SUCCESS); 165 } 166 167 168 /* 169 * iscsi_conn_offline - This attempts to take a connection from 170 * any state to ISCSI_CONN_STATE_FREE. 171 */ 172 iscsi_status_t 173 iscsi_conn_offline(iscsi_conn_t *icp) 174 { 175 clock_t delay; 176 177 ASSERT(icp != NULL); 178 179 /* 180 * We can only destroy a connection if its either in 181 * a state of FREE or LOGGED. The other states are 182 * transitionary and its unsafe to perform actions 183 * on the connection in those states. Set a flag 184 * on the connection to influence the transitions 185 * to quickly complete. Then wait for a state 186 * transition. 187 */ 188 delay = ddi_get_lbolt() + SEC_TO_TICK(SHUTDOWN_TIMEOUT); 189 mutex_enter(&icp->conn_state_mutex); 190 icp->conn_state_destroy = B_TRUE; 191 while ((icp->conn_state != ISCSI_CONN_STATE_FREE) && 192 (icp->conn_state != ISCSI_CONN_STATE_LOGGED_IN) && 193 (ddi_get_lbolt() < delay)) { 194 /* wait for transition */ 195 (void) cv_timedwait(&icp->conn_state_change, 196 &icp->conn_state_mutex, delay); 197 } 198 199 /* Final check whether we can destroy the connection */ 200 switch (icp->conn_state) { 201 case ISCSI_CONN_STATE_FREE: 202 /* Easy case - Connection is dead */ 203 break; 204 case ISCSI_CONN_STATE_LOGGED_IN: 205 /* Hard case - Force connection logout */ 206 (void) iscsi_conn_state_machine(icp, 207 ISCSI_CONN_EVENT_T9); 208 break; 209 case ISCSI_CONN_STATE_IN_LOGIN: 210 case ISCSI_CONN_STATE_IN_LOGOUT: 211 case ISCSI_CONN_STATE_FAILED: 212 case ISCSI_CONN_STATE_POLLING: 213 default: 214 /* All other cases fail the destroy */ 215 icp->conn_state_destroy = B_FALSE; 216 mutex_exit(&icp->conn_state_mutex); 217 return (ISCSI_STATUS_INTERNAL_ERROR); 218 } 219 mutex_exit(&icp->conn_state_mutex); 220 221 return (ISCSI_STATUS_SUCCESS); 222 } 223 224 /* 225 * iscsi_conn_destroy - This destroys an iscsi connection structure 226 * and de-associates it with the session. The connection should 227 * already been in the ISCSI_CONN_STATE_FREE when attempting this 228 * operation. 229 */ 230 iscsi_status_t 231 iscsi_conn_destroy(iscsi_conn_t *icp) 232 { 233 iscsi_sess_t *isp; 234 iscsi_conn_t *t_icp; 235 236 ASSERT(icp != NULL); 237 isp = icp->conn_sess; 238 ASSERT(isp != NULL); 239 240 if (icp->conn_state != ISCSI_CONN_STATE_FREE) { 241 return (ISCSI_STATUS_INTERNAL_ERROR); 242 } 243 244 /* Destroy receive thread */ 245 iscsi_thread_destroy(icp->conn_rx_thread); 246 247 /* Destroy transfer thread */ 248 iscsi_thread_destroy(icp->conn_tx_thread); 249 250 /* Terminate connection queues */ 251 iscsi_destroy_queue(&icp->conn_queue_active); 252 253 cv_destroy(&icp->conn_state_change); 254 mutex_destroy(&icp->conn_state_mutex); 255 256 /* 257 * Remove connection from sessions linked list. 258 */ 259 if (isp->sess_conn_list == icp) { 260 /* connection first item in list */ 261 isp->sess_conn_list = icp->conn_next; 262 /* 263 * check if this is also the last item in the list 264 */ 265 if (isp->sess_conn_list_last_ptr == icp) { 266 isp->sess_conn_list_last_ptr = NULL; 267 } 268 } else { 269 /* 270 * search session list for icp pointing 271 * to connection being removed. Then 272 * update that connections next pointer. 273 */ 274 t_icp = isp->sess_conn_list; 275 while (t_icp->conn_next != NULL) { 276 if (t_icp->conn_next == icp) { 277 break; 278 } 279 t_icp = t_icp->conn_next; 280 } 281 if (t_icp->conn_next == icp) { 282 t_icp->conn_next = icp->conn_next; 283 /* 284 * if this is the last connection in the list 285 * update the last_ptr to point to t_icp 286 */ 287 if (isp->sess_conn_list_last_ptr == icp) { 288 isp->sess_conn_list_last_ptr = t_icp; 289 } 290 } else { 291 /* couldn't find session */ 292 ASSERT(FALSE); 293 } 294 } 295 296 /* Free this Connections Data */ 297 iscsi_conn_kstat_term(icp); 298 kmem_free(icp, sizeof (iscsi_conn_t)); 299 300 return (ISCSI_STATUS_SUCCESS); 301 } 302 303 304 /* 305 * iscsi_conn_set_login_min_max - set min/max login window 306 * 307 * Used to set the min and max login window. Input values 308 * are in seconds. 309 */ 310 void 311 iscsi_conn_set_login_min_max(iscsi_conn_t *icp, int min, int max) 312 { 313 ASSERT(icp != NULL); 314 315 icp->conn_login_min = ddi_get_lbolt() + SEC_TO_TICK(min); 316 icp->conn_login_max = ddi_get_lbolt() + SEC_TO_TICK(max); 317 } 318 319 320 321 /* 322 * iscsi_conn_state_machine - This function is used to drive the 323 * state machine of the iscsi connection. It takes in a connection 324 * and the associated event effecting the connection. 325 * 326 * 7.1.3 Connection State Diagram for an Initiator 327 * Symbolic Names for States: 328 * S1: FREE - State on instantiation, or after successful 329 * connection closure. 330 * S2: IN_LOGIN - Waiting for login process to conclude, 331 * possibly involving several PDU exchanges. 332 * S3: LOGGED_IN - In Full Feature Phase, waiting for all internal, 333 * iSCSI, and transport events 334 * S4: IN_LOGOUT - Waiting for the Logout repsonse. 335 * S5: FAILED - The connection has failed. Attempting 336 * to reconnect. 337 * S6: POLLING - The connection reconnect attempts have 338 * failed. Continue to poll at a lower 339 * frequency. 340 * 341 * States S3, S4 constitute the Full Feature Phase 342 * of the connection. 343 * 344 * The state diagram is as follows: 345 * ------- 346 * +-------->/ S1 \<------------------------------+ 347 * | +->\ /<---+ /---\ | 348 * | / ---+--- |T7/30 T7| | | 349 * | + | | \->------ | 350 * | T8| |T1 / T5 / S6 \--->| 351 * | | | / +----------\ /T30 | 352 * | | V / / ------ | 353 * | | ------- / / ^ | 354 * | | / S2 \ / T5 |T7 | 355 * | | \ / +-------------- --+--- | 356 * | | ---+--- / / S5 \--->| 357 * | | | / T14/T15 \ /T30 | 358 * | | |T5 / +-------------> ------ | 359 * | | | / / | 360 * | | | / / T11 | 361 * | | | / / +----+ | 362 * | | V V / | | | 363 * | | ------+ ----+-- | | 364 * | +-----/ S3 \T9/11/ S4 \<+ | 365 * +----------\ /---->\ /----------------+ 366 * ------- ------- T15/T17 367 * 368 * The state transition table is as follows: 369 * 370 * +-----+---+---+------+------+---+ 371 * |S1 |S2 |S3 |S4 |S5 |S6 | 372 * ---+-----+---+---+------+------+---+ 373 * S1|T1 |T1 | - | - | - | | 374 * ---+-----+---+---+------+------+---+ 375 * S2|T7/30|- |T5 | - | - | | 376 * ---+-----+---+---+------+------+---+ 377 * S3|T8 |- | - |T9/11 |T14/15| | 378 * ---+-----+---+---+------+------+---+ 379 * S4| |- | - |T11 |T15/17| | 380 * ---+-----+---+---+------+------+---+ 381 * S5|T30 | |T5 | | |T7 | 382 * ---+-----+---+---+------+------+---+ 383 * S6|T30 | |T5 | | |T7 | 384 * ---+-----+---+---+------+------+---+ 385 * 386 * Events definitions: 387 * 388 * -T1: Transport connection request was made (e.g., TCP SYN sent). 389 * -T5: The final iSCSI Login response with a Status-Class of zero was 390 * received. 391 * -T7: One of the following events caused the transition: 392 * - Login timed out. 393 * - A transport disconnect indication was received. 394 * - A transport reset was received. 395 * - An internal event indicating a transport timeout was 396 * received. 397 * - An internal event of receiving a Logout repsonse (success) 398 * on another connection for a "close the session" Logout 399 * request was received. 400 * * In all these cases, the transport connection is closed. 401 * -T8: An internal event of receiving a Logout response (success) 402 * on another connection for a "close the session" Logout request 403 * was received, thus closing this connection requiring no further 404 * cleanup. 405 * -T9: An internal event that indicates the readiness to start the 406 * Logout process was received, thus prompting an iSCSI Logout to 407 * be sent by the initiator. 408 * -T11: Async PDU with AsyncEvent "Request Logout" was received. 409 * -T13: An iSCSI Logout response (success) was received, or an internal 410 * event of receiving a Logout response (success) on another 411 * connection was received. 412 * -T14: One or more of the following events case this transition: 413 * - Header Digest Error 414 * - Protocol Error 415 * -T15: One or more of the following events caused this transition: 416 * - Internal event that indicates a transport connection timeout 417 * was received thus prompting transport RESET or transport 418 * connection closure. 419 * - A transport RESET 420 * - A transport disconnect indication. 421 * - Async PDU with AsyncEvent "Drop connection" (for this CID) 422 * - Async PDU with AsyncEvent "Drop all connections" 423 * -T17: One or more of the following events caused this transition: 424 * - Logout response, (failure i.e., a non-zero status) was 425 * received, or Logout timed out. 426 * - Any of the events specified for T15. 427 * -T30: One of the following event caused the transition: 428 * - Thefinal iSCSI Login response was received with a non-zero 429 * Status-Class. 430 */ 431 iscsi_status_t 432 iscsi_conn_state_machine(iscsi_conn_t *icp, iscsi_conn_event_t event) 433 { 434 iscsi_status_t status = ISCSI_STATUS_SUCCESS; 435 436 ASSERT(icp != NULL); 437 ASSERT(mutex_owned(&icp->conn_state_mutex)); 438 439 DTRACE_PROBE3(event, iscsi_conn_t *, icp, 440 char *, iscsi_conn_state_str(icp->conn_state), 441 char *, iscsi_conn_event_str(event)); 442 443 icp->conn_prev_state = icp->conn_state; 444 icp->conn_state_lbolt = ddi_get_lbolt(); 445 446 switch (icp->conn_state) { 447 case ISCSI_CONN_STATE_FREE: 448 status = iscsi_conn_state_free(icp, event); 449 break; 450 case ISCSI_CONN_STATE_IN_LOGIN: 451 iscsi_conn_state_in_login(icp, event); 452 break; 453 case ISCSI_CONN_STATE_LOGGED_IN: 454 iscsi_conn_state_logged_in(icp, event); 455 break; 456 case ISCSI_CONN_STATE_IN_LOGOUT: 457 iscsi_conn_state_in_logout(icp, event); 458 break; 459 case ISCSI_CONN_STATE_FAILED: 460 iscsi_conn_state_failed(icp, event); 461 break; 462 case ISCSI_CONN_STATE_POLLING: 463 iscsi_conn_state_polling(icp, event); 464 break; 465 default: 466 ASSERT(FALSE); 467 status = ISCSI_STATUS_INTERNAL_ERROR; 468 } 469 470 cv_broadcast(&icp->conn_state_change); 471 return (status); 472 } 473 474 475 /* 476 * iscsi_conn_state_str - converts state enum to a string 477 */ 478 char * 479 iscsi_conn_state_str(iscsi_conn_state_t state) 480 { 481 switch (state) { 482 case ISCSI_CONN_STATE_FREE: 483 return ("free"); 484 case ISCSI_CONN_STATE_IN_LOGIN: 485 return ("in_login"); 486 case ISCSI_CONN_STATE_LOGGED_IN: 487 return ("logged_in"); 488 case ISCSI_CONN_STATE_IN_LOGOUT: 489 return ("in_logout"); 490 case ISCSI_CONN_STATE_FAILED: 491 return ("failed"); 492 case ISCSI_CONN_STATE_POLLING: 493 return ("polling"); 494 default: 495 return ("unknown"); 496 } 497 } 498 499 500 /* 501 * iscsi_conn_sync_params - used to update connection parameters 502 * 503 * Used to update connection parameters with current configured 504 * parameters in the persistent store. This should be called 505 * before starting to make a new iscsi connection in iscsi_login. 506 */ 507 iscsi_status_t 508 iscsi_conn_sync_params(iscsi_conn_t *icp) 509 { 510 iscsi_sess_t *isp; 511 iscsi_hba_t *ihp; 512 int param_id; 513 persistent_param_t pp; 514 iscsi_config_sess_t *ics; 515 int idx, size; 516 char *name; 517 518 ASSERT(icp != NULL); 519 ASSERT((icp->conn_state == ISCSI_CONN_STATE_IN_LOGIN) || 520 (icp->conn_state == ISCSI_CONN_STATE_FAILED) || 521 (icp->conn_state == ISCSI_CONN_STATE_POLLING)); 522 isp = icp->conn_sess; 523 ASSERT(isp != NULL); 524 ihp = isp->sess_hba; 525 ASSERT(ihp != NULL); 526 527 /* 528 * Check if someone is trying to destroy this 529 * connection. If so fail the sync request, 530 * as a method of fast fail. 531 */ 532 if (icp->conn_state_destroy == B_TRUE) { 533 return (ISCSI_STATUS_SHUTDOWN); 534 } 535 536 bzero(&pp, sizeof (pp)); 537 538 /* First get a copy of the HBA params */ 539 bcopy(&ihp->hba_params, &icp->conn_params, 540 sizeof (iscsi_login_params_t)); 541 542 /* 543 * Now we need to get the session configured 544 * values from the persistent store and apply 545 * them to our connection. 546 */ 547 (void) persistent_param_get((char *)isp->sess_name, &pp); 548 for (param_id = 0; param_id < ISCSI_NUM_LOGIN_PARAM; 549 param_id++) { 550 if (iscsiboot_prop && modrootloaded && 551 !iscsi_chk_bootlun_mpxio(ihp) && isp->sess_boot) { 552 /* 553 * iscsi boot with mpxio disabled 554 * while iscsi booting target's parameter overriden 555 * do no update target's parameters. 556 */ 557 if (pp.p_bitmap) { 558 cmn_err(CE_NOTE, "Adopting " 559 " default login parameters in" 560 " boot session as MPxIO is disabled"); 561 } 562 break; 563 } 564 if (pp.p_bitmap & (1 << param_id)) { 565 switch (param_id) { 566 /* 567 * Boolean parameters 568 */ 569 case ISCSI_LOGIN_PARAM_DATA_SEQUENCE_IN_ORDER: 570 icp->conn_params.data_pdu_in_order = 571 pp.p_params.data_pdu_in_order; 572 break; 573 case ISCSI_LOGIN_PARAM_IMMEDIATE_DATA: 574 icp->conn_params.immediate_data = 575 pp.p_params.immediate_data; 576 break; 577 case ISCSI_LOGIN_PARAM_INITIAL_R2T: 578 icp->conn_params.initial_r2t = 579 pp.p_params.initial_r2t; 580 break; 581 case ISCSI_LOGIN_PARAM_DATA_PDU_IN_ORDER: 582 icp->conn_params.data_pdu_in_order = 583 pp.p_params.data_pdu_in_order; 584 break; 585 /* 586 * Integer parameters 587 */ 588 case ISCSI_LOGIN_PARAM_HEADER_DIGEST: 589 icp->conn_params.header_digest = 590 pp.p_params.header_digest; 591 break; 592 case ISCSI_LOGIN_PARAM_DATA_DIGEST: 593 icp->conn_params.data_digest = 594 pp.p_params.data_digest; 595 break; 596 case ISCSI_LOGIN_PARAM_DEFAULT_TIME_2_RETAIN: 597 icp->conn_params.default_time_to_retain = 598 pp.p_params.default_time_to_retain; 599 break; 600 case ISCSI_LOGIN_PARAM_DEFAULT_TIME_2_WAIT: 601 icp->conn_params.default_time_to_wait = 602 pp.p_params.default_time_to_wait; 603 break; 604 case ISCSI_LOGIN_PARAM_MAX_RECV_DATA_SEGMENT_LENGTH: 605 icp->conn_params.max_recv_data_seg_len = 606 pp.p_params.max_recv_data_seg_len; 607 break; 608 case ISCSI_LOGIN_PARAM_FIRST_BURST_LENGTH: 609 icp->conn_params.first_burst_length = 610 pp.p_params.first_burst_length; 611 break; 612 case ISCSI_LOGIN_PARAM_MAX_BURST_LENGTH: 613 icp->conn_params.max_burst_length = 614 pp.p_params.max_burst_length; 615 break; 616 617 /* 618 * Integer parameters which currently are unsettable 619 */ 620 case ISCSI_LOGIN_PARAM_MAX_CONNECTIONS: 621 /* FALLTHRU */ 622 case ISCSI_LOGIN_PARAM_OUTSTANDING_R2T: 623 /* FALLTHRU */ 624 case ISCSI_LOGIN_PARAM_ERROR_RECOVERY_LEVEL: 625 /* FALLTHRU */ 626 default: 627 break; 628 } 629 } 630 } 631 632 /* Skip binding checks on discovery sessions */ 633 if (isp->sess_type == ISCSI_SESS_TYPE_DISCOVERY) { 634 return (ISCSI_STATUS_SUCCESS); 635 } 636 637 /* 638 * Now we need to get the current optional connection 639 * binding information. 640 */ 641 /* setup initial buffer for configured session information */ 642 size = sizeof (*ics); 643 ics = kmem_zalloc(size, KM_SLEEP); 644 ics->ics_in = 1; 645 646 /* get configured sessions information */ 647 name = (char *)isp->sess_name; 648 if (persistent_get_config_session(name, ics) == B_FALSE) { 649 /* 650 * If we were unable to get target level information 651 * then check the initiator level information. 652 */ 653 name = (char *)isp->sess_hba->hba_name; 654 if (persistent_get_config_session(name, ics) == B_FALSE) { 655 /* 656 * No hba information is found. So assume default 657 * one session unbound behavior. 658 */ 659 ics->ics_out = 1; 660 ics->ics_bound = B_FALSE; 661 } 662 } 663 664 if (iscsiboot_prop && (ics->ics_out > 1) && isp->sess_boot && 665 !iscsi_chk_bootlun_mpxio(ihp)) { 666 /* 667 * iscsi booting session with mpxio disabled, 668 * no need set multiple sessions for booting session 669 */ 670 ics->ics_out = 1; 671 ics->ics_bound = B_FALSE; 672 cmn_err(CE_NOTE, "MPxIO is disabled," 673 " no need to configure multiple boot sessions"); 674 } 675 676 /* 677 * Check to make sure this session is still a configured 678 * session. The user might have decreased the session 679 * count. (NOTE: byte 5 of the sess_isid is the session 680 * count (via MS/T). This counter starts at 0.) 681 */ 682 683 684 idx = isp->sess_isid[5]; 685 686 if (iscsiboot_prop && (idx == ISCSI_MAX_CONFIG_SESSIONS)) { 687 /* 688 * This is temporary session for boot session propose 689 * no need to bound IP for this session 690 */ 691 icp->conn_bound = B_FALSE; 692 kmem_free(ics, sizeof (iscsi_config_sess_t)); 693 return (ISCSI_STATUS_SUCCESS); 694 } 695 696 if (ics->ics_out <= idx) { 697 /* 698 * No longer a configured session. Return a 699 * failure so we don't attempt to relogin. 700 */ 701 return (ISCSI_STATUS_SHUTDOWN); 702 } 703 704 /* 705 * If sessions are unbound set this information on 706 * the connection and return success. 707 */ 708 if (ics->ics_bound == B_FALSE) { 709 icp->conn_bound = B_FALSE; 710 kmem_free(ics, sizeof (iscsi_config_sess_t)); 711 return (ISCSI_STATUS_SUCCESS); 712 } 713 714 /* 715 * Since the sessions are bound we need to find the matching 716 * binding information for the session's isid. If this 717 * session's isid is > 0 then we need to get more configured 718 * session information to find the binding info. 719 */ 720 if (idx > 0) { 721 int ics_out; 722 723 ics_out = ics->ics_out; 724 /* record new size and free last buffer */ 725 size = ISCSI_SESSION_CONFIG_SIZE(ics_out); 726 kmem_free(ics, sizeof (*ics)); 727 728 /* allocate new buffer */ 729 ics = kmem_zalloc(size, KM_SLEEP); 730 ics->ics_in = ics_out; 731 732 /* get configured sessions information */ 733 if (persistent_get_config_session(name, ics) != B_TRUE) { 734 cmn_err(CE_NOTE, "iscsi session(%d) - " 735 "unable to get configured session information\n", 736 isp->sess_oid); 737 kmem_free(ics, size); 738 return (ISCSI_STATUS_SHUTDOWN); 739 } 740 } 741 742 /* Copy correct binding information to the connection */ 743 icp->conn_bound = B_TRUE; 744 if (ics->ics_bindings[idx].i_insize == sizeof (struct in_addr)) { 745 bcopy(&ics->ics_bindings[idx].i_addr.in4, 746 &icp->conn_bound_addr.sin4.sin_addr.s_addr, 747 sizeof (struct in_addr)); 748 } else { 749 bcopy(&ics->ics_bindings[idx].i_addr.in6, 750 &icp->conn_bound_addr.sin6.sin6_addr.s6_addr, 751 sizeof (struct in6_addr)); 752 } 753 754 kmem_free(ics, size); 755 756 return (ISCSI_STATUS_SUCCESS); 757 } 758 759 /* 760 * +--------------------------------------------------------------------+ 761 * | Internal Connection Interfaces | 762 * +--------------------------------------------------------------------+ 763 */ 764 765 766 /* 767 * iscsi_conn_state_free - 768 * 769 * S1: FREE - State on instantiation, or after successful 770 * connection closure. 771 */ 772 static iscsi_status_t 773 iscsi_conn_state_free(iscsi_conn_t *icp, iscsi_conn_event_t event) 774 { 775 iscsi_sess_t *isp; 776 iscsi_hba_t *ihp; 777 iscsi_task_t *itp; 778 iscsi_status_t status = ISCSI_STATUS_SUCCESS; 779 780 ASSERT(icp != NULL); 781 isp = icp->conn_sess; 782 ASSERT(isp != NULL); 783 ihp = isp->sess_hba; 784 ASSERT(ihp != NULL); 785 ASSERT(icp->conn_state == ISCSI_CONN_STATE_FREE); 786 787 /* switch on event change */ 788 switch (event) { 789 /* -T1: Transport connection request was request */ 790 case ISCSI_CONN_EVENT_T1: 791 icp->conn_state = ISCSI_CONN_STATE_IN_LOGIN; 792 793 /* 794 * Release the connection state mutex cross the 795 * the dispatch of the login task. The login task 796 * will reacquire the connection state mutex when 797 * it pushes the connection successful or failed. 798 */ 799 mutex_exit(&icp->conn_state_mutex); 800 801 /* start login */ 802 itp = kmem_zalloc(sizeof (iscsi_task_t), KM_SLEEP); 803 itp->t_arg = icp; 804 itp->t_blocking = B_TRUE; 805 806 /* 807 * Sync base connection information before login 808 * A login redirection might have shifted the 809 * current information from the base. 810 */ 811 bcopy(&icp->conn_base_addr, &icp->conn_curr_addr, 812 sizeof (icp->conn_curr_addr)); 813 814 status = iscsi_login_start(itp); 815 kmem_free(itp, sizeof (iscsi_task_t)); 816 817 mutex_enter(&icp->conn_state_mutex); 818 break; 819 820 /* All other events are invalid for this state */ 821 default: 822 ASSERT(FALSE); 823 status = ISCSI_STATUS_INTERNAL_ERROR; 824 } 825 return (status); 826 } 827 828 /* 829 * iscsi_conn_state_in_login - During this state we are trying to 830 * connect the TCP connection and make a successful login to the 831 * target. To complete this we have a task queue item that is 832 * trying this processing at this point in time. When the task 833 * queue completed its processing it will issue either a T5/7 834 * event. 835 */ 836 static void 837 iscsi_conn_state_in_login(iscsi_conn_t *icp, iscsi_conn_event_t event) 838 { 839 iscsi_sess_t *isp; 840 841 ASSERT(icp != NULL); 842 isp = icp->conn_sess; 843 ASSERT(isp != NULL); 844 ASSERT(icp->conn_state == ISCSI_CONN_STATE_IN_LOGIN); 845 846 /* switch on event change */ 847 switch (event) { 848 /* 849 * -T5: The final iSCSI Login response with a Status-Class of zero 850 * was received. 851 */ 852 case ISCSI_CONN_EVENT_T5: 853 iscsi_conn_logged_in(isp, icp); 854 break; 855 856 /* 857 * -T30: One of the following event caused the transition: 858 * - Thefinal iSCSI Login response was received with a non-zero 859 * Status-Class. 860 */ 861 case ISCSI_CONN_EVENT_T30: 862 /* FALLTHRU */ 863 864 /* 865 * -T7: One of the following events caused the transition: 866 * - Login timed out. 867 * - A transport disconnect indication was received. 868 * - A transport reset was received. 869 * - An internal event indicating a transport timeout was 870 * received. 871 * - An internal event of receiving a Logout repsonse (success) 872 * on another connection for a "close the session" Logout 873 * request was received. 874 * * In all these cases, the transport connection is closed. 875 */ 876 case ISCSI_CONN_EVENT_T7: 877 icp->conn_state = ISCSI_CONN_STATE_FREE; 878 break; 879 880 /* All other events are invalid for this state */ 881 default: 882 ASSERT(FALSE); 883 } 884 } 885 886 887 /* 888 * iscsi_conn_state_logged_in - 889 * 890 */ 891 static void 892 iscsi_conn_state_logged_in(iscsi_conn_t *icp, iscsi_conn_event_t event) 893 { 894 iscsi_sess_t *isp; 895 iscsi_hba_t *ihp; 896 897 ASSERT(icp != NULL); 898 ASSERT(icp->conn_state == ISCSI_CONN_STATE_LOGGED_IN); 899 isp = icp->conn_sess; 900 ASSERT(isp != NULL); 901 ihp = isp->sess_hba; 902 ASSERT(ihp != NULL); 903 904 /* switch on event change */ 905 switch (event) { 906 /* 907 * -T8: An internal event of receiving a Logout response (success) 908 * on another connection for a "close the session" Logout request 909 * was received, thus closing this connection requiring no further 910 * cleanup. 911 */ 912 case ISCSI_CONN_EVENT_T8: 913 icp->conn_state = ISCSI_CONN_STATE_FREE; 914 915 /* stop tx thread */ 916 (void) iscsi_thread_stop(icp->conn_tx_thread); 917 918 /* Disconnect connection */ 919 iscsi_net->close(icp->conn_socket); 920 921 /* Notify session that a connection logged out */ 922 mutex_enter(&isp->sess_state_mutex); 923 iscsi_sess_state_machine(icp->conn_sess, ISCSI_SESS_EVENT_N3); 924 mutex_exit(&isp->sess_state_mutex); 925 break; 926 927 /* 928 * -T9: An internal event that indicates the readiness to start the 929 * Logout process was received, thus prompting an iSCSI Logout 930 * to be sent by the initiator. 931 */ 932 case ISCSI_CONN_EVENT_T9: 933 /* FALLTHRU */ 934 935 /* 936 * -T11: Aync PDU with AsyncEvent "Request Logout" was recevied 937 */ 938 case ISCSI_CONN_EVENT_T11: 939 icp->conn_state = ISCSI_CONN_STATE_IN_LOGOUT; 940 941 (void) iscsi_handle_logout(icp); 942 break; 943 944 /* 945 * -T14: One or more of the following events case this transition: 946 * - Header Digest Error 947 * - Protocol Error 948 */ 949 case ISCSI_CONN_EVENT_T14: 950 icp->conn_state = ISCSI_CONN_STATE_FAILED; 951 952 /* stop tx thread */ 953 (void) iscsi_thread_stop(icp->conn_tx_thread); 954 955 /* 956 * Error Recovery Level 0 states we should drop 957 * the connection here. Then we will fall through 958 * and treat this event like a T15. 959 */ 960 iscsi_net->close(icp->conn_socket); 961 962 /* FALLTHRU */ 963 964 /* 965 * -T15: One or more of the following events caused this transition 966 * - Internal event that indicates a transport connection timeout 967 * was received thus prompting transport RESET or transport 968 * connection closure. 969 * - A transport RESET 970 * - A transport disconnect indication. 971 * - Async PDU with AsyncEvent "Drop connection" (for this CID) 972 * - Async PDU with AsyncEvent "Drop all connections" 973 */ 974 case ISCSI_CONN_EVENT_T15: 975 icp->conn_state = ISCSI_CONN_STATE_FAILED; 976 977 /* stop tx thread, no-op if already done for T14 */ 978 (void) iscsi_thread_stop(icp->conn_tx_thread); 979 980 iscsi_conn_flush_active_cmds(icp); 981 982 mutex_enter(&isp->sess_state_mutex); 983 iscsi_sess_state_machine(isp, ISCSI_SESS_EVENT_N5); 984 mutex_exit(&isp->sess_state_mutex); 985 986 /* 987 * If session type is NORMAL, create a new login task 988 * to get this connection reestablished. 989 */ 990 if (isp->sess_type == ISCSI_SESS_TYPE_NORMAL) { 991 iscsi_conn_retry(isp, icp); 992 } else { 993 icp->conn_state = ISCSI_CONN_STATE_FREE; 994 mutex_enter(&isp->sess_state_mutex); 995 iscsi_sess_state_machine(isp, ISCSI_SESS_EVENT_N6); 996 mutex_exit(&isp->sess_state_mutex); 997 } 998 break; 999 1000 /* All other events are invalid for this state */ 1001 default: 1002 ASSERT(FALSE); 1003 } 1004 } 1005 1006 1007 /* 1008 * iscsi_conn_state_in_logout - 1009 * 1010 */ 1011 static void 1012 iscsi_conn_state_in_logout(iscsi_conn_t *icp, iscsi_conn_event_t event) 1013 { 1014 iscsi_sess_t *isp = NULL; 1015 1016 ASSERT(icp != NULL); 1017 ASSERT(icp->conn_state == ISCSI_CONN_STATE_IN_LOGOUT); 1018 isp = icp->conn_sess; 1019 ASSERT(isp != NULL); 1020 1021 /* switch on event change */ 1022 switch (event) { 1023 /* 1024 * -T11: Async PDU with AsyncEvent "Request Logout" was received again 1025 */ 1026 case ISCSI_CONN_EVENT_T11: 1027 icp->conn_state = ISCSI_CONN_STATE_IN_LOGOUT; 1028 1029 /* Already in LOGOUT ignore the request */ 1030 break; 1031 1032 /* 1033 * -T17: One or more of the following events caused this transition: 1034 * - Logout response, (failure i.e., a non-zero status) was 1035 * received, or logout timed out. 1036 * - Any of the events specified for T15 1037 * 1038 * -T14: One or more of the following events case this transition: 1039 * - Header Digest Error 1040 * - Protocol Error 1041 * 1042 * -T15: One or more of the following events caused this transition 1043 * - Internal event that indicates a transport connection timeout 1044 * was received thus prompting transport RESET or transport 1045 * connection closure. 1046 * - A transport RESET 1047 * - A transport disconnect indication. 1048 * - Async PDU with AsyncEvent "Drop connection" (for this CID) 1049 * - Async PDU with AsyncEvent "Drop all connections" 1050 */ 1051 case ISCSI_CONN_EVENT_T17: 1052 case ISCSI_CONN_EVENT_T14: 1053 case ISCSI_CONN_EVENT_T15: 1054 icp->conn_state = ISCSI_CONN_STATE_FREE; 1055 1056 /* stop tx thread */ 1057 (void) iscsi_thread_stop(icp->conn_tx_thread); 1058 1059 /* Disconnect Connection */ 1060 iscsi_net->close(icp->conn_socket); 1061 1062 iscsi_conn_flush_active_cmds(icp); 1063 1064 /* Notify session of a failed logout */ 1065 mutex_enter(&isp->sess_state_mutex); 1066 iscsi_sess_state_machine(icp->conn_sess, ISCSI_SESS_EVENT_N3); 1067 mutex_exit(&isp->sess_state_mutex); 1068 break; 1069 1070 /* All other events are invalid for this state */ 1071 default: 1072 ASSERT(FALSE); 1073 } 1074 } 1075 1076 1077 /* 1078 * iscsi_conn_state_failed - 1079 * 1080 */ 1081 static void 1082 iscsi_conn_state_failed(iscsi_conn_t *icp, iscsi_conn_event_t event) 1083 { 1084 iscsi_sess_t *isp; 1085 1086 ASSERT(icp != NULL); 1087 ASSERT(icp->conn_state == ISCSI_CONN_STATE_FAILED); 1088 isp = icp->conn_sess; 1089 ASSERT(isp != NULL); 1090 1091 /* switch on event change */ 1092 switch (event) { 1093 1094 /* 1095 * -T5: The final iSCSI Login response with a Status-Class of zero 1096 * was received. 1097 */ 1098 case ISCSI_CONN_EVENT_T5: 1099 iscsi_conn_logged_in(isp, icp); 1100 break; 1101 1102 /* 1103 * -T30: One of the following event caused the transition: 1104 * - Thefinal iSCSI Login response was received with a non-zero 1105 * Status-Class. 1106 */ 1107 case ISCSI_CONN_EVENT_T30: 1108 icp->conn_state = ISCSI_CONN_STATE_FREE; 1109 1110 mutex_enter(&isp->sess_state_mutex); 1111 iscsi_sess_state_machine(isp, ISCSI_SESS_EVENT_N6); 1112 mutex_exit(&isp->sess_state_mutex); 1113 1114 break; 1115 1116 /* 1117 * -T7: One of the following events caused the transition: 1118 * - Login timed out. 1119 * - A transport disconnect indication was received. 1120 * - A transport reset was received. 1121 * - An internal event indicating a transport timeout was 1122 * received. 1123 * - An internal event of receiving a Logout repsonse (success) 1124 * on another connection for a "close the session" Logout 1125 * request was received. 1126 * * In all these cases, the transport connection is closed. 1127 */ 1128 case ISCSI_CONN_EVENT_T7: 1129 icp->conn_state = ISCSI_CONN_STATE_POLLING; 1130 1131 mutex_enter(&isp->sess_state_mutex); 1132 iscsi_sess_state_machine(isp, ISCSI_SESS_EVENT_N6); 1133 mutex_exit(&isp->sess_state_mutex); 1134 1135 iscsi_conn_retry(isp, icp); 1136 break; 1137 1138 /* There are no valid transition out of this state. */ 1139 default: 1140 ASSERT(FALSE); 1141 } 1142 } 1143 1144 /* 1145 * iscsi_conn_state_polling - 1146 * 1147 * S6: POLLING - State on instantiation, or after successful 1148 * connection closure. 1149 */ 1150 static void 1151 iscsi_conn_state_polling(iscsi_conn_t *icp, iscsi_conn_event_t event) 1152 { 1153 iscsi_sess_t *isp = NULL; 1154 1155 ASSERT(icp != NULL); 1156 ASSERT(icp->conn_state == ISCSI_CONN_STATE_POLLING); 1157 isp = icp->conn_sess; 1158 ASSERT(isp != NULL); 1159 1160 /* switch on event change */ 1161 switch (event) { 1162 /* 1163 * -T5: The final iSCSI Login response with a Status-Class of zero 1164 * was received. 1165 */ 1166 case ISCSI_CONN_EVENT_T5: 1167 iscsi_conn_logged_in(isp, icp); 1168 break; 1169 1170 /* 1171 * -T30: One of the following event caused the transition: 1172 * - Thefinal iSCSI Login response was received with a non-zero 1173 * Status-Class. 1174 */ 1175 case ISCSI_CONN_EVENT_T30: 1176 icp->conn_state = ISCSI_CONN_STATE_FREE; 1177 1178 mutex_enter(&isp->sess_state_mutex); 1179 iscsi_sess_state_machine(isp, ISCSI_SESS_EVENT_N6); 1180 mutex_exit(&isp->sess_state_mutex); 1181 1182 break; 1183 1184 /* 1185 * -T7: One of the following events caused the transition: 1186 * - Login timed out. 1187 * - A transport disconnect indication was received. 1188 * - A transport reset was received. 1189 * - An internal event indicating a transport timeout was 1190 * received. 1191 * - An internal event of receiving a Logout repsonse (success) 1192 * on another connection for a "close the session" Logout 1193 * request was received. 1194 * * In all these cases, the transport connection is closed. 1195 */ 1196 case ISCSI_CONN_EVENT_T7: 1197 /* 1198 * If session type is NORMAL, create a new login task 1199 * to get this connection reestablished. 1200 */ 1201 if (isp->sess_type == ISCSI_SESS_TYPE_NORMAL) { 1202 iscsi_conn_retry(isp, icp); 1203 } else { 1204 icp->conn_state = ISCSI_CONN_STATE_FREE; 1205 } 1206 break; 1207 1208 /* All other events are invalid for this state */ 1209 default: 1210 ASSERT(FALSE); 1211 } 1212 } 1213 1214 /* 1215 * iscsi_conn_event_str - converts event enum to a string 1216 */ 1217 static char * 1218 iscsi_conn_event_str(iscsi_conn_event_t event) 1219 { 1220 switch (event) { 1221 case ISCSI_CONN_EVENT_T1: 1222 return ("T1"); 1223 case ISCSI_CONN_EVENT_T5: 1224 return ("T5"); 1225 case ISCSI_CONN_EVENT_T7: 1226 return ("T7"); 1227 case ISCSI_CONN_EVENT_T8: 1228 return ("T8"); 1229 case ISCSI_CONN_EVENT_T9: 1230 return ("T9"); 1231 case ISCSI_CONN_EVENT_T11: 1232 return ("T11"); 1233 case ISCSI_CONN_EVENT_T14: 1234 return ("T14"); 1235 case ISCSI_CONN_EVENT_T15: 1236 return ("T15"); 1237 case ISCSI_CONN_EVENT_T17: 1238 return ("T17"); 1239 case ISCSI_CONN_EVENT_T30: 1240 return ("T30"); 1241 1242 default: 1243 return ("unknown"); 1244 } 1245 } 1246 1247 /* 1248 * iscsi_conn_flush_active_cmds - flush all active icmdps 1249 * for a connection. 1250 */ 1251 static void 1252 iscsi_conn_flush_active_cmds(iscsi_conn_t *icp) 1253 { 1254 iscsi_cmd_t *icmdp; 1255 iscsi_sess_t *isp; 1256 boolean_t lock_held = B_FALSE; 1257 1258 ASSERT(icp != NULL); 1259 isp = icp->conn_sess; 1260 ASSERT(isp != NULL); 1261 1262 if (mutex_owned(&icp->conn_queue_active.mutex)) { 1263 lock_held = B_TRUE; 1264 } else { 1265 mutex_enter(&icp->conn_queue_active.mutex); 1266 } 1267 1268 /* Flush active queue */ 1269 icmdp = icp->conn_queue_active.head; 1270 while (icmdp != NULL) { 1271 iscsi_cmd_state_machine(icmdp, 1272 ISCSI_CMD_EVENT_E7, isp); 1273 icmdp = icp->conn_queue_active.head; 1274 } 1275 1276 if (lock_held == B_FALSE) { 1277 mutex_exit(&icp->conn_queue_active.mutex); 1278 } 1279 } 1280 1281 1282 /* 1283 * iscsi_conn_logged_in - connection has successfully logged in 1284 */ 1285 static void 1286 iscsi_conn_logged_in(iscsi_sess_t *isp, iscsi_conn_t *icp) 1287 { 1288 ASSERT(isp != NULL); 1289 ASSERT(icp != NULL); 1290 1291 icp->conn_state = ISCSI_CONN_STATE_LOGGED_IN; 1292 /* 1293 * We need to drop the connection state lock 1294 * before updating the session state. On update 1295 * of the session state it will enumerate the 1296 * target. If we hold the lock during enumeration 1297 * will block the watchdog thread from timing 1298 * a scsi_pkt, if required. This will lead to 1299 * a possible hang condition. 1300 * 1301 * Also the lock is no longer needed once the 1302 * connection state was updated. 1303 */ 1304 mutex_exit(&icp->conn_state_mutex); 1305 1306 /* startup threads */ 1307 (void) iscsi_thread_start(icp->conn_rx_thread); 1308 (void) iscsi_thread_start(icp->conn_tx_thread); 1309 1310 /* Notify the session that a connection is logged in */ 1311 mutex_enter(&isp->sess_state_mutex); 1312 iscsi_sess_state_machine(isp, ISCSI_SESS_EVENT_N1); 1313 mutex_exit(&isp->sess_state_mutex); 1314 1315 mutex_enter(&icp->conn_state_mutex); 1316 } 1317 1318 /* 1319 * iscsi_conn_retry - retry connect/login 1320 */ 1321 static void 1322 iscsi_conn_retry(iscsi_sess_t *isp, iscsi_conn_t *icp) 1323 { 1324 iscsi_task_t *itp; 1325 1326 ASSERT(isp != NULL); 1327 ASSERT(icp != NULL); 1328 1329 /* set login min/max time values */ 1330 iscsi_conn_set_login_min_max(icp, 1331 ISCSI_CONN_DEFAULT_LOGIN_MIN, 1332 ISCSI_CONN_DEFAULT_LOGIN_MAX); 1333 1334 /* 1335 * Sync base connection information before login. 1336 * A login redirection might have shifted the 1337 * current information from the base. 1338 */ 1339 bcopy(&icp->conn_base_addr, &icp->conn_curr_addr, 1340 sizeof (icp->conn_curr_addr)); 1341 1342 /* schedule login task */ 1343 itp = kmem_zalloc(sizeof (iscsi_task_t), KM_SLEEP); 1344 itp->t_arg = icp; 1345 itp->t_blocking = B_FALSE; 1346 if (ddi_taskq_dispatch(isp->sess_taskq, 1347 (void(*)())iscsi_login_start, itp, DDI_SLEEP) != 1348 DDI_SUCCESS) { 1349 kmem_free(itp, sizeof (iscsi_task_t)); 1350 cmn_err(CE_WARN, 1351 "iscsi connection(%u) failure - " 1352 "unable to schedule login task", 1353 icp->conn_oid); 1354 1355 icp->conn_state = ISCSI_CONN_STATE_FREE; 1356 mutex_enter(&isp->sess_state_mutex); 1357 iscsi_sess_state_machine(isp, 1358 ISCSI_SESS_EVENT_N6); 1359 mutex_exit(&isp->sess_state_mutex); 1360 } 1361 } 1362