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