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