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 26 /* 27 * Copyright 2019 Nexenta Systems, Inc. 28 * Copyright 2023 Oxide Computer Company 29 */ 30 31 /* 32 * iSCSI logical unit interfaces 33 */ 34 35 #include "iscsi.h" 36 #include <sys/bootprops.h> 37 #include <sys/sysevent/eventdefs.h> 38 #include <sys/sysevent/dev.h> 39 40 /* tpgt bytes in string form */ 41 #define TPGT_EXT_SIZE 5 42 43 /* logical unit number bytes in string form */ 44 #define LUN_EXT_SIZE 10 45 46 /* 47 * Addition addr size of size of ',' + max str form of tpgt (2 bytes) + 48 * ',' + max str form of logical unit number (4 bytes). 49 */ 50 #define ADDR_EXT_SIZE (1 + TPGT_EXT_SIZE + 1 + LUN_EXT_SIZE) 51 52 /* internal interfaces */ 53 static iscsi_status_t iscsi_lun_virt_create(iscsi_sess_t *isp, 54 uint16_t lun_num, iscsi_lun_t *ilp, struct scsi_inquiry *inq); 55 static iscsi_status_t iscsi_lun_phys_create(iscsi_sess_t *isp, 56 uint16_t lun_num, iscsi_lun_t *ilp, struct scsi_inquiry *inq); 57 58 extern dev_info_t *scsi_vhci_dip; 59 extern ib_boot_prop_t *iscsiboot_prop; 60 61 /* 62 * +--------------------------------------------------------------------+ 63 * | External Connection Interfaces | 64 * +--------------------------------------------------------------------+ 65 */ 66 67 68 /* 69 * iscsi_lun_create - This function will create a lun mapping. 70 * logic specific to MPxIO vs. NDI node creation is switched 71 * out to a helper function. 72 */ 73 iscsi_status_t 74 iscsi_lun_create(iscsi_sess_t *isp, uint16_t lun_num, uint8_t lun_addr_type, 75 struct scsi_inquiry *inq, char *guid) 76 { 77 iscsi_status_t rtn = ISCSI_STATUS_INTERNAL_ERROR; 78 iscsi_hba_t *ihp = NULL; 79 iscsi_lun_t *ilp = NULL; 80 iscsi_lun_t *ilp_tmp = NULL; 81 char *addr = NULL; 82 uint16_t boot_lun_num = 0; 83 uint64_t *lun_num_ptr = NULL; 84 uint32_t oid_tmp = 0; 85 86 ASSERT(isp != NULL); 87 ihp = isp->sess_hba; 88 ASSERT(ihp != NULL); 89 90 mutex_enter(&iscsi_oid_mutex); 91 oid_tmp = iscsi_oid++; 92 mutex_exit(&iscsi_oid_mutex); 93 94 rw_enter(&isp->sess_lun_list_rwlock, RW_WRITER); 95 /* 96 * Check whether it has already existed in the list. 97 */ 98 for (ilp_tmp = isp->sess_lun_list; ilp_tmp != NULL; 99 ilp_tmp = ilp_tmp->lun_next) { 100 if (ilp_tmp->lun_num == lun_num) { 101 /* 102 * The logic unit has already existed in the list, 103 * return with success. 104 */ 105 rw_exit(&isp->sess_lun_list_rwlock); 106 return (ISCSI_STATUS_SUCCESS); 107 } 108 } 109 110 addr = kmem_zalloc((strlen((char *)isp->sess_name) + 111 ADDR_EXT_SIZE + 1), KM_SLEEP); 112 (void) snprintf(addr, 113 (strlen((char *)isp->sess_name) + 114 ADDR_EXT_SIZE + 1), 115 "%02X%02X%s%04X,%d", isp->sess_isid[4], 116 isp->sess_isid[5], isp->sess_name, 117 isp->sess_tpgt_nego & 0xFFFF, lun_num); 118 119 /* allocate space for lun struct */ 120 ilp = kmem_zalloc(sizeof (iscsi_lun_t), KM_SLEEP); 121 ilp->lun_sig = ISCSI_SIG_LUN; 122 ilp->lun_state &= ISCSI_LUN_STATE_CLEAR; 123 ilp->lun_state |= ISCSI_LUN_STATE_OFFLINE; 124 125 /* initialize common LU information */ 126 ilp->lun_num = lun_num; 127 ilp->lun_addr_type = lun_addr_type; 128 ilp->lun_sess = isp; 129 ilp->lun_addr = addr; 130 ilp->lun_type = inq->inq_dtype & DTYPE_MASK; 131 ilp->lun_oid = oid_tmp; 132 /* 133 * Setting refcnt to 1 is the first hold for the LUN structure. 134 */ 135 ilp->lun_refcnt = 1; 136 mutex_init(&ilp->lun_mutex, NULL, MUTEX_DRIVER, NULL); 137 138 bcopy(inq->inq_vid, ilp->lun_vid, sizeof (inq->inq_vid)); 139 bcopy(inq->inq_pid, ilp->lun_pid, sizeof (inq->inq_pid)); 140 141 /* store GUID if valid one exists */ 142 if (guid != NULL) { 143 ilp->lun_guid_size = strlen(guid) + 1; 144 ilp->lun_guid = kmem_zalloc(ilp->lun_guid_size, KM_SLEEP); 145 (void) strcpy(ilp->lun_guid, guid); 146 } else { 147 ilp->lun_guid_size = 0; 148 ilp->lun_guid = NULL; 149 } 150 151 /* 152 * We need to add the lun to our lists now because during the 153 * lun creation we will get called back into multiple times 154 * depending on the createion type. These callbacks will 155 * occur via our tran_init_lun, tran_get_name, tran_get_bus_addr, 156 * tran_init_pkt, tran_start. 157 */ 158 if (isp->sess_lun_list == NULL) { 159 isp->sess_lun_list = ilp; 160 } else { 161 ilp->lun_next = isp->sess_lun_list; 162 isp->sess_lun_list = ilp; 163 } 164 165 /* Attempt to create a scsi_vhci binding if GUID is available */ 166 if ((ihp->hba_mpxio_enabled == B_TRUE) && 167 (guid != NULL)) { 168 rtn = iscsi_lun_virt_create(isp, lun_num, ilp, inq); 169 } 170 if (!ISCSI_SUCCESS(rtn)) { 171 /* unable to bind under scsi_vhci, failback to ndi */ 172 rtn = iscsi_lun_phys_create(isp, lun_num, ilp, inq); 173 } 174 175 /* 176 * If NOT successful we need to remove the lun from the 177 * session and free any related resources. 178 */ 179 if (!ISCSI_SUCCESS(rtn)) { 180 if (ilp == isp->sess_lun_list) { 181 /* if head, set head to our next */ 182 isp->sess_lun_list = ilp->lun_next; 183 } else { 184 /* if not head, set prev lun's next to our next */ 185 for (ilp_tmp = isp->sess_lun_list; ilp_tmp; 186 ilp_tmp = ilp_tmp->lun_next) { 187 if (ilp_tmp->lun_next == ilp) { 188 ilp_tmp->lun_next = ilp->lun_next; 189 break; 190 } 191 } 192 } 193 194 kmem_free(ilp->lun_addr, 195 (strlen((char *)isp->sess_name) + 196 ADDR_EXT_SIZE + 1)); 197 ilp->lun_addr = NULL; 198 199 if (ilp->lun_guid != NULL) { 200 kmem_free(ilp->lun_guid, ilp->lun_guid_size); 201 ilp->lun_guid = NULL; 202 } 203 mutex_destroy(&ilp->lun_mutex); 204 kmem_free(ilp, sizeof (iscsi_lun_t)); 205 } else { 206 ilp->lun_state &= ISCSI_LUN_STATE_CLEAR; 207 ilp->lun_state |= ISCSI_LUN_STATE_ONLINE; 208 ilp->lun_time_online = ddi_get_time(); 209 210 /* Check whether this is the required LUN for iscsi boot */ 211 if (iscsiboot_prop != NULL && isp->sess_boot == B_TRUE && 212 iscsiboot_prop->boot_tgt.lun_online == 0) { 213 lun_num_ptr = 214 (uint64_t *)iscsiboot_prop->boot_tgt.tgt_boot_lun; 215 boot_lun_num = (uint16_t)(*lun_num_ptr); 216 if (boot_lun_num == ilp->lun_num) { 217 /* 218 * During iscsi boot, the boot lun has been 219 * online, we should set the "online flag". 220 */ 221 iscsiboot_prop->boot_tgt.lun_online = 1; 222 } 223 } 224 } 225 rw_exit(&isp->sess_lun_list_rwlock); 226 227 return (rtn); 228 } 229 230 void 231 iscsi_lun_hold(iscsi_lun_t *ilp) 232 { 233 mutex_enter(&ilp->lun_mutex); 234 /* 235 * By design lun_refcnt should never be zero when this routine 236 * is called. When the LUN is created the refcnt is set to 1. 237 * If iscsi_lun_rele is called and the refcnt goes to zero the 238 * structure will be freed so this method shouldn't be called 239 * afterwards. 240 */ 241 ASSERT(ilp->lun_refcnt > 0); 242 ilp->lun_refcnt++; 243 mutex_exit(&ilp->lun_mutex); 244 } 245 246 void 247 iscsi_lun_rele(iscsi_lun_t *ilp) 248 { 249 ASSERT(ilp != NULL); 250 251 mutex_enter(&ilp->lun_mutex); 252 ASSERT(ilp->lun_refcnt > 0); 253 if (--ilp->lun_refcnt == 0) { 254 iscsi_sess_t *isp; 255 256 isp = ilp->lun_sess; 257 ASSERT(isp != NULL); 258 259 /* ---- release its memory ---- */ 260 kmem_free(ilp->lun_addr, (strlen((char *)isp->sess_name) + 261 ADDR_EXT_SIZE + 1)); 262 263 if (ilp->lun_guid != NULL) { 264 kmem_free(ilp->lun_guid, ilp->lun_guid_size); 265 } 266 mutex_destroy(&ilp->lun_mutex); 267 kmem_free(ilp, sizeof (iscsi_lun_t)); 268 } else { 269 mutex_exit(&ilp->lun_mutex); 270 } 271 } 272 273 /* 274 * iscsi_lun_cmd_cancel -- as the name implies, cancel all commands for the lun 275 * 276 * This code is similar to the timeout function with a lot less checking of 277 * state before sending the ABORT event for commands on the pending queue. 278 * 279 * This function is only used by iscsi_lun_destroy(). 280 */ 281 static void 282 iscsi_lun_cmd_cancel(iscsi_lun_t *ilp) 283 { 284 iscsi_sess_t *isp; 285 iscsi_cmd_t *icmdp, *nicmdp; 286 287 isp = ilp->lun_sess; 288 rw_enter(&isp->sess_state_rwlock, RW_READER); 289 mutex_enter(&isp->sess_queue_pending.mutex); 290 for (icmdp = isp->sess_queue_pending.head; 291 icmdp; icmdp = nicmdp) { 292 nicmdp = icmdp->cmd_next; 293 294 /* 295 * For commands on the pending queue we can go straight 296 * to and abort request which will free the command 297 * and call back to the complete function. 298 */ 299 iscsi_cmd_state_machine(icmdp, ISCSI_CMD_EVENT_E4, isp); 300 } 301 mutex_exit(&isp->sess_queue_pending.mutex); 302 rw_exit(&isp->sess_state_rwlock); 303 } 304 305 /* 306 * iscsi_lun_destroy - offline and remove lun 307 * 308 * This interface is called when a name service change has 309 * occured and the storage is no longer available to this 310 * initiator. This function will offline and free the 311 * solaris node resources. Then it will free all iscsi lun 312 * resources. 313 * 314 * This function can fail with ISCSI_STATUS_BUSY if the 315 * logical unit is in use. The user should unmount or 316 * close the device and perform the nameservice operation 317 * again if this occurs. 318 */ 319 iscsi_status_t 320 iscsi_lun_destroy(iscsi_hba_t *ihp, iscsi_lun_t *ilp) 321 { 322 iscsi_status_t status = ISCSI_STATUS_SUCCESS; 323 iscsi_sess_t *isp = NULL; 324 iscsi_lun_t *t_ilp = NULL; 325 326 ASSERT(ilp != NULL); 327 isp = ilp->lun_sess; 328 ASSERT(isp != NULL); 329 330 /* flush all outstanding commands first */ 331 iscsi_lun_cmd_cancel(ilp); 332 333 /* attempt to offline and free solaris node */ 334 status = iscsi_lun_offline(ihp, ilp, B_TRUE); 335 336 /* If we successfully unplumbed the lun remove it from our lists */ 337 if (ISCSI_SUCCESS(status)) { 338 if (isp->sess_lun_list == ilp) { 339 /* target first item in list */ 340 isp->sess_lun_list = ilp->lun_next; 341 } else { 342 /* 343 * search session list for ilp pointing 344 * to lun being removed. Then 345 * update that luns next pointer. 346 */ 347 t_ilp = isp->sess_lun_list; 348 while (t_ilp->lun_next != NULL) { 349 if (t_ilp->lun_next == ilp) { 350 break; 351 } 352 t_ilp = t_ilp->lun_next; 353 } 354 if (t_ilp->lun_next == ilp) { 355 t_ilp->lun_next = ilp->lun_next; 356 } else { 357 /* couldn't find session */ 358 ASSERT(FALSE); 359 } 360 } 361 362 iscsi_lun_rele(ilp); 363 } 364 365 return (status); 366 } 367 368 /* 369 * +--------------------------------------------------------------------+ 370 * | External Logical Unit Interfaces | 371 * +--------------------------------------------------------------------+ 372 */ 373 374 /* 375 * iscsi_lun_virt_create - Creates solaris logical unit via MDI 376 */ 377 static iscsi_status_t 378 iscsi_lun_virt_create(iscsi_sess_t *isp, uint16_t lun_num, iscsi_lun_t *ilp, 379 struct scsi_inquiry *inq) 380 { 381 iscsi_status_t rtn = ISCSI_STATUS_INTERNAL_ERROR; 382 int mdi_rtn = MDI_FAILURE; 383 iscsi_hba_t *ihp = NULL; 384 mdi_pathinfo_t *pip = NULL; 385 char *nodename = NULL; 386 char **compatible = NULL; 387 int ncompatible = 0; 388 389 ASSERT(isp != NULL); 390 ASSERT(ilp != NULL); 391 ihp = isp->sess_hba; 392 ASSERT(ihp != NULL); 393 394 /* 395 * Generate compatible property 396 */ 397 scsi_hba_nodename_compatible_get(inq, "vhci", 398 inq->inq_dtype, NULL, &nodename, &compatible, &ncompatible); 399 400 /* if nodename can't be determined then print a message and skip it */ 401 if (nodename == NULL) { 402 cmn_err(CE_WARN, "iscsi driver found no compatible driver " 403 "for %s lun %d dtype:0x%02x", isp->sess_name, lun_num, 404 inq->inq_dtype); 405 return (ISCSI_STATUS_INTERNAL_ERROR); 406 } 407 408 /* 409 * 410 */ 411 ndi_devi_enter(scsi_vhci_dip); 412 mdi_rtn = mdi_pi_alloc_compatible(ihp->hba_dip, nodename, 413 ilp->lun_guid, ilp->lun_addr, compatible, ncompatible, 414 0, &pip); 415 416 if (mdi_rtn == MDI_SUCCESS) { 417 mdi_pi_set_phci_private(pip, (caddr_t)ilp); 418 419 if (mdi_prop_update_string(pip, MDI_GUID, 420 ilp->lun_guid) != DDI_SUCCESS) { 421 cmn_err(CE_WARN, "iscsi driver unable to create " 422 "property for %s lun %d (MDI_GUID)", 423 isp->sess_name, lun_num); 424 mdi_rtn = MDI_FAILURE; 425 goto virt_create_done; 426 } 427 428 if (mdi_prop_update_int(pip, TARGET_PROP, 429 isp->sess_oid) != DDI_SUCCESS) { 430 cmn_err(CE_WARN, "iscsi driver unable to create " 431 "property for %s lun %d (TARGET_PROP)", 432 isp->sess_name, lun_num); 433 mdi_rtn = MDI_FAILURE; 434 goto virt_create_done; 435 } 436 437 if (mdi_prop_update_int(pip, LUN_PROP, 438 ilp->lun_num) != DDI_SUCCESS) { 439 cmn_err(CE_WARN, "iscsi driver unable to create " 440 "property for %s lun %d (LUN_PROP)", 441 isp->sess_name, lun_num); 442 mdi_rtn = MDI_FAILURE; 443 goto virt_create_done; 444 } 445 446 if (mdi_prop_update_string_array(pip, "compatible", 447 compatible, ncompatible) != 448 DDI_PROP_SUCCESS) { 449 cmn_err(CE_WARN, "iscsi driver unable to create " 450 "property for %s lun %d (COMPATIBLE)", 451 isp->sess_name, lun_num); 452 mdi_rtn = MDI_FAILURE; 453 goto virt_create_done; 454 } 455 456 mdi_rtn = mdi_pi_online(pip, 0); 457 if (mdi_rtn == MDI_NOT_SUPPORTED) { 458 mdi_rtn = MDI_FAILURE; 459 goto virt_create_done; 460 } 461 462 ilp->lun_pip = pip; 463 ilp->lun_dip = NULL; 464 465 virt_create_done: 466 467 if (pip && mdi_rtn != MDI_SUCCESS) { 468 ilp->lun_pip = NULL; 469 ilp->lun_dip = NULL; 470 (void) mdi_prop_remove(pip, NULL); 471 (void) mdi_pi_free(pip, 0); 472 } else { 473 rtn = ISCSI_STATUS_SUCCESS; 474 } 475 } 476 ndi_devi_exit(scsi_vhci_dip); 477 478 scsi_hba_nodename_compatible_free(nodename, compatible); 479 480 return (rtn); 481 } 482 483 484 /* 485 * iscsi_lun_phys_create - creates solaris logical unit via NDI 486 */ 487 static iscsi_status_t 488 iscsi_lun_phys_create(iscsi_sess_t *isp, uint16_t lun_num, 489 iscsi_lun_t *ilp, struct scsi_inquiry *inq) 490 { 491 iscsi_status_t rtn = ISCSI_STATUS_INTERNAL_ERROR; 492 int ndi_rtn = NDI_FAILURE; 493 iscsi_hba_t *ihp = NULL; 494 dev_info_t *lun_dip = NULL; 495 char *nodename = NULL; 496 char **compatible = NULL; 497 int ncompatible = 0; 498 char *scsi_binding_set = NULL; 499 char instance[32]; 500 501 ASSERT(isp != NULL); 502 ASSERT(ilp != NULL); 503 ihp = isp->sess_hba; 504 ASSERT(ihp != NULL); 505 ASSERT(inq != NULL); 506 507 /* get the 'scsi-binding-set' property */ 508 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, isp->sess_hba->hba_dip, 509 DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, "scsi-binding-set", 510 &scsi_binding_set) != DDI_PROP_SUCCESS) { 511 scsi_binding_set = NULL; 512 } 513 514 /* generate compatible property */ 515 scsi_hba_nodename_compatible_get(inq, scsi_binding_set, 516 inq->inq_dtype, NULL, &nodename, &compatible, &ncompatible); 517 if (scsi_binding_set) 518 ddi_prop_free(scsi_binding_set); 519 520 /* if nodename can't be determined then print a message and skip it */ 521 if (nodename == NULL) { 522 cmn_err(CE_WARN, "iscsi driver found no compatible driver " 523 "for %s lun %d", isp->sess_name, lun_num); 524 return (ISCSI_STATUS_INTERNAL_ERROR); 525 } 526 527 ndi_devi_enter(ihp->hba_dip); 528 529 ndi_rtn = ndi_devi_alloc(ihp->hba_dip, nodename, 530 DEVI_SID_NODEID, &lun_dip); 531 532 /* if lun alloc success, set props */ 533 if (ndi_rtn == NDI_SUCCESS) { 534 535 if (ndi_prop_update_int(DDI_DEV_T_NONE, 536 lun_dip, TARGET_PROP, (int)isp->sess_oid) != 537 DDI_PROP_SUCCESS) { 538 cmn_err(CE_WARN, "iscsi driver unable to create " 539 "property for %s lun %d (TARGET_PROP)", 540 isp->sess_name, lun_num); 541 ndi_rtn = NDI_FAILURE; 542 goto phys_create_done; 543 } 544 545 if (ndi_prop_update_int(DDI_DEV_T_NONE, 546 lun_dip, LUN_PROP, (int)ilp->lun_num) != 547 DDI_PROP_SUCCESS) { 548 cmn_err(CE_WARN, "iscsi driver unable to create " 549 "property for %s lun %d (LUN_PROP)", 550 isp->sess_name, lun_num); 551 ndi_rtn = NDI_FAILURE; 552 goto phys_create_done; 553 } 554 555 if (ndi_prop_update_string_array(DDI_DEV_T_NONE, 556 lun_dip, "compatible", compatible, ncompatible) 557 != DDI_PROP_SUCCESS) { 558 cmn_err(CE_WARN, "iscsi driver unable to create " 559 "property for %s lun %d (COMPATIBLE)", 560 isp->sess_name, lun_num); 561 ndi_rtn = NDI_FAILURE; 562 goto phys_create_done; 563 } 564 565 phys_create_done: 566 /* If props were setup ok, online the lun */ 567 if (ndi_rtn == NDI_SUCCESS) { 568 /* Try to online the new node */ 569 ndi_rtn = ndi_devi_online(lun_dip, 0); 570 } 571 572 /* If success set rtn flag, else unwire alloc'd lun */ 573 if (ndi_rtn == NDI_SUCCESS) { 574 rtn = ISCSI_STATUS_SUCCESS; 575 /* 576 * Assign the instance number for the dev_link 577 * generator. This will ensure the link name is 578 * unique and persistent across reboots. 579 */ 580 (void) snprintf(instance, 32, "%d", 581 ddi_get_instance(lun_dip)); 582 (void) ndi_prop_update_string(DDI_DEV_T_NONE, 583 lun_dip, NDI_GUID, instance); 584 } else { 585 cmn_err(CE_WARN, "iscsi driver unable to online " 586 "%s lun %d", isp->sess_name, lun_num); 587 ndi_prop_remove_all(lun_dip); 588 (void) ndi_devi_free(lun_dip); 589 } 590 591 } 592 ndi_devi_exit(ihp->hba_dip); 593 594 ilp->lun_dip = lun_dip; 595 ilp->lun_pip = NULL; 596 597 scsi_hba_nodename_compatible_free(nodename, compatible); 598 599 return (rtn); 600 } 601 602 603 /* 604 * iscsi_lun_online - _di_online logical unit 605 * 606 * This is called after a path has recovered it will cause 607 * an offline path to become online/active again. 608 */ 609 void 610 iscsi_lun_online(iscsi_hba_t *ihp, iscsi_lun_t *ilp) 611 { 612 int rval = 0; 613 uint64_t *lun_num_ptr = NULL; 614 uint16_t boot_lun_num = 0; 615 iscsi_sess_t *isp = NULL; 616 boolean_t online = B_FALSE; 617 nvlist_t *attr_list = NULL; 618 char *pathname = NULL; 619 dev_info_t *lun_dip = NULL; 620 621 ASSERT(ilp != NULL); 622 ASSERT((ilp->lun_pip != NULL) || (ilp->lun_dip != NULL)); 623 624 if (ilp->lun_pip != NULL) { 625 ndi_devi_enter(scsi_vhci_dip); 626 rval = mdi_pi_online(ilp->lun_pip, 0); 627 ndi_devi_exit(scsi_vhci_dip); 628 if (rval == MDI_SUCCESS) { 629 ilp->lun_state &= ISCSI_LUN_STATE_CLEAR; 630 ilp->lun_state |= ISCSI_LUN_STATE_ONLINE; 631 ilp->lun_time_online = ddi_get_time(); 632 online = B_TRUE; 633 } 634 635 } else if (ilp->lun_dip != NULL) { 636 ndi_devi_enter(ihp->hba_dip); 637 rval = ndi_devi_online(ilp->lun_dip, 0); 638 ndi_devi_exit(ihp->hba_dip); 639 if (rval == NDI_SUCCESS) { 640 ilp->lun_state &= ISCSI_LUN_STATE_CLEAR; 641 ilp->lun_state |= ISCSI_LUN_STATE_ONLINE; 642 ilp->lun_time_online = ddi_get_time(); 643 online = B_TRUE; 644 } 645 } 646 647 /* Check whether this is the required LUN for iscsi boot */ 648 if (iscsiboot_prop != NULL && 649 iscsiboot_prop->boot_tgt.lun_online == 0) { 650 isp = ilp->lun_sess; 651 if (isp->sess_boot == B_TRUE) { 652 lun_num_ptr = 653 (uint64_t *)iscsiboot_prop->boot_tgt.tgt_boot_lun; 654 boot_lun_num = (uint16_t)(*lun_num_ptr); 655 if (boot_lun_num == ilp->lun_num) { 656 /* 657 * During iscsi boot, the boot lun has been 658 * online, we should set the "online flag". 659 */ 660 iscsiboot_prop->boot_tgt.lun_online = 1; 661 } 662 } 663 } 664 665 /* 666 * If the LUN has been online and it is a disk, 667 * send out a system event. 668 */ 669 if (online == B_TRUE && ilp->lun_type == DTYPE_DIRECT) { 670 if (nvlist_alloc(&attr_list, NV_UNIQUE_NAME_TYPE, KM_SLEEP) != 671 DDI_SUCCESS) { 672 return; 673 } 674 675 if (ilp->lun_pip != NULL) { 676 lun_dip = mdi_pi_get_client(ilp->lun_pip); 677 } else { 678 lun_dip = ilp->lun_dip; 679 } 680 681 pathname = kmem_zalloc(MAXNAMELEN + 1, KM_SLEEP); 682 (void) ddi_pathname(lun_dip, pathname); 683 684 if (nvlist_add_string(attr_list, DEV_PHYS_PATH, pathname) != 685 DDI_SUCCESS) { 686 nvlist_free(attr_list); 687 kmem_free(pathname, MAXNAMELEN + 1); 688 return; 689 } 690 iscsi_send_sysevent(ihp, EC_DEV_ADD, ESC_DISK, attr_list); 691 kmem_free(pathname, MAXNAMELEN + 1); 692 nvlist_free(attr_list); 693 } 694 } 695 696 /* 697 * iscsi_lun_offline - attempt _di_offline [and optional _di_free] 698 * 699 * This function is called via two paths. When a transport 700 * path has failed it will be called to offline the logical 701 * unit. When nameservice access has been removed it will 702 * be called to both offline and free the logical unit. 703 * (This operates soley on the solaris node states. 704 * iscsi_lun_destroy() should be called when attempting 705 * to free all iscsi lun resources.) 706 * 707 * This function can fail with ISCSI_STATUS_BUSY if the 708 * logical unit is in use. The user should unmount or 709 * close the device and perform the nameservice operation 710 * again if this occurs. 711 * 712 * If we fail to offline a LUN that we don't want to destroy, 713 * we will mark it with invalid state. If this LUN still 714 * exists on the target, we can have another chance to online 715 * it again when we do the LUN enumeration. 716 */ 717 iscsi_status_t 718 iscsi_lun_offline(iscsi_hba_t *ihp, iscsi_lun_t *ilp, boolean_t lun_free) 719 { 720 iscsi_status_t status = ISCSI_STATUS_SUCCESS; 721 dev_info_t *cdip; 722 char *pathname = NULL; 723 boolean_t offline = B_FALSE; 724 nvlist_t *attr_list = NULL; 725 726 ASSERT(ilp != NULL); 727 ASSERT((ilp->lun_pip != NULL) || (ilp->lun_dip != NULL)); 728 729 if (ilp->lun_pip == NULL) 730 cdip = ilp->lun_dip; 731 else 732 cdip = mdi_pi_get_client(ilp->lun_pip); 733 734 if (cdip != NULL && ilp->lun_type == DTYPE_DIRECT) { 735 pathname = kmem_zalloc(MAXNAMELEN + 1, KM_SLEEP); 736 (void) ddi_pathname(cdip, pathname); 737 } 738 739 /* Attempt to offline the logical units */ 740 if (ilp->lun_pip != NULL) { 741 /* virt/mdi */ 742 ndi_devi_enter(scsi_vhci_dip); 743 if (mdi_pi_offline(ilp->lun_pip, 0) == MDI_SUCCESS) { 744 ilp->lun_state &= ISCSI_LUN_STATE_CLEAR; 745 ilp->lun_state |= ISCSI_LUN_STATE_OFFLINE; 746 if (lun_free == B_TRUE) { 747 (void) mdi_prop_remove(ilp->lun_pip, NULL); 748 (void) mdi_pi_free(ilp->lun_pip, 0); 749 } 750 offline = B_TRUE; 751 } else { 752 status = ISCSI_STATUS_BUSY; 753 if (lun_free == B_FALSE) { 754 ilp->lun_state |= ISCSI_LUN_STATE_INVALID; 755 offline = B_TRUE; 756 } 757 } 758 ndi_devi_exit(scsi_vhci_dip); 759 760 } else { 761 /* phys/ndi */ 762 int flags = NDI_DEVFS_CLEAN; 763 764 ndi_devi_enter(ihp->hba_dip); 765 if (lun_free == B_TRUE && 766 (ilp->lun_state & ISCSI_LUN_STATE_ONLINE)) 767 flags |= NDI_DEVI_REMOVE; 768 if (ndi_devi_offline(ilp->lun_dip, flags) != NDI_SUCCESS) { 769 status = ISCSI_STATUS_BUSY; 770 if (lun_free == B_FALSE) { 771 ilp->lun_state |= ISCSI_LUN_STATE_INVALID; 772 offline = B_TRUE; 773 } 774 } else { 775 ilp->lun_state &= ISCSI_LUN_STATE_CLEAR; 776 ilp->lun_state |= ISCSI_LUN_STATE_OFFLINE; 777 offline = B_TRUE; 778 } 779 ndi_devi_exit(ihp->hba_dip); 780 } 781 782 if (offline == B_TRUE && pathname != NULL && 783 ilp->lun_type == DTYPE_DIRECT) { 784 if (nvlist_alloc(&attr_list, NV_UNIQUE_NAME_TYPE, KM_SLEEP) != 785 DDI_SUCCESS) { 786 kmem_free(pathname, MAXNAMELEN + 1); 787 return (status); 788 } 789 790 if (nvlist_add_string(attr_list, DEV_PHYS_PATH, pathname) != 791 DDI_SUCCESS) { 792 nvlist_free(attr_list); 793 kmem_free(pathname, MAXNAMELEN + 1); 794 return (status); 795 } 796 797 iscsi_send_sysevent(ihp, EC_DEV_REMOVE, ESC_DISK, attr_list); 798 nvlist_free(attr_list); 799 } 800 801 if (pathname != NULL) { 802 kmem_free(pathname, MAXNAMELEN + 1); 803 } 804 805 return (status); 806 } 807