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