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