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