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