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