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