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