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 /*
23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * Copyright 2019 Nexenta Systems, Inc.
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
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
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
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
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
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
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 int circ = 0;
389
390 ASSERT(isp != NULL);
391 ASSERT(ilp != NULL);
392 ihp = isp->sess_hba;
393 ASSERT(ihp != NULL);
394
395 /*
396 * Generate compatible property
397 */
398 scsi_hba_nodename_compatible_get(inq, "vhci",
399 inq->inq_dtype, NULL, &nodename, &compatible, &ncompatible);
400
401 /* if nodename can't be determined then print a message and skip it */
402 if (nodename == NULL) {
403 cmn_err(CE_WARN, "iscsi driver found no compatible driver "
404 "for %s lun %d dtype:0x%02x", isp->sess_name, lun_num,
405 inq->inq_dtype);
406 return (ISCSI_STATUS_INTERNAL_ERROR);
407 }
408
409 /*
410 *
411 */
412 ndi_devi_enter(scsi_vhci_dip, &circ);
413 mdi_rtn = mdi_pi_alloc_compatible(ihp->hba_dip, nodename,
414 ilp->lun_guid, ilp->lun_addr, compatible, ncompatible,
415 0, &pip);
416
417 if (mdi_rtn == MDI_SUCCESS) {
418 mdi_pi_set_phci_private(pip, (caddr_t)ilp);
419
420 if (mdi_prop_update_string(pip, MDI_GUID,
421 ilp->lun_guid) != DDI_SUCCESS) {
422 cmn_err(CE_WARN, "iscsi driver unable to create "
423 "property for %s lun %d (MDI_GUID)",
424 isp->sess_name, lun_num);
425 mdi_rtn = MDI_FAILURE;
426 goto virt_create_done;
427 }
428
429 if (mdi_prop_update_int(pip, TARGET_PROP,
430 isp->sess_oid) != DDI_SUCCESS) {
431 cmn_err(CE_WARN, "iscsi driver unable to create "
432 "property for %s lun %d (TARGET_PROP)",
433 isp->sess_name, lun_num);
434 mdi_rtn = MDI_FAILURE;
435 goto virt_create_done;
436 }
437
438 if (mdi_prop_update_int(pip, LUN_PROP,
439 ilp->lun_num) != DDI_SUCCESS) {
440 cmn_err(CE_WARN, "iscsi driver unable to create "
441 "property for %s lun %d (LUN_PROP)",
442 isp->sess_name, lun_num);
443 mdi_rtn = MDI_FAILURE;
444 goto virt_create_done;
445 }
446
447 if (mdi_prop_update_string_array(pip, "compatible",
448 compatible, ncompatible) !=
449 DDI_PROP_SUCCESS) {
450 cmn_err(CE_WARN, "iscsi driver unable to create "
451 "property for %s lun %d (COMPATIBLE)",
452 isp->sess_name, lun_num);
453 mdi_rtn = MDI_FAILURE;
454 goto virt_create_done;
455 }
456
457 mdi_rtn = mdi_pi_online(pip, 0);
458 if (mdi_rtn == MDI_NOT_SUPPORTED) {
459 mdi_rtn = MDI_FAILURE;
460 goto virt_create_done;
461 }
462
463 ilp->lun_pip = pip;
464 ilp->lun_dip = NULL;
465
466 virt_create_done:
467
468 if (pip && mdi_rtn != MDI_SUCCESS) {
469 ilp->lun_pip = NULL;
470 ilp->lun_dip = NULL;
471 (void) mdi_prop_remove(pip, NULL);
472 (void) mdi_pi_free(pip, 0);
473 } else {
474 rtn = ISCSI_STATUS_SUCCESS;
475 }
476 }
477 ndi_devi_exit(scsi_vhci_dip, circ);
478
479 scsi_hba_nodename_compatible_free(nodename, compatible);
480
481 return (rtn);
482 }
483
484
485 /*
486 * iscsi_lun_phys_create - creates solaris logical unit via NDI
487 */
488 static iscsi_status_t
489 iscsi_lun_phys_create(iscsi_sess_t *isp, uint16_t lun_num,
490 iscsi_lun_t *ilp, struct scsi_inquiry *inq)
491 {
492 iscsi_status_t rtn = ISCSI_STATUS_INTERNAL_ERROR;
493 int ndi_rtn = NDI_FAILURE;
494 iscsi_hba_t *ihp = NULL;
495 dev_info_t *lun_dip = NULL;
496 char *nodename = NULL;
497 char **compatible = NULL;
498 int ncompatible = 0;
499 char *scsi_binding_set = NULL;
500 char instance[32];
501 int circ = 0;
502
503 ASSERT(isp != NULL);
504 ASSERT(ilp != NULL);
505 ihp = isp->sess_hba;
506 ASSERT(ihp != NULL);
507 ASSERT(inq != NULL);
508
509 /* get the 'scsi-binding-set' property */
510 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, isp->sess_hba->hba_dip,
511 DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, "scsi-binding-set",
512 &scsi_binding_set) != DDI_PROP_SUCCESS) {
513 scsi_binding_set = NULL;
514 }
515
516 /* generate compatible property */
517 scsi_hba_nodename_compatible_get(inq, scsi_binding_set,
518 inq->inq_dtype, NULL, &nodename, &compatible, &ncompatible);
519 if (scsi_binding_set)
520 ddi_prop_free(scsi_binding_set);
521
522 /* if nodename can't be determined then print a message and skip it */
523 if (nodename == NULL) {
524 cmn_err(CE_WARN, "iscsi driver found no compatible driver "
525 "for %s lun %d", isp->sess_name, lun_num);
526 return (ISCSI_STATUS_INTERNAL_ERROR);
527 }
528
529 ndi_devi_enter(ihp->hba_dip, &circ);
530
531 ndi_rtn = ndi_devi_alloc(ihp->hba_dip, nodename,
532 DEVI_SID_NODEID, &lun_dip);
533
534 /* if lun alloc success, set props */
535 if (ndi_rtn == NDI_SUCCESS) {
536
537 if (ndi_prop_update_int(DDI_DEV_T_NONE,
538 lun_dip, TARGET_PROP, (int)isp->sess_oid) !=
539 DDI_PROP_SUCCESS) {
540 cmn_err(CE_WARN, "iscsi driver unable to create "
541 "property for %s lun %d (TARGET_PROP)",
542 isp->sess_name, lun_num);
543 ndi_rtn = NDI_FAILURE;
544 goto phys_create_done;
545 }
546
547 if (ndi_prop_update_int(DDI_DEV_T_NONE,
548 lun_dip, LUN_PROP, (int)ilp->lun_num) !=
549 DDI_PROP_SUCCESS) {
550 cmn_err(CE_WARN, "iscsi driver unable to create "
551 "property for %s lun %d (LUN_PROP)",
552 isp->sess_name, lun_num);
553 ndi_rtn = NDI_FAILURE;
554 goto phys_create_done;
555 }
556
557 if (ndi_prop_update_string_array(DDI_DEV_T_NONE,
558 lun_dip, "compatible", compatible, ncompatible)
559 != DDI_PROP_SUCCESS) {
560 cmn_err(CE_WARN, "iscsi driver unable to create "
561 "property for %s lun %d (COMPATIBLE)",
562 isp->sess_name, lun_num);
563 ndi_rtn = NDI_FAILURE;
564 goto phys_create_done;
565 }
566
567 phys_create_done:
568 /* If props were setup ok, online the lun */
569 if (ndi_rtn == NDI_SUCCESS) {
570 /* Try to online the new node */
571 ndi_rtn = ndi_devi_online(lun_dip, 0);
572 }
573
574 /* If success set rtn flag, else unwire alloc'd lun */
575 if (ndi_rtn == NDI_SUCCESS) {
576 rtn = ISCSI_STATUS_SUCCESS;
577 /*
578 * Assign the instance number for the dev_link
579 * generator. This will ensure the link name is
580 * unique and persistent across reboots.
581 */
582 (void) snprintf(instance, 32, "%d",
583 ddi_get_instance(lun_dip));
584 (void) ndi_prop_update_string(DDI_DEV_T_NONE,
585 lun_dip, NDI_GUID, instance);
586 } else {
587 cmn_err(CE_WARN, "iscsi driver unable to online "
588 "%s lun %d", isp->sess_name, lun_num);
589 ndi_prop_remove_all(lun_dip);
590 (void) ndi_devi_free(lun_dip);
591 }
592
593 }
594 ndi_devi_exit(ihp->hba_dip, circ);
595
596 ilp->lun_dip = lun_dip;
597 ilp->lun_pip = NULL;
598
599 scsi_hba_nodename_compatible_free(nodename, compatible);
600
601 return (rtn);
602 }
603
604
605 /*
606 * iscsi_lun_online - _di_online logical unit
607 *
608 * This is called after a path has recovered it will cause
609 * an offline path to become online/active again.
610 */
611 void
612 iscsi_lun_online(iscsi_hba_t *ihp, iscsi_lun_t *ilp)
613 {
614 int circ = 0;
615 int rval = 0;
616 uint64_t *lun_num_ptr = NULL;
617 uint16_t boot_lun_num = 0;
618 iscsi_sess_t *isp = NULL;
619 boolean_t online = B_FALSE;
620 nvlist_t *attr_list = NULL;
621 char *pathname = NULL;
622 dev_info_t *lun_dip = NULL;
623
624 ASSERT(ilp != NULL);
625 ASSERT((ilp->lun_pip != NULL) || (ilp->lun_dip != NULL));
626
627 if (ilp->lun_pip != NULL) {
628 ndi_devi_enter(scsi_vhci_dip, &circ);
629 rval = mdi_pi_online(ilp->lun_pip, 0);
630 ndi_devi_exit(scsi_vhci_dip, circ);
631 if (rval == MDI_SUCCESS) {
632 ilp->lun_state &= ISCSI_LUN_STATE_CLEAR;
633 ilp->lun_state |= ISCSI_LUN_STATE_ONLINE;
634 ilp->lun_time_online = ddi_get_time();
635 online = B_TRUE;
636 }
637
638 } else if (ilp->lun_dip != NULL) {
639 ndi_devi_enter(ihp->hba_dip, &circ);
640 rval = ndi_devi_online(ilp->lun_dip, 0);
641 ndi_devi_exit(ihp->hba_dip, circ);
642 if (rval == NDI_SUCCESS) {
643 ilp->lun_state &= ISCSI_LUN_STATE_CLEAR;
644 ilp->lun_state |= ISCSI_LUN_STATE_ONLINE;
645 ilp->lun_time_online = ddi_get_time();
646 online = B_TRUE;
647 }
648 }
649
650 /* Check whether this is the required LUN for iscsi boot */
651 if (iscsiboot_prop != NULL &&
652 iscsiboot_prop->boot_tgt.lun_online == 0) {
653 isp = ilp->lun_sess;
654 if (isp->sess_boot == B_TRUE) {
655 lun_num_ptr =
656 (uint64_t *)iscsiboot_prop->boot_tgt.tgt_boot_lun;
657 boot_lun_num = (uint16_t)(*lun_num_ptr);
658 if (boot_lun_num == ilp->lun_num) {
659 /*
660 * During iscsi boot, the boot lun has been
661 * online, we should set the "online flag".
662 */
663 iscsiboot_prop->boot_tgt.lun_online = 1;
664 }
665 }
666 }
667
668 /*
669 * If the LUN has been online and it is a disk,
670 * send out a system event.
671 */
672 if (online == B_TRUE && ilp->lun_type == DTYPE_DIRECT) {
673 if (nvlist_alloc(&attr_list, NV_UNIQUE_NAME_TYPE, KM_SLEEP) !=
674 DDI_SUCCESS) {
675 return;
676 }
677
678 if (ilp->lun_pip != NULL) {
679 lun_dip = mdi_pi_get_client(ilp->lun_pip);
680 } else {
681 lun_dip = ilp->lun_dip;
682 }
683
684 pathname = kmem_zalloc(MAXNAMELEN + 1, KM_SLEEP);
685 (void) ddi_pathname(lun_dip, pathname);
686
687 if (nvlist_add_string(attr_list, DEV_PHYS_PATH, pathname) !=
688 DDI_SUCCESS) {
689 nvlist_free(attr_list);
690 kmem_free(pathname, MAXNAMELEN + 1);
691 return;
692 }
693 iscsi_send_sysevent(ihp, EC_DEV_ADD, ESC_DISK, attr_list);
694 kmem_free(pathname, MAXNAMELEN + 1);
695 nvlist_free(attr_list);
696 }
697 }
698
699 /*
700 * iscsi_lun_offline - attempt _di_offline [and optional _di_free]
701 *
702 * This function is called via two paths. When a transport
703 * path has failed it will be called to offline the logical
704 * unit. When nameservice access has been removed it will
705 * be called to both offline and free the logical unit.
706 * (This operates soley on the solaris node states.
707 * iscsi_lun_destroy() should be called when attempting
708 * to free all iscsi lun resources.)
709 *
710 * This function can fail with ISCSI_STATUS_BUSY if the
711 * logical unit is in use. The user should unmount or
712 * close the device and perform the nameservice operation
713 * again if this occurs.
714 *
715 * If we fail to offline a LUN that we don't want to destroy,
716 * we will mark it with invalid state. If this LUN still
717 * exists on the target, we can have another chance to online
718 * it again when we do the LUN enumeration.
719 */
720 iscsi_status_t
721 iscsi_lun_offline(iscsi_hba_t *ihp, iscsi_lun_t *ilp, boolean_t lun_free)
722 {
723 iscsi_status_t status = ISCSI_STATUS_SUCCESS;
724 int circ = 0;
725 dev_info_t *cdip;
726 char *pathname = NULL;
727 boolean_t offline = B_FALSE;
728 nvlist_t *attr_list = NULL;
729
730 ASSERT(ilp != NULL);
731 ASSERT((ilp->lun_pip != NULL) || (ilp->lun_dip != NULL));
732
733 if (ilp->lun_pip == NULL)
734 cdip = ilp->lun_dip;
735 else
736 cdip = mdi_pi_get_client(ilp->lun_pip);
737
738 if (cdip != NULL && ilp->lun_type == DTYPE_DIRECT) {
739 pathname = kmem_zalloc(MAXNAMELEN + 1, KM_SLEEP);
740 (void) ddi_pathname(cdip, pathname);
741 }
742
743 /* Attempt to offline the logical units */
744 if (ilp->lun_pip != NULL) {
745 /* virt/mdi */
746 ndi_devi_enter(scsi_vhci_dip, &circ);
747 if (mdi_pi_offline(ilp->lun_pip, 0) == MDI_SUCCESS) {
748 ilp->lun_state &= ISCSI_LUN_STATE_CLEAR;
749 ilp->lun_state |= ISCSI_LUN_STATE_OFFLINE;
750 if (lun_free == B_TRUE) {
751 (void) mdi_prop_remove(ilp->lun_pip, NULL);
752 (void) mdi_pi_free(ilp->lun_pip, 0);
753 }
754 offline = B_TRUE;
755 } else {
756 status = ISCSI_STATUS_BUSY;
757 if (lun_free == B_FALSE) {
758 ilp->lun_state |= ISCSI_LUN_STATE_INVALID;
759 offline = B_TRUE;
760 }
761 }
762 ndi_devi_exit(scsi_vhci_dip, circ);
763
764 } else {
765 /* phys/ndi */
766 int flags = NDI_DEVFS_CLEAN;
767
768 ndi_devi_enter(ihp->hba_dip, &circ);
769 if (lun_free == B_TRUE &&
770 (ilp->lun_state & ISCSI_LUN_STATE_ONLINE))
771 flags |= NDI_DEVI_REMOVE;
772 if (ndi_devi_offline(ilp->lun_dip, flags) != NDI_SUCCESS) {
773 status = ISCSI_STATUS_BUSY;
774 if (lun_free == B_FALSE) {
775 ilp->lun_state |= ISCSI_LUN_STATE_INVALID;
776 offline = B_TRUE;
777 }
778 } else {
779 ilp->lun_state &= ISCSI_LUN_STATE_CLEAR;
780 ilp->lun_state |= ISCSI_LUN_STATE_OFFLINE;
781 offline = B_TRUE;
782 }
783 ndi_devi_exit(ihp->hba_dip, circ);
784 }
785
786 if (offline == B_TRUE && pathname != NULL &&
787 ilp->lun_type == DTYPE_DIRECT) {
788 if (nvlist_alloc(&attr_list, NV_UNIQUE_NAME_TYPE, KM_SLEEP) !=
789 DDI_SUCCESS) {
790 kmem_free(pathname, MAXNAMELEN + 1);
791 return (status);
792 }
793
794 if (nvlist_add_string(attr_list, DEV_PHYS_PATH, pathname) !=
795 DDI_SUCCESS) {
796 nvlist_free(attr_list);
797 kmem_free(pathname, MAXNAMELEN + 1);
798 return (status);
799 }
800
801 iscsi_send_sysevent(ihp, EC_DEV_REMOVE, ESC_DISK, attr_list);
802 nvlist_free(attr_list);
803 }
804
805 if (pathname != NULL) {
806 kmem_free(pathname, MAXNAMELEN + 1);
807 }
808
809 return (status);
810 }