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.
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.
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]
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
29 /* Function prototypes */
31 static fpcfga_ret_t
get_xport_devlink(const char *hba_phys
,
32 char **hba_logpp
, int *l_errnop
);
33 static char ctoi(char c
);
34 static fpcfga_ret_t
is_apid_configured(const char *xport_phys
,
35 const char *dyncomp
, struct luninfo_list
**lunlistpp
, int *l_errnop
);
36 static fpcfga_ret_t
insert_lun_to_lunlist(struct luninfo_list
**lunlistpp
,
37 const char *dyncomp
, di_node_t devnode
, int *l_errnop
);
38 static fpcfga_ret_t
update_lunlist(struct luninfo_list
**lunlistpp
, int lun
,
39 uint_t state
, char *pathp
, int *l_errnop
);
44 /* Various conversions routines */
47 cvt_lawwn_to_dyncomp(const la_wwn_t
*pwwn
, char **dyncomp
, int *l_errnop
)
49 *dyncomp
= calloc(1, WWN_SIZE
*2 + 1);
50 if (*dyncomp
== NULL
) {
54 (void) sprintf(*dyncomp
, "%016llx",
55 (wwnConversion((uchar_t
*)pwwn
->raw_wwn
)));
60 cvt_dyncomp_to_lawwn(const char *dyncomp
, la_wwn_t
*port_wwn
)
66 wwnp
= port_wwn
->raw_wwn
;
67 for (i
= 0; i
< WWN_SIZE
; i
++, wwnp
++) {
70 c1
= ctoi(*dyncomp
++);
71 if (c
== -1 || c1
== -1)
73 *wwnp
= ((c
<< 4) + c1
);
83 if ((c
>= '0') && (c
<= '9'))
85 else if ((c
>= 'A') && (c
<= 'F'))
87 else if ((c
>= 'a') && (c
<= 'f'))
96 * Generates the HBA logical ap_id from physical ap_id.
99 make_xport_logid(const char *xport_phys
, char **xport_logpp
, int *l_errnop
)
101 if (*xport_logpp
!= NULL
) {
106 * A devlink for the XPORT should exist. Without the /dev/cfg link
107 * driver name and instance number based based link needs to be
108 * constructed for the minor node type of DDI_NT_FC_ATTACHMENT_POINT.
109 * sunddi.h defines DDI_NT_FC_ATTACHMENT_POINT for
110 * ddi_ctl:attachment_point:fc
112 if (get_xport_devlink(xport_phys
, xport_logpp
, l_errnop
) == FPCFGA_OK
) {
113 assert(*xport_logpp
!= NULL
);
121 get_xport_devlink(const char *xport_phys
, char **xport_logpp
, int *l_errnop
)
128 ret
= physpath_to_devlink(CFGA_DEV_DIR
, (char *)xport_phys
,
129 xport_logpp
, l_errnop
, match_minor
);
130 if (ret
!= FPCFGA_OK
) {
134 assert(*xport_logpp
!= NULL
);
136 /* Remove the "/dev/cfg/" prefix */
137 len
= strlen(CFGA_DEV_DIR SLASH
);
139 (void) memmove(*xport_logpp
, *xport_logpp
+ len
,
140 strlen(*xport_logpp
+ len
) + 1);
147 * Given a xport path and dynamic ap_id, returns the physical
148 * path in pathpp. If the dynamic ap is not configured pathpp set to NULL
149 * and returns FPCFGA_APID_NOCONFIGURE.
153 const char *xport_phys
,
155 struct luninfo_list
**lunlistpp
,
160 /* A device MUST have a dynamic component */
161 if (dyncomp
== NULL
) {
162 return (FPCFGA_LIB_ERR
);
165 ret
= is_apid_configured(xport_phys
, dyncomp
, lunlistpp
, l_errnop
);
167 assert(ret
!= FPCFGA_OK
);
173 * When both the transport and dynamic comp are given this function
174 * checks to see if the dynamic ap is configured on the dev tree.
175 * If it is configured the devfs path will be stored in pathpp.
176 * When the dynamic comp is null this function check to see if the transport
177 * node has any child.
179 * Retrun value: FPCFGA_OK if the apid is configured.
180 * FPCFGA_APID_NOCONFIGURE if the apid is not configured.
181 * FPCFGA_LIB_ERR for other errors.
185 const char *xport_phys
,
187 struct luninfo_list
**lunlistpp
,
190 char *devfs_phys
, *devfs_fp_path
, *client_path
, *cp
,
192 char path_name
[MAXPATHLEN
];
193 di_node_t tree_root
, root
, fpnode
, dev_node
, client_node
;
194 di_path_t path
= DI_PATH_NIL
;
195 di_prop_t prop
= DI_PROP_NIL
;
196 uchar_t
*port_wwn_data
= NULL
;
197 char *lun_guid
= NULL
;
198 char port_wwn
[WWN_SIZE
*2+1];
199 int count
, *lunnump
, devlen
,
205 if (*lunlistpp
!= NULL
) {
206 return (FPCFGA_LIB_ERR
);
209 ret
= FPCFGA_APID_NOCONFIGURE
;
211 if ((devfs_phys
= strdup(xport_phys
)) == NULL
) {
213 return (FPCFGA_LIB_ERR
);
216 if (strncmp(devfs_phys
, DEVICES_DIR SLASH
, strlen(DEVICES_DIR
) +
217 strlen(SLASH
)) == 0) {
218 cp
= devfs_phys
+ strlen(DEVICES_DIR
);
219 (void) memmove(devfs_phys
, cp
, strlen(cp
) + 1);
222 if ((cp
= strstr(devfs_phys
, MINOR_SEP
)) != NULL
) {
223 *cp
= '\0'; /* Terminate string. */
226 if ((tree_root
= di_init("/", DINFOCPYALL
| DINFOPATH
))
230 return (FPCFGA_LIB_ERR
);
233 fpnode
= di_drv_first_node("fp", tree_root
);
236 devfs_fp_path
= di_devfs_path(fpnode
);
237 if ((devfs_fp_path
) && !(strncmp(devfs_fp_path
,
238 devfs_phys
, strlen(devfs_phys
)))) {
240 di_devfs_path_free(devfs_fp_path
);
243 di_devfs_path_free(devfs_fp_path
);
244 fpnode
= di_drv_next_node(fpnode
);
247 ret
= FPCFGA_LIB_ERR
;
254 * when there is no child and path info node the
255 * FPCFGA_APID_NOCONFIGURE is returned
256 * regardless of the dynamic comp.
258 dev_node
= di_child_node(root
);
259 path
= di_path_next_client(root
, path
);
260 if ((dev_node
== DI_NODE_NIL
) && (path
== DI_PATH_NIL
)) {
262 ret
= FPCFGA_APID_NOCONFIGURE
;
267 * when dyn comp is null the function just checks if there is any
268 * child under fp transport attachment point.
270 if (dyncomp
== NULL
) {
276 * now checks the children node to find
277 * if dynamic ap is configured. if there are multiple luns
278 * store into lunlist.
280 if (dev_node
!= DI_NODE_NIL
) {
282 while ((prop
= di_prop_next(dev_node
, prop
)) !=
284 /* is property name port-wwn */
285 if ((!(strcmp(PORT_WWN_PROP
,
286 di_prop_name(prop
)))) &&
287 (di_prop_type(prop
) ==
288 DI_PROP_TYPE_BYTE
)) {
293 if (prop
!= DI_PROP_NIL
) {
294 count
= di_prop_bytes(prop
, &port_wwn_data
);
295 if (count
!= WWN_SIZE
) {
296 ret
= FPCFGA_LIB_ERR
;
299 (void) sprintf(port_wwn
,
300 "%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x",
301 port_wwn_data
[0], port_wwn_data
[1],
302 port_wwn_data
[2], port_wwn_data
[3],
303 port_wwn_data
[4], port_wwn_data
[5],
304 port_wwn_data
[6], port_wwn_data
[7]);
305 if (!(strncmp(port_wwn
, dyncomp
,
307 ret
= insert_lun_to_lunlist(
310 if (ret
!= FPCFGA_OK
) {
316 dev_node
= di_sibling_node(dev_node
);
318 } while (dev_node
!= DI_NODE_NIL
);
322 * now checks the path info node to find
323 * if dynamic ap is configured. if there are multiple luns
324 * store into lunlist.
326 if (path
!= DI_PATH_NIL
) {
328 * now parse the path info node.
331 count
= di_path_prop_lookup_bytes(path
, PORT_WWN_PROP
,
333 if (count
!= WWN_SIZE
) {
334 ret
= FPCFGA_LIB_ERR
;
338 (void) sprintf(port_wwn
,
339 "%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x",
340 port_wwn_data
[0], port_wwn_data
[1],
341 port_wwn_data
[2], port_wwn_data
[3],
342 port_wwn_data
[4], port_wwn_data
[5],
343 port_wwn_data
[6], port_wwn_data
[7]);
345 /* if matches get the path of scsi_vhci child node. */
346 if (!(strncmp(port_wwn
, dyncomp
, WWN_SIZE
*2))) {
347 client_node
= di_path_client_node(path
);
348 if (client_node
== DI_NODE_NIL
) {
349 ret
= FPCFGA_LIB_ERR
;
353 count
= di_path_prop_lookup_ints(path
,
355 client_path
= di_devfs_path(client_node
);
356 strcpy(path_name
, client_path
);
357 di_devfs_path_free(client_path
);
358 state
= di_state(client_node
);
359 statep
= di_path_state(path
);
363 * state then check the devfs_path to
364 * see if it has a complete path.
365 * For non scsi_vhci node the path
366 * doesn't contain @w(portwwn) part
367 * consistently. For scsi_vhci
368 * this behavior may not be there.
369 * To be safe @g(guid) is attempted
372 if ((state
& DI_DRIVER_DETACHED
) &&
373 (strstr(path_name
, "@g") == NULL
)) {
375 while ((prop
= di_prop_next(client_node
,
376 prop
)) != DI_PROP_NIL
) {
377 /* is property name lun-wwn */
378 if ((!(strcmp(LUN_GUID_PROP
,
379 di_prop_name(prop
)))) &&
380 (di_prop_type(prop
) ==
381 DI_PROP_TYPE_STRING
)) {
386 if (prop
!= DI_PROP_NIL
) {
387 count
= di_prop_strings(
393 ret
= FPCFGA_LIB_ERR
;
398 devlen
= strlen(DEVICES_DIR
) +
399 strlen(path_name
) + 1;
400 if ((pathp
= calloc(1, devlen
))
403 return (FPCFGA_LIB_ERR
);
405 (void) snprintf(pathp
, devlen
,
406 "%s%s", DEVICES_DIR
, path_name
);
408 if ((ret
= (update_lunlist(lunlistpp
, *lunnump
,
409 statep
, pathp
, l_errnop
))) !=
415 path
= di_path_next_client(root
, path
);
416 } while (path
!= DI_PATH_NIL
);
426 insert_lun_to_lunlist(
427 struct luninfo_list
**lunlistpp
,
432 char path_name
[MAXPATHLEN
];
433 char *pathp
, *dev_phys
;
434 di_prop_t prop_lun
= DI_PROP_NIL
;
439 while ((prop_lun
= di_prop_next(dev_node
, prop_lun
)) != DI_PROP_NIL
) {
440 if (!(strcmp(LUN_PROP
, di_prop_name(prop_lun
))) &&
441 (di_prop_type(prop_lun
) == DI_PROP_TYPE_INT
)) {
442 count
= di_prop_ints(prop_lun
, &lunp
);
444 return (FPCFGA_LIB_ERR
);
450 if (prop_lun
== DI_PROP_NIL
) {
451 return (FPCFGA_LIB_ERR
);
455 * stores state info in state.
456 * This information is used to get the
458 * if driver_detached don't try to get
459 * the devfs_path since it is not
460 * complete. ex, /pci@1f,2000/pci@1/
461 * SUNW,qlc@5/fp@0,0/ssd
462 * which doesn't contain the port wwn
463 * part. The attached node looks like
464 * /pci@1f,2000/pci@1/SUNW,qlc@5/fp@0,0/
465 * ssd@w2100002037006b14,0
467 state
= di_state(dev_node
);
469 dev_phys
= di_devfs_path(dev_node
);
470 if (dev_phys
== NULL
) {
471 return (FPCFGA_LIB_ERR
);
474 strcpy(path_name
, dev_phys
);
476 di_devfs_path_free(dev_phys
);
478 if ((state
& DI_DRIVER_DETACHED
) &&
479 (strstr(path_name
, "@w") == NULL
)) {
480 sprintf(&path_name
[strlen(path_name
)], "@w%s,%x", dyncomp
,
484 devlen
= strlen(DEVICES_DIR
) + strlen(path_name
) + 1;
486 if ((pathp
= calloc(1, devlen
))
489 return (FPCFGA_LIB_ERR
);
491 (void) snprintf(pathp
, devlen
, "%s%s", DEVICES_DIR
, path_name
);
494 return (update_lunlist(lunlistpp
, *lunp
, state
, pathp
, l_errnop
));
499 struct luninfo_list
**lunlistpp
,
505 struct luninfo_list
*newlun
, *curlun
, *prevlun
;
507 newlun
= curlun
= prevlun
= NULL
;
509 newlun
= calloc(1, sizeof (struct luninfo_list
));
510 if (newlun
== NULL
) {
512 return (FPCFGA_LIB_ERR
);
515 newlun
->lunnum
= lun
;
516 newlun
->node_state
= state
;
517 newlun
->path
= pathp
;
520 /* if lunlist is empty add the new lun info and return. */
521 if (*lunlistpp
== NULL
) {
526 /* if the first lun in the list is the same as the new lun return. */
527 if ((*lunlistpp
)->lunnum
== lun
) {
533 * if the first lun in the list is less than the new lun add the
534 * new lun as the first lun and return.
536 if ((*lunlistpp
)->lunnum
< lun
) {
537 newlun
->next
= *lunlistpp
;
543 * if the first lun in the list is greater than the new lun and
544 * there is a single lun add new lun after the first lun and return.
546 if ((*lunlistpp
)->next
== NULL
) {
547 (*lunlistpp
)->next
= newlun
;
552 * now there is more than two luns in the list and the first lun
553 * is greter than the input lun.
555 curlun
= (*lunlistpp
)->next
;
556 prevlun
= *lunlistpp
;
558 while (curlun
!= NULL
) {
559 if (curlun
->lunnum
== lun
) {
562 } else if (curlun
->lunnum
< lun
) {
563 newlun
->next
= curlun
;
564 prevlun
->next
= newlun
;
568 curlun
= curlun
->next
;
572 /* add the new lun at the end of list. */
573 prevlun
->next
= newlun
;
580 make_dyncomp_from_dinode(
581 const di_node_t node
,
585 di_prop_t prop
= DI_PROP_NIL
;
586 uchar_t
*port_wwn_data
;
590 *dyncompp
= calloc(1, WWN_SIZE
*2 + 1);
591 if (*dyncompp
== NULL
) {
593 return (FPCFGA_LIB_ERR
);
596 /* now get port-wwn for the input node. */
597 while ((prop
= di_prop_next(node
, prop
)) != DI_PROP_NIL
) {
598 if (!(strcmp(PORT_WWN_PROP
, di_prop_name(prop
))) &&
599 (di_prop_type(prop
) == DI_PROP_TYPE_BYTE
)) {
604 if (prop
!= DI_PROP_NIL
) {
605 count
= di_prop_bytes(prop
, &port_wwn_data
);
606 if (count
!= WWN_SIZE
) {
608 return (FPCFGA_LIB_ERR
);
611 (void) sprintf(*dyncompp
, "%016llx",
612 (wwnConversion(port_wwn_data
)));
616 return (FPCFGA_LIB_ERR
);
623 make_portwwn_luncomp_from_dinode(
624 const di_node_t node
,
629 uchar_t
*port_wwn_data
;
630 int pwwn_ret
, lun_ret
;
634 if ((dyncompp
!= NULL
) &&
635 ((pwwn_ret
= di_prop_lookup_bytes(DDI_DEV_T_ANY
,
636 node
, PORT_WWN_PROP
, &port_wwn_data
)) <= 0)) {
639 if ((luncompp
!= NULL
) &&
640 ((lun_ret
= di_prop_lookup_ints(DDI_DEV_T_ANY
,
641 node
, LUN_PROP
, luncompp
)) <= 0)) {
646 * di_prop* returns the number of entries found or 0 if not found
647 * or -1 for othere failure.
649 if ((pwwn_ret
<= 0) || (lun_ret
<= 0)) {
650 return (FPCFGA_LIB_ERR
);
653 *dyncompp
= calloc(1, WWN_SIZE
*2+1);
654 if (*dyncompp
== NULL
) {
656 return (FPCFGA_LIB_ERR
);
659 (void) sprintf(*dyncompp
, "%016llx", (wwnConversion(port_wwn_data
)));
665 make_portwwn_luncomp_from_pinode(
666 const di_path_t pinode
,
671 uchar_t
*port_wwn_data
;
672 int pwwn_ret
, lun_ret
;
676 if ((dyncompp
!= NULL
) &&
677 ((pwwn_ret
= di_path_prop_lookup_bytes(pinode
,
678 PORT_WWN_PROP
, &port_wwn_data
)) <= 0)) {
681 if ((luncompp
!= NULL
) &&
682 ((lun_ret
= di_path_prop_lookup_ints(pinode
,
683 LUN_PROP
, luncompp
)) <= 0)) {
688 * di_prop* returns the number of entries found or 0 if not found
689 * or -1 for othere failure.
691 if ((pwwn_ret
<= 0) || (lun_ret
<= 0)) {
692 return (FPCFGA_LIB_ERR
);
695 *dyncompp
= calloc(1, WWN_SIZE
*2+1);
696 if (*dyncompp
== NULL
) {
698 return (FPCFGA_LIB_ERR
);
701 (void) sprintf(*dyncompp
, "%016llx", (wwnConversion(port_wwn_data
)));
707 construct_nodepath_from_dinode(
708 const di_node_t node
,
712 char *devfs_path
, path_name
[MAXPATHLEN
], *lun_guid
, *port_wwn
;
713 uchar_t
*port_wwn_data
;
714 int is_scsi_vhci_dev
, di_ret
, devlen
;
717 devfs_path
= di_devfs_path(node
);
718 strcpy(path_name
, devfs_path
);
719 di_devfs_path_free(devfs_path
);
720 state
= di_state(node
);
722 is_scsi_vhci_dev
= (strstr(path_name
, SCSI_VHCI_DRVR
) != NULL
) ? 1 : 0;
726 * state then check the devfs_path to
727 * see if it has a complete path.
728 * For non scsi_vhci node the path
729 * doesn't contain @w(portwwn) part
730 * consistently. For scsi_vhci
731 * this behavior may not be there.
732 * To be safe @g(guid) is attempted
735 if (state
& DI_DRIVER_DETACHED
) {
736 if (is_scsi_vhci_dev
&&
737 (strstr(path_name
, "@g") == NULL
)) {
738 di_ret
= di_prop_lookup_strings(DDI_DEV_T_ANY
, node
,
739 LUN_GUID_PROP
, &lun_guid
);
742 return (FPCFGA_LIB_ERR
);
744 sprintf(&path_name
[strlen(path_name
)],
747 } else if (!is_scsi_vhci_dev
&&
748 (strstr(path_name
, "@w") == NULL
)) {
749 di_ret
= di_prop_lookup_bytes(DDI_DEV_T_ANY
, node
,
750 PORT_WWN_PROP
, &port_wwn_data
);
753 return (FPCFGA_LIB_ERR
);
755 if ((port_wwn
= calloc(1, WWN_SIZE
*2 + 1))
758 return (FPCFGA_LIB_ERR
);
761 (void) sprintf(port_wwn
, "%016llx",
762 (wwnConversion(port_wwn_data
)));
763 (void) sprintf(&path_name
[strlen(path_name
)],
770 devlen
= strlen(DEVICES_DIR
) + strlen(path_name
) + 1;
771 if ((*node_pathp
= calloc(1, devlen
)) == NULL
) {
773 return (FPCFGA_LIB_ERR
);
775 (void) snprintf(*node_pathp
, devlen
,
776 "%s%s", DEVICES_DIR
, path_name
);
783 wwnConversion(uchar_t
*wwn
)
786 memcpy(&tmp
, wwn
, sizeof (u_longlong_t
));
787 return (ntohll(tmp
));