1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * 25 * iSCSI logical unit interfaces 26 */ 27 28 #include "iscsi.h" 29 #include <sys/fs/dv_node.h> /* devfs_clean */ 30 #include <sys/bootprops.h> 31 32 /* tpgt bytes in string form */ 33 #define TPGT_EXT_SIZE 5 34 35 /* logical unit number bytes in string form */ 36 #define LUN_EXT_SIZE 10 37 38 /* 39 * Addition addr size of size of ',' + max str form of tpgt (2 bytes) + 40 * ',' + max str form of logical unit number (4 bytes). 41 */ 42 #define ADDR_EXT_SIZE (1 + TPGT_EXT_SIZE + 1 + LUN_EXT_SIZE) 43 44 /* internal interfaces */ 45 static iscsi_status_t iscsi_lun_virt_create(iscsi_sess_t *isp, 46 uint16_t lun_num, iscsi_lun_t *ilp, struct scsi_inquiry *inq); 47 static iscsi_status_t iscsi_lun_phys_create(iscsi_sess_t *isp, 48 uint16_t lun_num, iscsi_lun_t *ilp, struct scsi_inquiry *inq); 49 50 extern dev_info_t *scsi_vhci_dip; 51 extern ib_boot_prop_t *iscsiboot_prop; 52 53 /* 54 * +--------------------------------------------------------------------+ 55 * | External Connection Interfaces | 56 * +--------------------------------------------------------------------+ 57 */ 58 59 60 /* 61 * iscsi_lun_create - This function will create a lun mapping. 62 * logic specific to MPxIO vs. NDI node creation is switched 63 * out to a helper function. 64 */ 65 iscsi_status_t 66 iscsi_lun_create(iscsi_sess_t *isp, uint16_t lun_num, uint8_t lun_addr_type, 67 struct scsi_inquiry *inq, char *guid) 68 { 69 iscsi_status_t rtn = ISCSI_STATUS_INTERNAL_ERROR; 70 iscsi_hba_t *ihp = NULL; 71 iscsi_lun_t *ilp = NULL; 72 iscsi_lun_t *ilp_tmp = NULL; 73 char *addr = NULL; 74 uint16_t boot_lun_num = 0; 75 uint64_t *lun_num_ptr = NULL; 76 77 ASSERT(isp != NULL); 78 ihp = isp->sess_hba; 79 ASSERT(ihp != NULL); 80 81 addr = kmem_zalloc((strlen((char *)isp->sess_name) + 82 ADDR_EXT_SIZE + 1), KM_SLEEP); 83 (void) snprintf(addr, 84 (strlen((char *)isp->sess_name) + 85 ADDR_EXT_SIZE + 1), 86 "%02X%02X%s%04X,%d", isp->sess_isid[4], 87 isp->sess_isid[5], isp->sess_name, 88 isp->sess_tpgt_nego & 0xFFFF, lun_num); 89 90 /* allocate space for lun struct */ 91 ilp = kmem_zalloc(sizeof (iscsi_lun_t), KM_SLEEP); 92 ilp->lun_sig = ISCSI_SIG_LUN; 93 ilp->lun_state = ISCSI_LUN_STATE_OFFLINE; 94 95 /* initialize common LU information */ 96 ilp->lun_num = lun_num; 97 ilp->lun_addr_type = lun_addr_type; 98 ilp->lun_sess = isp; 99 ilp->lun_addr = addr; 100 101 mutex_enter(&iscsi_oid_mutex); 102 ilp->lun_oid = iscsi_oid++; 103 mutex_exit(&iscsi_oid_mutex); 104 105 bcopy(inq->inq_vid, ilp->lun_vid, sizeof (inq->inq_vid)); 106 bcopy(inq->inq_pid, ilp->lun_pid, sizeof (inq->inq_pid)); 107 108 /* store GUID if valid one exists */ 109 if (guid != NULL) { 110 ilp->lun_guid_size = strlen(guid) + 1; 111 ilp->lun_guid = kmem_zalloc(ilp->lun_guid_size, KM_SLEEP); 112 (void) strcpy(ilp->lun_guid, guid); 113 } else { 114 ilp->lun_guid_size = 0; 115 ilp->lun_guid = NULL; 116 } 117 118 /* 119 * We need to add the lun to our lists now because during the 120 * lun creation we will get called back into multiple times 121 * depending on the createion type. These callbacks will 122 * occur via our tran_init_lun, tran_get_name, tran_get_bus_addr, 123 * tran_init_pkt, tran_start. 124 */ 125 rw_enter(&isp->sess_lun_list_rwlock, RW_WRITER); 126 if (isp->sess_lun_list == NULL) { 127 isp->sess_lun_list = ilp; 128 } else { 129 ilp->lun_next = isp->sess_lun_list; 130 isp->sess_lun_list = ilp; 131 } 132 133 /* Attempt to create a scsi_vhci binding if GUID is available */ 134 if ((ihp->hba_mpxio_enabled == B_TRUE) && 135 (guid != NULL)) { 136 rtn = iscsi_lun_virt_create(isp, lun_num, ilp, inq); 137 } 138 if (!ISCSI_SUCCESS(rtn)) { 139 /* unable to bind under scsi_vhci, failback to ndi */ 140 rtn = iscsi_lun_phys_create(isp, lun_num, ilp, inq); 141 } 142 143 /* 144 * If NOT successful we need to remove the lun from the 145 * session and free any related resources. 146 */ 147 if (!ISCSI_SUCCESS(rtn)) { 148 if (ilp == isp->sess_lun_list) { 149 /* if head, set head to our next */ 150 isp->sess_lun_list = ilp->lun_next; 151 } else { 152 /* if not head, set prev lun's next to our next */ 153 for (ilp_tmp = isp->sess_lun_list; ilp_tmp; 154 ilp_tmp = ilp_tmp->lun_next) { 155 if (ilp_tmp->lun_next == ilp) { 156 ilp_tmp->lun_next = ilp->lun_next; 157 break; 158 } 159 } 160 } 161 162 kmem_free(ilp->lun_addr, 163 (strlen((char *)isp->sess_name) + 164 ADDR_EXT_SIZE + 1)); 165 ilp->lun_addr = NULL; 166 167 if (ilp->lun_guid != NULL) { 168 kmem_free(ilp->lun_guid, ilp->lun_guid_size); 169 ilp->lun_guid = NULL; 170 } 171 kmem_free(ilp, sizeof (iscsi_lun_t)); 172 } else { 173 ilp->lun_state = ISCSI_LUN_STATE_ONLINE; 174 ilp->lun_time_online = ddi_get_time(); 175 176 /* Check whether this is the required LUN for iscsi boot */ 177 if (iscsiboot_prop != NULL && isp->sess_boot == B_TRUE && 178 iscsiboot_prop->boot_tgt.lun_online == 0) { 179 lun_num_ptr = 180 (uint64_t *)iscsiboot_prop->boot_tgt.tgt_boot_lun; 181 boot_lun_num = (uint16_t)(*lun_num_ptr); 182 if (boot_lun_num == ilp->lun_num) { 183 /* 184 * During iscsi boot, the boot lun has been 185 * online, we should set the "online flag". 186 */ 187 iscsiboot_prop->boot_tgt.lun_online = 1; 188 } 189 } 190 } 191 rw_exit(&isp->sess_lun_list_rwlock); 192 193 return (rtn); 194 } 195 196 /* 197 * iscsi_lun_destroy - offline and remove lun 198 * 199 * This interface is called when a name service change has 200 * occured and the storage is no longer available to this 201 * initiator. This function will offline and free the 202 * solaris node resources. Then it will free all iscsi lun 203 * resources. 204 * 205 * This function can fail with ISCSI_STATUS_BUSY if the 206 * logical unit is in use. The user should unmount or 207 * close the device and perform the nameservice operation 208 * again if this occurs. 209 */ 210 iscsi_status_t 211 iscsi_lun_destroy(iscsi_hba_t *ihp, iscsi_lun_t *ilp) 212 { 213 iscsi_status_t status = ISCSI_STATUS_SUCCESS; 214 iscsi_sess_t *isp = NULL; 215 iscsi_lun_t *t_ilp = NULL; 216 217 ASSERT(ilp != NULL); 218 isp = ilp->lun_sess; 219 ASSERT(isp != NULL); 220 221 /* attempt to offline and free solaris node */ 222 status = iscsi_lun_offline(ihp, ilp, B_TRUE); 223 224 /* If we successfully unplumbed the lun remove it from our lists */ 225 if (ISCSI_SUCCESS(status)) { 226 if (isp->sess_lun_list == ilp) { 227 /* target first item in list */ 228 isp->sess_lun_list = ilp->lun_next; 229 } else { 230 /* 231 * search session list for ilp pointing 232 * to lun being removed. Then 233 * update that luns next pointer. 234 */ 235 t_ilp = isp->sess_lun_list; 236 while (t_ilp->lun_next != NULL) { 237 if (t_ilp->lun_next == ilp) { 238 break; 239 } 240 t_ilp = t_ilp->lun_next; 241 } 242 if (t_ilp->lun_next == ilp) { 243 t_ilp->lun_next = ilp->lun_next; 244 } else { 245 /* couldn't find session */ 246 ASSERT(FALSE); 247 } 248 } 249 250 /* release its memory */ 251 kmem_free(ilp->lun_addr, (strlen((char *)isp->sess_name) + 252 ADDR_EXT_SIZE + 1)); 253 ilp->lun_addr = NULL; 254 if (ilp->lun_guid != NULL) { 255 kmem_free(ilp->lun_guid, ilp->lun_guid_size); 256 ilp->lun_guid = NULL; 257 } 258 kmem_free(ilp, sizeof (iscsi_lun_t)); 259 ilp = NULL; 260 } 261 262 return (status); 263 } 264 265 /* 266 * +--------------------------------------------------------------------+ 267 * | External Logical Unit Interfaces | 268 * +--------------------------------------------------------------------+ 269 */ 270 271 /* 272 * iscsi_lun_virt_create - Creates solaris logical unit via MDI 273 */ 274 static iscsi_status_t 275 iscsi_lun_virt_create(iscsi_sess_t *isp, uint16_t lun_num, iscsi_lun_t *ilp, 276 struct scsi_inquiry *inq) 277 { 278 iscsi_status_t rtn = ISCSI_STATUS_INTERNAL_ERROR; 279 int mdi_rtn = MDI_FAILURE; 280 iscsi_hba_t *ihp = NULL; 281 mdi_pathinfo_t *pip = NULL; 282 char *nodename = NULL; 283 char **compatible = NULL; 284 int ncompatible = 0; 285 int circ = 0; 286 287 ASSERT(isp != NULL); 288 ASSERT(ilp != NULL); 289 ihp = isp->sess_hba; 290 ASSERT(ihp != NULL); 291 292 /* 293 * Generate compatible property 294 */ 295 scsi_hba_nodename_compatible_get(inq, "vhci", 296 inq->inq_dtype, NULL, &nodename, &compatible, &ncompatible); 297 298 /* if nodename can't be determined then print a message and skip it */ 299 if (nodename == NULL) { 300 cmn_err(CE_WARN, "iscsi driver found no compatible driver " 301 "for %s lun %d dtype:0x%02x", isp->sess_name, lun_num, 302 inq->inq_dtype); 303 return (ISCSI_STATUS_INTERNAL_ERROR); 304 } 305 306 /* 307 * 308 */ 309 ndi_devi_enter(scsi_vhci_dip, &circ); 310 mdi_rtn = mdi_pi_alloc_compatible(ihp->hba_dip, nodename, 311 ilp->lun_guid, ilp->lun_addr, compatible, ncompatible, 312 0, &pip); 313 314 if (mdi_rtn == MDI_SUCCESS) { 315 mdi_pi_set_phci_private(pip, (caddr_t)ilp); 316 317 if (mdi_prop_update_string(pip, MDI_GUID, 318 ilp->lun_guid) != DDI_SUCCESS) { 319 cmn_err(CE_WARN, "iscsi driver unable to create " 320 "property for %s lun %d (MDI_GUID)", 321 isp->sess_name, lun_num); 322 mdi_rtn = MDI_FAILURE; 323 goto virt_create_done; 324 } 325 326 if (mdi_prop_update_int(pip, TARGET_PROP, 327 isp->sess_oid) != DDI_SUCCESS) { 328 cmn_err(CE_WARN, "iscsi driver unable to create " 329 "property for %s lun %d (TARGET_PROP)", 330 isp->sess_name, lun_num); 331 mdi_rtn = MDI_FAILURE; 332 goto virt_create_done; 333 } 334 335 if (mdi_prop_update_int(pip, LUN_PROP, 336 ilp->lun_num) != DDI_SUCCESS) { 337 cmn_err(CE_WARN, "iscsi driver unable to create " 338 "property for %s lun %d (LUN_PROP)", 339 isp->sess_name, lun_num); 340 mdi_rtn = MDI_FAILURE; 341 goto virt_create_done; 342 } 343 344 if (mdi_prop_update_string_array(pip, "compatible", 345 compatible, ncompatible) != 346 DDI_PROP_SUCCESS) { 347 cmn_err(CE_WARN, "iscsi driver unable to create " 348 "property for %s lun %d (COMPATIBLE)", 349 isp->sess_name, lun_num); 350 mdi_rtn = MDI_FAILURE; 351 goto virt_create_done; 352 } 353 354 mdi_rtn = mdi_pi_online(pip, 0); 355 if (mdi_rtn == MDI_NOT_SUPPORTED) { 356 mdi_rtn = MDI_FAILURE; 357 goto virt_create_done; 358 } 359 360 ilp->lun_pip = pip; 361 ilp->lun_dip = NULL; 362 363 virt_create_done: 364 365 if (pip && mdi_rtn != MDI_SUCCESS) { 366 ilp->lun_pip = NULL; 367 ilp->lun_dip = NULL; 368 (void) mdi_prop_remove(pip, NULL); 369 (void) mdi_pi_free(pip, 0); 370 } else { 371 rtn = ISCSI_STATUS_SUCCESS; 372 } 373 } 374 ndi_devi_exit(scsi_vhci_dip, circ); 375 376 scsi_hba_nodename_compatible_free(nodename, compatible); 377 378 return (rtn); 379 } 380 381 382 /* 383 * iscsi_lun_phys_create - creates solaris logical unit via NDI 384 */ 385 static iscsi_status_t 386 iscsi_lun_phys_create(iscsi_sess_t *isp, uint16_t lun_num, 387 iscsi_lun_t *ilp, struct scsi_inquiry *inq) 388 { 389 iscsi_status_t rtn = ISCSI_STATUS_INTERNAL_ERROR; 390 int ndi_rtn = NDI_FAILURE; 391 iscsi_hba_t *ihp = NULL; 392 dev_info_t *lun_dip = NULL; 393 char *nodename = NULL; 394 char **compatible = NULL; 395 int ncompatible = 0; 396 char *scsi_binding_set = NULL; 397 char instance[32]; 398 int circ = 0; 399 400 ASSERT(isp != NULL); 401 ASSERT(ilp != NULL); 402 ihp = isp->sess_hba; 403 ASSERT(ihp != NULL); 404 ASSERT(inq != NULL); 405 406 /* get the 'scsi-binding-set' property */ 407 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, isp->sess_hba->hba_dip, 408 DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, "scsi-binding-set", 409 &scsi_binding_set) != DDI_PROP_SUCCESS) { 410 scsi_binding_set = NULL; 411 } 412 413 /* generate compatible property */ 414 scsi_hba_nodename_compatible_get(inq, scsi_binding_set, 415 inq->inq_dtype, NULL, &nodename, &compatible, &ncompatible); 416 if (scsi_binding_set) 417 ddi_prop_free(scsi_binding_set); 418 419 /* if nodename can't be determined then print a message and skip it */ 420 if (nodename == NULL) { 421 cmn_err(CE_WARN, "iscsi driver found no compatible driver " 422 "for %s lun %d", isp->sess_name, lun_num); 423 return (ISCSI_STATUS_INTERNAL_ERROR); 424 } 425 426 ndi_devi_enter(ihp->hba_dip, &circ); 427 428 ndi_rtn = ndi_devi_alloc(ihp->hba_dip, nodename, 429 DEVI_SID_NODEID, &lun_dip); 430 431 /* if lun alloc success, set props */ 432 if (ndi_rtn == NDI_SUCCESS) { 433 434 if (ndi_prop_update_int(DDI_DEV_T_NONE, 435 lun_dip, TARGET_PROP, (int)isp->sess_oid) != 436 DDI_PROP_SUCCESS) { 437 cmn_err(CE_WARN, "iscsi driver unable to create " 438 "property for %s lun %d (TARGET_PROP)", 439 isp->sess_name, lun_num); 440 ndi_rtn = NDI_FAILURE; 441 goto phys_create_done; 442 } 443 444 if (ndi_prop_update_int(DDI_DEV_T_NONE, 445 lun_dip, LUN_PROP, (int)ilp->lun_num) != 446 DDI_PROP_SUCCESS) { 447 cmn_err(CE_WARN, "iscsi driver unable to create " 448 "property for %s lun %d (LUN_PROP)", 449 isp->sess_name, lun_num); 450 ndi_rtn = NDI_FAILURE; 451 goto phys_create_done; 452 } 453 454 if (ndi_prop_update_string_array(DDI_DEV_T_NONE, 455 lun_dip, "compatible", compatible, ncompatible) 456 != DDI_PROP_SUCCESS) { 457 cmn_err(CE_WARN, "iscsi driver unable to create " 458 "property for %s lun %d (COMPATIBLE)", 459 isp->sess_name, lun_num); 460 ndi_rtn = NDI_FAILURE; 461 goto phys_create_done; 462 } 463 464 phys_create_done: 465 /* If props were setup ok, online the lun */ 466 if (ndi_rtn == NDI_SUCCESS) { 467 /* Try to online the new node */ 468 ndi_rtn = ndi_devi_online(lun_dip, 0); 469 } 470 471 /* If success set rtn flag, else unwire alloc'd lun */ 472 if (ndi_rtn == NDI_SUCCESS) { 473 rtn = ISCSI_STATUS_SUCCESS; 474 /* 475 * Assign the instance number for the dev_link 476 * generator. This will ensure the link name is 477 * unique and persistent across reboots. 478 */ 479 (void) snprintf(instance, 32, "%d", 480 ddi_get_instance(lun_dip)); 481 (void) ndi_prop_update_string(DDI_DEV_T_NONE, 482 lun_dip, NDI_GUID, instance); 483 } else { 484 cmn_err(CE_WARN, "iscsi driver unable to online " 485 "%s lun %d", isp->sess_name, lun_num); 486 ndi_prop_remove_all(lun_dip); 487 (void) ndi_devi_free(lun_dip); 488 } 489 490 } 491 ndi_devi_exit(ihp->hba_dip, circ); 492 493 ilp->lun_dip = lun_dip; 494 ilp->lun_pip = NULL; 495 496 scsi_hba_nodename_compatible_free(nodename, compatible); 497 498 return (rtn); 499 } 500 501 502 /* 503 * iscsi_lun_online - _di_online logical unit 504 * 505 * This is called after a path has recovered it will cause 506 * an offline path to become online/active again. 507 */ 508 void 509 iscsi_lun_online(iscsi_hba_t *ihp, iscsi_lun_t *ilp) 510 { 511 int circ = 0; 512 int rval = 0; 513 uint64_t *lun_num_ptr = NULL; 514 uint16_t boot_lun_num = 0; 515 iscsi_sess_t *isp = NULL; 516 517 ASSERT(ilp != NULL); 518 ASSERT((ilp->lun_pip != NULL) || (ilp->lun_dip != NULL)); 519 520 if (ilp->lun_pip != NULL) { 521 ndi_devi_enter(scsi_vhci_dip, &circ); 522 rval = mdi_pi_online(ilp->lun_pip, 0); 523 ndi_devi_exit(scsi_vhci_dip, circ); 524 if (rval == MDI_SUCCESS) { 525 ilp->lun_state = ISCSI_LUN_STATE_ONLINE; 526 ilp->lun_time_online = ddi_get_time(); 527 } 528 529 } else if (ilp->lun_dip != NULL) { 530 ndi_devi_enter(ihp->hba_dip, &circ); 531 rval = ndi_devi_online(ilp->lun_dip, 0); 532 ndi_devi_exit(ihp->hba_dip, circ); 533 if (rval == NDI_SUCCESS) { 534 ilp->lun_state = ISCSI_LUN_STATE_ONLINE; 535 ilp->lun_time_online = ddi_get_time(); 536 } 537 } 538 539 /* Check whether this is the required LUN for iscsi boot */ 540 if (iscsiboot_prop != NULL && 541 iscsiboot_prop->boot_tgt.lun_online == 0) { 542 isp = ilp->lun_sess; 543 if (isp->sess_boot == B_TRUE) { 544 lun_num_ptr = 545 (uint64_t *)iscsiboot_prop->boot_tgt.tgt_boot_lun; 546 boot_lun_num = (uint16_t)(*lun_num_ptr); 547 if (boot_lun_num == ilp->lun_num) { 548 /* 549 * During iscsi boot, the boot lun has been 550 * online, we should set the "online flag". 551 */ 552 iscsiboot_prop->boot_tgt.lun_online = 1; 553 } 554 } 555 } 556 } 557 558 /* 559 * iscsi_lun_offline - attempt _di_offline [and optional _di_free] 560 * 561 * This function is called via two paths. When a transport 562 * path has failed it will be called to offline the logical 563 * unit. When nameservice access has been removed it will 564 * be called to both offline and free the logical unit. 565 * (This operates soley on the solaris node states. 566 * iscsi_lun_destroy() should be called when attempting 567 * to free all iscsi lun resources.) 568 * 569 * This function can fail with ISCSI_STATUS_BUSY if the 570 * logical unit is in use. The user should unmount or 571 * close the device and perform the nameservice operation 572 * again if this occurs. 573 */ 574 iscsi_status_t 575 iscsi_lun_offline(iscsi_hba_t *ihp, iscsi_lun_t *ilp, boolean_t lun_free) 576 { 577 iscsi_status_t status = ISCSI_STATUS_SUCCESS; 578 int circ = 0; 579 dev_info_t *cdip, *pdip; 580 char *devname; 581 int rval; 582 583 ASSERT(ilp != NULL); 584 ASSERT((ilp->lun_pip != NULL) || (ilp->lun_dip != NULL)); 585 586 /* 587 * Since we carry the logical units parent 588 * lock across the offline call it will not 589 * issue devfs_clean() and may fail with a 590 * devi_ref count > 0. 591 */ 592 if (ilp->lun_pip == NULL) { 593 cdip = ilp->lun_dip; 594 } else { 595 cdip = mdi_pi_get_client(ilp->lun_pip); 596 } 597 598 if ((cdip != NULL) && 599 (lun_free == B_TRUE) && 600 (ilp->lun_state == ISCSI_LUN_STATE_ONLINE)) { 601 /* 602 * Make sure node is attached otherwise 603 * it won't have related cache nodes to 604 * clean up. i_ddi_devi_attached is 605 * similiar to i_ddi_node_state(cdip) >= 606 * DS_ATTACHED. We should clean up only 607 * when lun_free is set. 608 */ 609 if (i_ddi_devi_attached(cdip)) { 610 611 /* Get parent dip */ 612 pdip = ddi_get_parent(cdip); 613 614 /* Get full devname */ 615 devname = kmem_alloc(MAXNAMELEN + 1, KM_SLEEP); 616 ndi_devi_enter(pdip, &circ); 617 (void) ddi_deviname(cdip, devname); 618 /* Release lock before devfs_clean() */ 619 ndi_devi_exit(pdip, circ); 620 621 /* Clean cache */ 622 rval = devfs_clean(pdip, devname + 1, 623 DV_CLEAN_FORCE); 624 kmem_free(devname, MAXNAMELEN + 1); 625 626 if ((rval != 0) && (ilp->lun_pip == NULL)) { 627 return (ISCSI_STATUS_BUSY); 628 } 629 } 630 } 631 632 /* Attempt to offline the logical units */ 633 if (ilp->lun_pip != NULL) { 634 635 /* virt/mdi */ 636 ndi_devi_enter(scsi_vhci_dip, &circ); 637 if ((lun_free == B_TRUE) && 638 (ilp->lun_state == ISCSI_LUN_STATE_ONLINE)) { 639 rval = mdi_pi_offline(ilp->lun_pip, 640 NDI_DEVI_REMOVE); 641 } else { 642 rval = mdi_pi_offline(ilp->lun_pip, 0); 643 } 644 645 if (rval == MDI_SUCCESS) { 646 ilp->lun_state = ISCSI_LUN_STATE_OFFLINE; 647 if (lun_free == B_TRUE) { 648 (void) mdi_prop_remove(ilp->lun_pip, NULL); 649 (void) mdi_pi_free(ilp->lun_pip, 0); 650 } 651 } else { 652 status = ISCSI_STATUS_INTERNAL_ERROR; 653 } 654 ndi_devi_exit(scsi_vhci_dip, circ); 655 656 } else { 657 658 /* phys/ndi */ 659 ndi_devi_enter(ihp->hba_dip, &circ); 660 if ((lun_free == B_TRUE) && 661 (ilp->lun_state == ISCSI_LUN_STATE_ONLINE)) { 662 rval = ndi_devi_offline( 663 ilp->lun_dip, NDI_DEVI_REMOVE); 664 } else { 665 rval = ndi_devi_offline( 666 ilp->lun_dip, 0); 667 } 668 if (rval != NDI_SUCCESS) { 669 status = ISCSI_STATUS_INTERNAL_ERROR; 670 } else { 671 ilp->lun_state = ISCSI_LUN_STATE_OFFLINE; 672 } 673 ndi_devi_exit(ihp->hba_dip, circ); 674 675 } 676 return (status); 677 } 678