Home | History | Annotate | Download | only in iscsi
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  *
     25  * iSCSI logical unit interfaces
     26  */
     27 
     28 #include "iscsi.h"
     29 #include <sys/fs/dv_node.h>	/* devfs_clean */
     30 #include <sys/bootprops.h>
     31 #include <sys/sysevent/eventdefs.h>
     32 #include <sys/sysevent/dev.h>
     33 
     34 /* tpgt bytes in string form */
     35 #define	TPGT_EXT_SIZE	5
     36 
     37 /* logical unit number bytes in string form */
     38 #define	LUN_EXT_SIZE	10
     39 
     40 /*
     41  * Addition addr size of size of ',' + max str form of tpgt (2 bytes) +
     42  * ',' + max str form of logical unit number (4 bytes).
     43  */
     44 #define	ADDR_EXT_SIZE	(1 + TPGT_EXT_SIZE + 1 + LUN_EXT_SIZE)
     45 
     46 /* internal interfaces */
     47 static iscsi_status_t iscsi_lun_virt_create(iscsi_sess_t *isp,
     48     uint16_t lun_num, iscsi_lun_t *ilp, struct scsi_inquiry *inq);
     49 static iscsi_status_t iscsi_lun_phys_create(iscsi_sess_t *isp,
     50     uint16_t lun_num, iscsi_lun_t *ilp, struct scsi_inquiry *inq);
     51 
     52 extern dev_info_t	*scsi_vhci_dip;
     53 extern ib_boot_prop_t   *iscsiboot_prop;
     54 
     55 /*
     56  * +--------------------------------------------------------------------+
     57  * | External Connection Interfaces					|
     58  * +--------------------------------------------------------------------+
     59  */
     60 
     61 
     62 /*
     63  * iscsi_lun_create - This function will create a lun mapping.
     64  * logic specific to MPxIO vs. NDI node creation is switched
     65  * out to a helper function.
     66  */
     67 iscsi_status_t
     68 iscsi_lun_create(iscsi_sess_t *isp, uint16_t lun_num, uint8_t lun_addr_type,
     69     struct scsi_inquiry *inq, char *guid)
     70 {
     71 	iscsi_status_t		rtn		= ISCSI_STATUS_INTERNAL_ERROR;
     72 	iscsi_hba_t		*ihp		= NULL;
     73 	iscsi_lun_t		*ilp		= NULL;
     74 	iscsi_lun_t		*ilp_tmp	= NULL;
     75 	char			*addr		= NULL;
     76 	uint16_t		boot_lun_num	= 0;
     77 	uint64_t		*lun_num_ptr	= NULL;
     78 	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 			rval = devfs_clean(pdip, devname + 1,
    691 			    DV_CLEAN_FORCE);
    692 			kmem_free(devname, MAXNAMELEN + 1);
    693 
    694 			if ((rval != 0) && (ilp->lun_pip == NULL)) {
    695 				return (ISCSI_STATUS_BUSY);
    696 			}
    697 		}
    698 	}
    699 
    700 	if (cdip != NULL && ilp->lun_type == DTYPE_DIRECT) {
    701 		pathname = kmem_zalloc(MAXNAMELEN + 1, KM_SLEEP);
    702 		(void) ddi_pathname(cdip, pathname);
    703 	}
    704 
    705 	/* Attempt to offline the logical units */
    706 	if (ilp->lun_pip != NULL) {
    707 
    708 		/* virt/mdi */
    709 		ndi_devi_enter(scsi_vhci_dip, &circ);
    710 		if ((lun_free == B_TRUE) &&
    711 		    (ilp->lun_state & ISCSI_LUN_STATE_ONLINE)) {
    712 			rval = mdi_pi_offline(ilp->lun_pip,
    713 			    NDI_DEVI_REMOVE);
    714 		} else {
    715 			rval = mdi_pi_offline(ilp->lun_pip, 0);
    716 		}
    717 
    718 		if (rval == MDI_SUCCESS) {
    719 			ilp->lun_state &= ISCSI_LUN_STATE_CLEAR;
    720 			ilp->lun_state |= ISCSI_LUN_STATE_OFFLINE;
    721 			if (lun_free == B_TRUE) {
    722 				(void) mdi_prop_remove(ilp->lun_pip, NULL);
    723 				(void) mdi_pi_free(ilp->lun_pip, 0);
    724 			}
    725 			offline = B_TRUE;
    726 		} else {
    727 			status = ISCSI_STATUS_INTERNAL_ERROR;
    728 			if (lun_free == B_FALSE) {
    729 				ilp->lun_state |= ISCSI_LUN_STATE_INVALID;
    730 				offline = B_TRUE;
    731 			}
    732 		}
    733 		ndi_devi_exit(scsi_vhci_dip, circ);
    734 
    735 	} else  {
    736 
    737 		/* phys/ndi */
    738 		ndi_devi_enter(ihp->hba_dip, &circ);
    739 		if ((lun_free == B_TRUE) &&
    740 		    (ilp->lun_state & ISCSI_LUN_STATE_ONLINE)) {
    741 			rval = ndi_devi_offline(
    742 			    ilp->lun_dip, NDI_DEVI_REMOVE);
    743 		} else {
    744 			rval = ndi_devi_offline(
    745 			    ilp->lun_dip, 0);
    746 		}
    747 		if (rval != NDI_SUCCESS) {
    748 			status = ISCSI_STATUS_INTERNAL_ERROR;
    749 			if (lun_free == B_FALSE) {
    750 				ilp->lun_state |= ISCSI_LUN_STATE_INVALID;
    751 				offline = B_TRUE;
    752 			}
    753 		} else {
    754 			ilp->lun_state &= ISCSI_LUN_STATE_CLEAR;
    755 			ilp->lun_state |= ISCSI_LUN_STATE_OFFLINE;
    756 			offline = B_TRUE;
    757 		}
    758 		ndi_devi_exit(ihp->hba_dip, circ);
    759 	}
    760 
    761 	if (offline == B_TRUE && pathname != NULL &&
    762 	    ilp->lun_type == DTYPE_DIRECT) {
    763 		if (nvlist_alloc(&attr_list, NV_UNIQUE_NAME_TYPE, KM_SLEEP) !=
    764 		    DDI_SUCCESS) {
    765 			kmem_free(pathname, MAXNAMELEN + 1);
    766 			return (status);
    767 		}
    768 
    769 		if (nvlist_add_string(attr_list, DEV_PHYS_PATH, pathname) !=
    770 		    DDI_SUCCESS) {
    771 			nvlist_free(attr_list);
    772 			kmem_free(pathname, MAXNAMELEN + 1);
    773 			return (status);
    774 		}
    775 
    776 		iscsi_send_sysevent(ihp, EC_DEV_REMOVE, ESC_DISK, attr_list);
    777 		nvlist_free(attr_list);
    778 	}
    779 
    780 	if (pathname != NULL) {
    781 		kmem_free(pathname, MAXNAMELEN + 1);
    782 	}
    783 
    784 	return (status);
    785 }
    786