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