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 (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. 23 * 24 * iSCSI connection interfaces 25 */ 26 27 #define ISCSI_ICS_NAMES 28 #include "iscsi.h" 29 #include "persistent.h" 30 #include <sys/bootprops.h> 31 32 extern ib_boot_prop_t *iscsiboot_prop; 33 34 static void iscsi_client_notify_task(void *cn_task_void); 35 36 static void iscsi_conn_flush_active_cmds(iscsi_conn_t *icp); 37 38 #define SHUTDOWN_TIMEOUT 180 /* seconds */ 39 40 extern int modrootloaded; 41 42 boolean_t iscsi_conn_logging = B_FALSE; 43 44 #define ISCSI_LOGIN_TPGT_NEGO_ERROR(icp) \ 45 (((icp)->conn_login_state == LOGIN_ERROR) && \ 46 ((icp)->conn_login_status == ISCSI_STATUS_LOGIN_TPGT_NEGO_FAIL)) 47 48 /* 49 * +--------------------------------------------------------------------+ 50 * | External Connection Interfaces | 51 * +--------------------------------------------------------------------+ 52 */ 53 54 /* 55 * iscsi_conn_create - This creates an iscsi connection structure and 56 * associates it with a session structure. The session's sess_conn_list_rwlock 57 * should be held as a writer before calling this function. 58 */ 59 iscsi_status_t 60 iscsi_conn_create(struct sockaddr *addr, iscsi_sess_t *isp, iscsi_conn_t **icpp) 61 { 62 iscsi_conn_t *icp = NULL; 63 char th_name[ISCSI_TH_MAX_NAME_LEN]; 64 65 /* See if this connection already exists */ 66 for (icp = isp->sess_conn_list; icp; icp = icp->conn_next) { 67 68 /* 69 * Compare the ioctl information to see if 70 * its a match for this connection. (This 71 * is done by making sure the IPs are of 72 * the same size and then they are the 73 * same value. 74 */ 75 if (bcmp(&icp->conn_base_addr, addr, 76 SIZEOF_SOCKADDR(addr)) == 0) { 77 /* It's a match, record this connection */ 78 break; 79 } 80 } 81 82 /* If icp is found return it */ 83 if (icp != NULL) { 84 *icpp = icp; 85 return (ISCSI_STATUS_SUCCESS); 86 } 87 88 /* We are creating the connection, allocate, and setup */ 89 icp = (iscsi_conn_t *)kmem_zalloc(sizeof (iscsi_conn_t), KM_SLEEP); 90 91 /* 92 * Setup connection 93 */ 94 icp->conn_sig = ISCSI_SIG_CONN; 95 icp->conn_state = ISCSI_CONN_STATE_FREE; 96 mutex_init(&icp->conn_state_mutex, NULL, MUTEX_DRIVER, NULL); 97 cv_init(&icp->conn_state_change, NULL, CV_DRIVER, NULL); 98 mutex_init(&icp->conn_login_mutex, NULL, MUTEX_DRIVER, NULL); 99 cv_init(&icp->conn_login_cv, NULL, CV_DRIVER, NULL); 100 icp->conn_state_destroy = B_FALSE; 101 idm_sm_audit_init(&icp->conn_state_audit); 102 icp->conn_sess = isp; 103 104 mutex_enter(&iscsi_oid_mutex); 105 icp->conn_oid = iscsi_oid++; 106 mutex_exit(&iscsi_oid_mutex); 107 108 /* 109 * IDM CN taskq 110 */ 111 112 if (snprintf(th_name, sizeof (th_name) - 1, 113 ISCSI_CONN_CN_TASKQ_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_cn_taskq = 124 ddi_taskq_create(icp->conn_sess->sess_hba->hba_dip, th_name, 1, 125 TASKQ_DEFAULTPRI, 0); 126 if (icp->conn_cn_taskq == NULL) { 127 cv_destroy(&icp->conn_state_change); 128 mutex_destroy(&icp->conn_state_mutex); 129 kmem_free(icp, sizeof (iscsi_conn_t)); 130 *icpp = NULL; 131 return (ISCSI_STATUS_INTERNAL_ERROR); 132 } 133 134 /* Creation of the transfer thread */ 135 if (snprintf(th_name, sizeof (th_name) - 1, ISCSI_CONN_TXTH_NAME_FORMAT, 136 icp->conn_sess->sess_hba->hba_oid, icp->conn_sess->sess_oid, 137 icp->conn_oid) >= sizeof (th_name)) { 138 cv_destroy(&icp->conn_state_change); 139 mutex_destroy(&icp->conn_state_mutex); 140 kmem_free(icp, sizeof (iscsi_conn_t)); 141 ddi_taskq_destroy(icp->conn_cn_taskq); 142 *icpp = NULL; 143 return (ISCSI_STATUS_INTERNAL_ERROR); 144 } 145 146 icp->conn_tx_thread = iscsi_thread_create(isp->sess_hba->hba_dip, 147 th_name, iscsi_tx_thread, icp); 148 149 /* setup connection queues */ 150 iscsi_init_queue(&icp->conn_queue_active); 151 iscsi_init_queue(&icp->conn_queue_idm_aborting); 152 153 bcopy(addr, &icp->conn_base_addr, sizeof (icp->conn_base_addr)); 154 155 /* Add new connection to the session connection list */ 156 icp->conn_cid = isp->sess_conn_next_cid++; 157 if (isp->sess_conn_list == NULL) { 158 isp->sess_conn_list = isp->sess_conn_list_last_ptr = icp; 159 } else { 160 isp->sess_conn_list_last_ptr->conn_next = icp; 161 isp->sess_conn_list_last_ptr = icp; 162 } 163 164 KSTAT_INC_SESS_CNTR_CONN(isp); 165 (void) iscsi_conn_kstat_init(icp); 166 167 *icpp = icp; 168 169 return (ISCSI_STATUS_SUCCESS); 170 } 171 172 /* 173 * iscsi_conn_online - This attempts to take a connection from 174 * ISCSI_CONN_STATE_FREE to ISCSI_CONN_STATE_LOGGED_IN. 175 */ 176 iscsi_status_t 177 iscsi_conn_online(iscsi_conn_t *icp) 178 { 179 iscsi_task_t *itp; 180 iscsi_status_t rval; 181 182 ASSERT(icp != NULL); 183 ASSERT(mutex_owned(&icp->conn_state_mutex)); 184 ASSERT(icp->conn_state == ISCSI_CONN_STATE_FREE); 185 186 /* 187 * If we are attempting to connect then for the purposes of the 188 * other initiator code we are effectively in ISCSI_CONN_STATE_IN_LOGIN. 189 */ 190 iscsi_conn_update_state_locked(icp, ISCSI_CONN_STATE_IN_LOGIN); 191 mutex_exit(&icp->conn_state_mutex); 192 193 /* 194 * Sync base connection information before login 195 * A login redirection might have shifted the 196 * current information from the base. 197 */ 198 bcopy(&icp->conn_base_addr, &icp->conn_curr_addr, 199 sizeof (icp->conn_curr_addr)); 200 201 itp = kmem_zalloc(sizeof (iscsi_task_t), KM_SLEEP); 202 ASSERT(itp != NULL); 203 204 itp->t_arg = icp; 205 itp->t_blocking = B_TRUE; 206 rval = iscsi_login_start(itp); 207 kmem_free(itp, sizeof (iscsi_task_t)); 208 209 mutex_enter(&icp->conn_state_mutex); 210 211 return (rval); 212 } 213 214 /* 215 * iscsi_conn_offline - This attempts to take a connection from 216 * any state to ISCSI_CONN_STATE_FREE. 217 */ 218 iscsi_status_t 219 iscsi_conn_offline(iscsi_conn_t *icp) 220 { 221 clock_t delay; 222 223 ASSERT(icp != NULL); 224 225 /* 226 * We can only destroy a connection if its either in 227 * a state of FREE or LOGGED. The other states are 228 * transitionary and its unsafe to perform actions 229 * on the connection in those states. Set a flag 230 * on the connection to influence the transitions 231 * to quickly complete. Then wait for a state 232 * transition. 233 * 234 * ISCSI_CONN_STATE_LOGGED_IN is set immediately at the 235 * start of CN_NOTIFY_FFP processing. icp->conn_state_ffp 236 * is set to true at the end of ffp processing, at which 237 * point any session updates are complete. We don't 238 * want to start offlining the connection before we're 239 * done completing the FFP processing since this might 240 * interrupt the discovery process. 241 */ 242 delay = ddi_get_lbolt() + SEC_TO_TICK(SHUTDOWN_TIMEOUT); 243 mutex_enter(&icp->conn_state_mutex); 244 icp->conn_state_destroy = B_TRUE; 245 while ((((icp->conn_state != ISCSI_CONN_STATE_FREE) && 246 (icp->conn_state != ISCSI_CONN_STATE_LOGGED_IN)) || 247 ((icp->conn_state == ISCSI_CONN_STATE_LOGGED_IN) && 248 !icp->conn_state_ffp)) && 249 (ddi_get_lbolt() < delay)) { 250 /* wait for transition */ 251 (void) cv_timedwait(&icp->conn_state_change, 252 &icp->conn_state_mutex, delay); 253 } 254 255 switch (icp->conn_state) { 256 case ISCSI_CONN_STATE_FREE: 257 break; 258 case ISCSI_CONN_STATE_LOGGED_IN: 259 if (icp->conn_state_ffp) { 260 /* Hold is released in iscsi_handle_logout */ 261 idm_conn_hold(icp->conn_ic); 262 (void) iscsi_handle_logout(icp); 263 } else { 264 icp->conn_state_destroy = B_FALSE; 265 mutex_exit(&icp->conn_state_mutex); 266 return (ISCSI_STATUS_INTERNAL_ERROR); 267 } 268 break; 269 case ISCSI_CONN_STATE_IN_LOGIN: 270 case ISCSI_CONN_STATE_IN_LOGOUT: 271 case ISCSI_CONN_STATE_FAILED: 272 case ISCSI_CONN_STATE_POLLING: 273 default: 274 icp->conn_state_destroy = B_FALSE; 275 mutex_exit(&icp->conn_state_mutex); 276 return (ISCSI_STATUS_INTERNAL_ERROR); 277 } 278 mutex_exit(&icp->conn_state_mutex); 279 280 return (ISCSI_STATUS_SUCCESS); 281 } 282 283 /* 284 * iscsi_conn_destroy - This destroys an iscsi connection structure 285 * and de-associates it with the session. The connection should 286 * already been in the ISCSI_CONN_STATE_FREE when attempting this 287 * operation. 288 */ 289 iscsi_status_t 290 iscsi_conn_destroy(iscsi_conn_t *icp) 291 { 292 iscsi_sess_t *isp; 293 iscsi_conn_t *t_icp; 294 295 ASSERT(icp != NULL); 296 isp = icp->conn_sess; 297 ASSERT(isp != NULL); 298 299 if (icp->conn_state != ISCSI_CONN_STATE_FREE) { 300 return (ISCSI_STATUS_INTERNAL_ERROR); 301 } 302 303 /* Destroy transfer thread */ 304 iscsi_thread_destroy(icp->conn_tx_thread); 305 ddi_taskq_destroy(icp->conn_cn_taskq); 306 307 /* Terminate connection queues */ 308 iscsi_destroy_queue(&icp->conn_queue_idm_aborting); 309 iscsi_destroy_queue(&icp->conn_queue_active); 310 311 cv_destroy(&icp->conn_login_cv); 312 mutex_destroy(&icp->conn_login_mutex); 313 cv_destroy(&icp->conn_state_change); 314 mutex_destroy(&icp->conn_state_mutex); 315 316 /* 317 * Remove connection from sessions linked list. 318 */ 319 if (isp->sess_conn_list == icp) { 320 /* connection first item in list */ 321 isp->sess_conn_list = icp->conn_next; 322 /* 323 * check if this is also the last item in the list 324 */ 325 if (isp->sess_conn_list_last_ptr == icp) { 326 isp->sess_conn_list_last_ptr = NULL; 327 } 328 } else { 329 /* 330 * search session list for icp pointing 331 * to connection being removed. Then 332 * update that connections next pointer. 333 */ 334 t_icp = isp->sess_conn_list; 335 while (t_icp->conn_next != NULL) { 336 if (t_icp->conn_next == icp) { 337 break; 338 } 339 t_icp = t_icp->conn_next; 340 } 341 if (t_icp->conn_next == icp) { 342 t_icp->conn_next = icp->conn_next; 343 /* 344 * if this is the last connection in the list 345 * update the last_ptr to point to t_icp 346 */ 347 if (isp->sess_conn_list_last_ptr == icp) { 348 isp->sess_conn_list_last_ptr = t_icp; 349 } 350 } else { 351 /* couldn't find session */ 352 ASSERT(FALSE); 353 } 354 } 355 356 /* Free this Connections Data */ 357 iscsi_conn_kstat_term(icp); 358 kmem_free(icp, sizeof (iscsi_conn_t)); 359 360 return (ISCSI_STATUS_SUCCESS); 361 } 362 363 364 /* 365 * iscsi_conn_set_login_min_max - set min/max login window 366 * 367 * Used to set the min and max login window. Input values 368 * are in seconds. 369 */ 370 void 371 iscsi_conn_set_login_min_max(iscsi_conn_t *icp, int min, int max) 372 { 373 ASSERT(icp != NULL); 374 375 icp->conn_login_min = ddi_get_lbolt() + SEC_TO_TICK(min); 376 icp->conn_login_max = ddi_get_lbolt() + SEC_TO_TICK(max); 377 } 378 379 380 /* 381 * Process the idm notifications 382 */ 383 idm_status_t 384 iscsi_client_notify(idm_conn_t *ic, idm_client_notify_t icn, uintptr_t data) 385 { 386 iscsi_cn_task_t *cn; 387 iscsi_conn_t *icp = ic->ic_handle; 388 iscsi_sess_t *isp; 389 uint32_t event_count; 390 391 /* 392 * Don't access icp if the notification is CN_CONNECT_DESTROY 393 * since icp may have already been freed. 394 * 395 * In particular, we cannot audit the CN_CONNECT_DESTROY event. 396 * 397 * Handle a few cases immediately, the rest in a task queue. 398 */ 399 switch (icn) { 400 case CN_CONNECT_FAIL: 401 case CN_LOGIN_FAIL: 402 /* 403 * Wakeup any thread waiting for login stuff to happen. 404 */ 405 ASSERT(icp != NULL); 406 407 mutex_enter(&icp->conn_state_mutex); 408 idm_sm_audit_event(&icp->conn_state_audit, 409 SAS_ISCSI_CONN, icp->conn_state, icn, data); 410 mutex_exit(&icp->conn_state_mutex); 411 iscsi_login_update_state(icp, LOGIN_ERROR); 412 return (IDM_STATUS_SUCCESS); 413 414 case CN_READY_FOR_LOGIN: 415 idm_conn_hold(ic); /* Released in CN_CONNECT_LOST */ 416 ASSERT(icp != NULL); 417 418 mutex_enter(&icp->conn_state_mutex); 419 idm_sm_audit_event(&icp->conn_state_audit, 420 SAS_ISCSI_CONN, icp->conn_state, icn, data); 421 icp->conn_state_idm_connected = B_TRUE; 422 cv_broadcast(&icp->conn_state_change); 423 mutex_exit(&icp->conn_state_mutex); 424 425 iscsi_login_update_state(icp, LOGIN_READY); 426 return (IDM_STATUS_SUCCESS); 427 428 case CN_CONNECT_DESTROY: 429 /* 430 * We released any dependecies we had on this object in 431 * either CN_LOGIN_FAIL or CN_CONNECT_LOST so we just need 432 * to destroy the IDM connection now. 433 */ 434 idm_ini_conn_destroy(ic); 435 return (IDM_STATUS_SUCCESS); 436 } 437 438 ASSERT(icp != NULL); 439 mutex_enter(&icp->conn_state_mutex); 440 idm_sm_audit_event(&icp->conn_state_audit, 441 SAS_ISCSI_CONN, icp->conn_state, icn, data); 442 mutex_exit(&icp->conn_state_mutex); 443 isp = icp->conn_sess; 444 445 /* 446 * Dispatch notifications to the taskq since they often require 447 * long blocking operations. In the case of CN_CONNECT_DESTROY 448 * we actually just want to destroy the connection which we 449 * can't do in the IDM taskq context. 450 */ 451 cn = kmem_alloc(sizeof (*cn), KM_SLEEP); 452 453 cn->ct_ic = ic; 454 cn->ct_icn = icn; 455 cn->ct_data = data; 456 457 idm_conn_hold(ic); 458 459 if (ddi_taskq_dispatch(icp->conn_cn_taskq, 460 iscsi_client_notify_task, cn, DDI_SLEEP) != DDI_SUCCESS) { 461 idm_conn_rele(ic); 462 cmn_err(CE_WARN, "iscsi connection(%u) failure - " 463 "unable to schedule notify task", icp->conn_oid); 464 iscsi_conn_update_state(icp, ISCSI_CONN_STATE_FREE); 465 event_count = atomic_inc_32_nv(&isp->sess_state_event_count); 466 iscsi_sess_enter_state_zone(isp); 467 468 iscsi_sess_state_machine(isp, 469 ISCSI_SESS_EVENT_N6, event_count); 470 471 iscsi_sess_exit_state_zone(isp); 472 } 473 474 return (IDM_STATUS_SUCCESS); 475 } 476 477 static void 478 iscsi_client_notify_task(void *cn_task_void) 479 { 480 iscsi_cn_task_t *cn_task = cn_task_void; 481 iscsi_conn_t *icp; 482 iscsi_sess_t *isp; 483 idm_conn_t *ic; 484 idm_client_notify_t icn; 485 uintptr_t data; 486 idm_ffp_disable_t disable_type; 487 boolean_t in_login; 488 uint32_t event_count; 489 490 ic = cn_task->ct_ic; 491 icn = cn_task->ct_icn; 492 data = cn_task->ct_data; 493 494 icp = ic->ic_handle; 495 ASSERT(icp != NULL); 496 isp = icp->conn_sess; 497 498 switch (icn) { 499 case CN_FFP_ENABLED: 500 mutex_enter(&icp->conn_state_mutex); 501 icp->conn_async_logout = B_FALSE; 502 icp->conn_state_ffp = B_TRUE; 503 cv_broadcast(&icp->conn_state_change); 504 mutex_exit(&icp->conn_state_mutex); 505 506 /* 507 * This logic assumes that the IDM login-snooping code 508 * and the initiator login code will agree to go when 509 * the connection is in FFP or final error received. 510 * The reason we do this is that we don't want to process 511 * CN_FFP_DISABLED until CN_FFP_ENABLED has been full handled. 512 */ 513 mutex_enter(&icp->conn_login_mutex); 514 while ((icp->conn_login_state != LOGIN_FFP) && 515 (icp->conn_login_state != LOGIN_ERROR)) { 516 cv_wait(&icp->conn_login_cv, &icp->conn_login_mutex); 517 } 518 mutex_exit(&icp->conn_login_mutex); 519 break; 520 case CN_FFP_DISABLED: 521 disable_type = (idm_ffp_disable_t)data; 522 523 mutex_enter(&icp->conn_state_mutex); 524 switch (disable_type) { 525 case FD_SESS_LOGOUT: 526 case FD_CONN_LOGOUT: 527 if (icp->conn_async_logout) { 528 /* 529 * Our logout was in response to an 530 * async logout request so treat this 531 * like a connection failure (we will 532 * try to re-establish the connection) 533 */ 534 iscsi_conn_update_state_locked(icp, 535 ISCSI_CONN_STATE_FAILED); 536 } else { 537 /* 538 * Logout due to to user config change, 539 * we will not try to re-establish 540 * the connection. 541 */ 542 iscsi_conn_update_state_locked(icp, 543 ISCSI_CONN_STATE_IN_LOGOUT); 544 /* 545 * Hold off generating the ISCSI_SESS_EVENT_N3 546 * event until we get the CN_CONNECT_LOST 547 * notification. This matches the pre-IDM 548 * implementation better. 549 */ 550 } 551 break; 552 553 case FD_CONN_FAIL: 554 default: 555 if (icp->conn_state == ISCSI_CONN_STATE_IN_LOGIN) { 556 iscsi_conn_update_state_locked(icp, 557 ISCSI_CONN_STATE_FREE); 558 } else { 559 iscsi_conn_update_state_locked(icp, 560 ISCSI_CONN_STATE_FAILED); 561 } 562 break; 563 } 564 565 icp->conn_state_ffp = B_FALSE; 566 cv_broadcast(&icp->conn_state_change); 567 mutex_exit(&icp->conn_state_mutex); 568 569 break; 570 case CN_CONNECT_LOST: 571 /* 572 * We only care about CN_CONNECT_LOST if we've logged in. IDM 573 * sends a flag as the data payload to indicate whether we 574 * were trying to login. The CN_LOGIN_FAIL notification 575 * gives us what we need to know for login failures and 576 * otherwise we will need to keep a bunch of state to know 577 * what CN_CONNECT_LOST means to us. 578 */ 579 in_login = (boolean_t)data; 580 if (in_login || 581 (icp->conn_prev_state == ISCSI_CONN_STATE_IN_LOGIN)) { 582 mutex_enter(&icp->conn_state_mutex); 583 584 icp->conn_state_idm_connected = B_FALSE; 585 cv_broadcast(&icp->conn_state_change); 586 mutex_exit(&icp->conn_state_mutex); 587 588 /* Release connect hold from CN_READY_FOR_LOGIN */ 589 idm_conn_rele(ic); 590 break; 591 } 592 593 /* Any remaining commands are never going to finish */ 594 iscsi_conn_flush_active_cmds(icp); 595 596 /* 597 * The connection is no longer active so cleanup any 598 * references to the connection and release any holds so 599 * that IDM can finish cleanup. 600 */ 601 mutex_enter(&icp->conn_state_mutex); 602 if (icp->conn_state != ISCSI_CONN_STATE_FAILED) { 603 mutex_exit(&icp->conn_state_mutex); 604 event_count = atomic_inc_32_nv( 605 &isp->sess_state_event_count); 606 iscsi_sess_enter_state_zone(isp); 607 608 iscsi_sess_state_machine(isp, ISCSI_SESS_EVENT_N3, 609 event_count); 610 611 iscsi_sess_exit_state_zone(isp); 612 613 mutex_enter(&icp->conn_state_mutex); 614 iscsi_conn_update_state_locked(icp, 615 ISCSI_CONN_STATE_FREE); 616 } else { 617 mutex_exit(&icp->conn_state_mutex); 618 event_count = atomic_inc_32_nv( 619 &isp->sess_state_event_count); 620 iscsi_sess_enter_state_zone(isp); 621 622 iscsi_sess_state_machine(isp, 623 ISCSI_SESS_EVENT_N5, event_count); 624 625 iscsi_sess_exit_state_zone(isp); 626 627 /* 628 * If session type is NORMAL, try to reestablish the 629 * connection. 630 */ 631 if ((isp->sess_type == ISCSI_SESS_TYPE_NORMAL) && 632 !(ISCSI_LOGIN_TPGT_NEGO_ERROR(icp))) { 633 iscsi_conn_retry(isp, icp); 634 mutex_enter(&icp->conn_state_mutex); 635 } else { 636 event_count = atomic_inc_32_nv( 637 &isp->sess_state_event_count); 638 iscsi_sess_enter_state_zone(isp); 639 640 iscsi_sess_state_machine(isp, 641 ISCSI_SESS_EVENT_N6, event_count); 642 643 iscsi_sess_exit_state_zone(isp); 644 645 mutex_enter(&icp->conn_state_mutex); 646 iscsi_conn_update_state_locked(icp, 647 ISCSI_CONN_STATE_FREE); 648 } 649 } 650 651 (void) iscsi_thread_stop(icp->conn_tx_thread); 652 653 icp->conn_state_idm_connected = B_FALSE; 654 cv_broadcast(&icp->conn_state_change); 655 mutex_exit(&icp->conn_state_mutex); 656 657 /* Release connect hold from CN_READY_FOR_LOGIN */ 658 idm_conn_rele(ic); 659 break; 660 default: 661 ISCSI_CONN_LOG(CE_WARN, 662 "iscsi_client_notify: unknown notification: " 663 "%x: NOT IMPLEMENTED YET: icp: %p ic: %p ", 664 icn, (void *)icp, (void *)ic); 665 break; 666 } 667 /* free the task notify structure we allocated in iscsi_client_notify */ 668 kmem_free(cn_task, sizeof (*cn_task)); 669 670 /* Release the hold we acquired in iscsi_client_notify */ 671 idm_conn_rele(ic); 672 } 673 674 /* 675 * iscsi_conn_sync_params - used to update connection parameters 676 * 677 * Used to update connection parameters with current configured 678 * parameters in the persistent store. This should be called 679 * before starting to make a new iscsi connection in iscsi_login. 680 */ 681 iscsi_status_t 682 iscsi_conn_sync_params(iscsi_conn_t *icp) 683 { 684 iscsi_sess_t *isp; 685 iscsi_hba_t *ihp; 686 int param_id; 687 persistent_param_t pp; 688 persistent_tunable_param_t ptp; 689 iscsi_config_sess_t *ics; 690 int idx, size; 691 char *name; 692 693 ASSERT(icp != NULL); 694 ASSERT((icp->conn_state == ISCSI_CONN_STATE_IN_LOGIN) || 695 (icp->conn_state == ISCSI_CONN_STATE_FAILED) || 696 (icp->conn_state == ISCSI_CONN_STATE_POLLING)); 697 isp = icp->conn_sess; 698 ASSERT(isp != NULL); 699 ihp = isp->sess_hba; 700 ASSERT(ihp != NULL); 701 702 /* 703 * Check if someone is trying to destroy this 704 * connection. If so fail the sync request, 705 * as a method of fast fail. 706 */ 707 if (icp->conn_state_destroy == B_TRUE) { 708 return (ISCSI_STATUS_SHUTDOWN); 709 } 710 711 bzero(&pp, sizeof (pp)); 712 713 /* First get a copy of the HBA params */ 714 bcopy(&ihp->hba_params, &icp->conn_params, 715 sizeof (iscsi_login_params_t)); 716 bcopy(&ihp->hba_tunable_params, &icp->conn_tunable_params, 717 sizeof (iscsi_tunable_params_t)); 718 719 /* 720 * Now we need to get the session configured 721 * values from the persistent store and apply 722 * them to our connection. 723 */ 724 (void) persistent_param_get((char *)isp->sess_name, &pp); 725 for (param_id = 0; param_id < ISCSI_NUM_LOGIN_PARAM; 726 param_id++) { 727 if (iscsiboot_prop && modrootloaded && 728 !iscsi_chk_bootlun_mpxio(ihp) && isp->sess_boot) { 729 /* 730 * iscsi boot with mpxio disabled 731 * while iscsi booting target's parameter overriden 732 * do no update target's parameters. 733 */ 734 if (pp.p_bitmap) { 735 cmn_err(CE_NOTE, "Adopting " 736 " default login parameters in" 737 " boot session as MPxIO is disabled"); 738 } 739 break; 740 } 741 if (pp.p_bitmap & (1 << param_id)) { 742 743 switch (param_id) { 744 /* 745 * Boolean parameters 746 */ 747 case ISCSI_LOGIN_PARAM_DATA_SEQUENCE_IN_ORDER: 748 icp->conn_params.data_pdu_in_order = 749 pp.p_params.data_pdu_in_order; 750 break; 751 case ISCSI_LOGIN_PARAM_IMMEDIATE_DATA: 752 icp->conn_params.immediate_data = 753 pp.p_params.immediate_data; 754 break; 755 case ISCSI_LOGIN_PARAM_INITIAL_R2T: 756 icp->conn_params.initial_r2t = 757 pp.p_params.initial_r2t; 758 break; 759 case ISCSI_LOGIN_PARAM_DATA_PDU_IN_ORDER: 760 icp->conn_params.data_pdu_in_order = 761 pp.p_params.data_pdu_in_order; 762 break; 763 /* 764 * Integer parameters 765 */ 766 case ISCSI_LOGIN_PARAM_HEADER_DIGEST: 767 icp->conn_params.header_digest = 768 pp.p_params.header_digest; 769 break; 770 case ISCSI_LOGIN_PARAM_DATA_DIGEST: 771 icp->conn_params.data_digest = 772 pp.p_params.data_digest; 773 break; 774 case ISCSI_LOGIN_PARAM_DEFAULT_TIME_2_RETAIN: 775 icp->conn_params.default_time_to_retain = 776 pp.p_params.default_time_to_retain; 777 break; 778 case ISCSI_LOGIN_PARAM_DEFAULT_TIME_2_WAIT: 779 icp->conn_params.default_time_to_wait = 780 pp.p_params.default_time_to_wait; 781 break; 782 case ISCSI_LOGIN_PARAM_MAX_RECV_DATA_SEGMENT_LENGTH: 783 icp->conn_params.max_recv_data_seg_len = 784 pp.p_params.max_recv_data_seg_len; 785 break; 786 case ISCSI_LOGIN_PARAM_FIRST_BURST_LENGTH: 787 icp->conn_params.first_burst_length = 788 pp.p_params.first_burst_length; 789 break; 790 case ISCSI_LOGIN_PARAM_MAX_BURST_LENGTH: 791 icp->conn_params.max_burst_length = 792 pp.p_params.max_burst_length; 793 break; 794 795 /* 796 * Integer parameters which currently are unsettable 797 */ 798 case ISCSI_LOGIN_PARAM_MAX_CONNECTIONS: 799 /* FALLTHRU */ 800 case ISCSI_LOGIN_PARAM_OUTSTANDING_R2T: 801 /* FALLTHRU */ 802 case ISCSI_LOGIN_PARAM_ERROR_RECOVERY_LEVEL: 803 /* FALLTHRU */ 804 default: 805 break; 806 } 807 } 808 } 809 810 if (persistent_get_tunable_param((char *)isp->sess_name, &ptp) == 811 B_TRUE) { 812 if (ptp.p_bitmap & ISCSI_TUNABLE_PARAM_RX_TIMEOUT_VALUE) { 813 icp->conn_tunable_params.recv_login_rsp_timeout = 814 ptp.p_params.recv_login_rsp_timeout; 815 } 816 if (ptp.p_bitmap & ISCSI_TUNABLE_PARAM_CONN_LOGIN_MAX) { 817 icp->conn_tunable_params.conn_login_max = 818 ptp.p_params.conn_login_max; 819 } 820 if (ptp.p_bitmap & ISCSI_TUNABLE_PARAM_LOGIN_POLLING_DELAY) { 821 icp->conn_tunable_params.polling_login_delay = 822 ptp.p_params.polling_login_delay; 823 } 824 } 825 826 /* Skip binding checks on discovery sessions */ 827 if (isp->sess_type == ISCSI_SESS_TYPE_DISCOVERY) { 828 return (ISCSI_STATUS_SUCCESS); 829 } 830 831 /* 832 * Now we need to get the current optional connection 833 * binding information. 834 */ 835 /* setup initial buffer for configured session information */ 836 size = sizeof (*ics); 837 ics = kmem_zalloc(size, KM_SLEEP); 838 ics->ics_in = 1; 839 840 /* get configured sessions information */ 841 name = (char *)isp->sess_name; 842 if (persistent_get_config_session(name, ics) == B_FALSE) { 843 /* 844 * If we were unable to get target level information 845 * then check the initiator level information. 846 */ 847 name = (char *)isp->sess_hba->hba_name; 848 if (persistent_get_config_session(name, ics) == B_FALSE) { 849 /* 850 * No hba information is found. So assume default 851 * one session unbound behavior. 852 */ 853 ics->ics_out = 1; 854 ics->ics_bound = B_FALSE; 855 } 856 } 857 858 if (iscsiboot_prop && (ics->ics_out > 1) && isp->sess_boot && 859 !iscsi_chk_bootlun_mpxio(ihp)) { 860 /* 861 * iscsi booting session with mpxio disabled, 862 * no need set multiple sessions for booting session 863 */ 864 ics->ics_out = 1; 865 ics->ics_bound = B_FALSE; 866 cmn_err(CE_NOTE, "MPxIO is disabled," 867 " no need to configure multiple boot sessions"); 868 } 869 870 /* 871 * Check to make sure this session is still a configured 872 * session. The user might have decreased the session 873 * count. (NOTE: byte 5 of the sess_isid is the session 874 * count (via MS/T). This counter starts at 0.) 875 */ 876 877 878 idx = isp->sess_isid[5]; 879 880 if (iscsiboot_prop && (idx == ISCSI_MAX_CONFIG_SESSIONS)) { 881 /* 882 * This is temporary session for boot session propose 883 * no need to bound IP for this session 884 */ 885 icp->conn_bound = B_FALSE; 886 kmem_free(ics, sizeof (iscsi_config_sess_t)); 887 return (ISCSI_STATUS_SUCCESS); 888 } 889 890 if (ics->ics_out <= idx) { 891 /* 892 * No longer a configured session. Return a 893 * failure so we don't attempt to relogin. 894 */ 895 return (ISCSI_STATUS_SHUTDOWN); 896 } 897 898 /* 899 * If sessions are unbound set this information on 900 * the connection and return success. 901 */ 902 if (ics->ics_bound == B_FALSE) { 903 icp->conn_bound = B_FALSE; 904 kmem_free(ics, sizeof (iscsi_config_sess_t)); 905 return (ISCSI_STATUS_SUCCESS); 906 } 907 908 /* 909 * Since the sessions are bound we need to find the matching 910 * binding information for the session's isid. If this 911 * session's isid is > 0 then we need to get more configured 912 * session information to find the binding info. 913 */ 914 if (idx > 0) { 915 int ics_out; 916 917 ics_out = ics->ics_out; 918 /* record new size and free last buffer */ 919 size = ISCSI_SESSION_CONFIG_SIZE(ics_out); 920 kmem_free(ics, sizeof (*ics)); 921 922 /* allocate new buffer */ 923 ics = kmem_zalloc(size, KM_SLEEP); 924 ics->ics_in = ics_out; 925 926 /* get configured sessions information */ 927 if (persistent_get_config_session(name, ics) != B_TRUE) { 928 cmn_err(CE_NOTE, "iscsi session(%d) - " 929 "unable to get configured session information\n", 930 isp->sess_oid); 931 kmem_free(ics, size); 932 return (ISCSI_STATUS_SHUTDOWN); 933 } 934 } 935 936 /* Copy correct binding information to the connection */ 937 icp->conn_bound = B_TRUE; 938 if (ics->ics_bindings[idx].i_insize == sizeof (struct in_addr)) { 939 bcopy(&ics->ics_bindings[idx].i_addr.in4, 940 &icp->conn_bound_addr.sin4.sin_addr.s_addr, 941 sizeof (struct in_addr)); 942 icp->conn_bound_addr.sin4.sin_family = AF_INET; 943 } else { 944 bcopy(&ics->ics_bindings[idx].i_addr.in6, 945 &icp->conn_bound_addr.sin6.sin6_addr.s6_addr, 946 sizeof (struct in6_addr)); 947 icp->conn_bound_addr.sin6.sin6_family = AF_INET6; 948 } 949 950 kmem_free(ics, size); 951 952 return (ISCSI_STATUS_SUCCESS); 953 } 954 955 /* 956 * +--------------------------------------------------------------------+ 957 * | Internal Connection Interfaces | 958 * +--------------------------------------------------------------------+ 959 */ 960 961 /* 962 * iscsi_conn_flush_active_cmds - flush all active icmdps 963 * for a connection. 964 */ 965 static void 966 iscsi_conn_flush_active_cmds(iscsi_conn_t *icp) 967 { 968 iscsi_cmd_t *icmdp; 969 iscsi_sess_t *isp; 970 boolean_t lock_held = B_FALSE; 971 972 ASSERT(icp != NULL); 973 isp = icp->conn_sess; 974 ASSERT(isp != NULL); 975 976 if (mutex_owned(&icp->conn_queue_active.mutex)) { 977 lock_held = B_TRUE; 978 } else { 979 mutex_enter(&icp->conn_queue_active.mutex); 980 } 981 982 /* Flush active queue */ 983 icmdp = icp->conn_queue_active.head; 984 while (icmdp != NULL) { 985 986 mutex_enter(&icmdp->cmd_mutex); 987 if (icmdp->cmd_type == ISCSI_CMD_TYPE_SCSI) { 988 icmdp->cmd_un.scsi.pkt_stat |= STAT_ABORTED; 989 } 990 mutex_exit(&icmdp->cmd_mutex); 991 992 iscsi_cmd_state_machine(icmdp, 993 ISCSI_CMD_EVENT_E7, isp); 994 icmdp = icp->conn_queue_active.head; 995 } 996 997 /* Wait for active queue to drain */ 998 while (icp->conn_queue_active.count) { 999 mutex_exit(&icp->conn_queue_active.mutex); 1000 delay(drv_usectohz(100000)); 1001 mutex_enter(&icp->conn_queue_active.mutex); 1002 } 1003 1004 if (lock_held == B_FALSE) { 1005 mutex_exit(&icp->conn_queue_active.mutex); 1006 } 1007 1008 /* Wait for IDM abort queue to drain (if necessary) */ 1009 mutex_enter(&icp->conn_queue_idm_aborting.mutex); 1010 while (icp->conn_queue_idm_aborting.count) { 1011 mutex_exit(&icp->conn_queue_idm_aborting.mutex); 1012 delay(drv_usectohz(100000)); 1013 mutex_enter(&icp->conn_queue_idm_aborting.mutex); 1014 } 1015 mutex_exit(&icp->conn_queue_idm_aborting.mutex); 1016 } 1017 1018 /* 1019 * iscsi_conn_retry - retry connect/login 1020 */ 1021 void 1022 iscsi_conn_retry(iscsi_sess_t *isp, iscsi_conn_t *icp) 1023 { 1024 iscsi_task_t *itp; 1025 uint32_t event_count; 1026 1027 ASSERT(isp != NULL); 1028 ASSERT(icp != NULL); 1029 1030 /* set login min/max time values */ 1031 iscsi_conn_set_login_min_max(icp, 1032 ISCSI_CONN_DEFAULT_LOGIN_MIN, 1033 icp->conn_tunable_params.conn_login_max); 1034 1035 ISCSI_CONN_LOG(CE_NOTE, "DEBUG: iscsi_conn_retry: icp: %p icp: %p ", 1036 (void *)icp, 1037 (void *)icp->conn_ic); 1038 1039 /* 1040 * Sync base connection information before login. 1041 * A login redirection might have shifted the 1042 * current information from the base. 1043 */ 1044 bcopy(&icp->conn_base_addr, &icp->conn_curr_addr, 1045 sizeof (icp->conn_curr_addr)); 1046 1047 /* schedule login task */ 1048 itp = kmem_zalloc(sizeof (iscsi_task_t), KM_SLEEP); 1049 itp->t_arg = icp; 1050 itp->t_blocking = B_FALSE; 1051 if (ddi_taskq_dispatch(isp->sess_login_taskq, 1052 (void(*)())iscsi_login_start, itp, DDI_SLEEP) != 1053 DDI_SUCCESS) { 1054 kmem_free(itp, sizeof (iscsi_task_t)); 1055 cmn_err(CE_WARN, "iscsi connection(%u) failure - " 1056 "unable to schedule login task", icp->conn_oid); 1057 1058 iscsi_conn_update_state(icp, ISCSI_CONN_STATE_FREE); 1059 event_count = atomic_inc_32_nv( 1060 &isp->sess_state_event_count); 1061 iscsi_sess_enter_state_zone(isp); 1062 1063 iscsi_sess_state_machine(isp, 1064 ISCSI_SESS_EVENT_N6, event_count); 1065 1066 iscsi_sess_exit_state_zone(isp); 1067 } 1068 } 1069 1070 void 1071 iscsi_conn_update_state(iscsi_conn_t *icp, iscsi_conn_state_t 1072 next_state) 1073 { 1074 mutex_enter(&icp->conn_state_mutex); 1075 (void) iscsi_conn_update_state_locked(icp, next_state); 1076 mutex_exit(&icp->conn_state_mutex); 1077 } 1078 1079 void 1080 iscsi_conn_update_state_locked(iscsi_conn_t *icp, 1081 iscsi_conn_state_t next_state) 1082 { 1083 ASSERT(mutex_owned(&icp->conn_state_mutex)); 1084 next_state = (next_state > ISCSI_CONN_STATE_MAX) ? 1085 ISCSI_CONN_STATE_MAX : next_state; 1086 idm_sm_audit_state_change(&icp->conn_state_audit, 1087 SAS_ISCSI_CONN, icp->conn_state, next_state); 1088 switch (next_state) { 1089 case ISCSI_CONN_STATE_FREE: 1090 case ISCSI_CONN_STATE_IN_LOGIN: 1091 case ISCSI_CONN_STATE_LOGGED_IN: 1092 case ISCSI_CONN_STATE_IN_LOGOUT: 1093 case ISCSI_CONN_STATE_FAILED: 1094 case ISCSI_CONN_STATE_POLLING: 1095 ISCSI_CONN_LOG(CE_NOTE, 1096 "iscsi_conn_update_state conn %p %s(%d) -> %s(%d)", 1097 (void *)icp, 1098 iscsi_ics_name[icp->conn_state], icp->conn_state, 1099 iscsi_ics_name[next_state], next_state); 1100 icp->conn_prev_state = icp->conn_state; 1101 icp->conn_state = next_state; 1102 cv_broadcast(&icp->conn_state_change); 1103 break; 1104 default: 1105 cmn_err(CE_WARN, "Update state found illegal state: %x " 1106 "prev_state: %x", next_state, icp->conn_prev_state); 1107 ASSERT(0); 1108 } 1109 } 1110