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