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