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