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