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