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