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