summaryrefslogtreecommitdiff
path: root/usr/src/lib/cfgadm_plugins
diff options
context:
space:
mode:
authorJohn Forte <John.Forte@Sun.COM>2008-10-14 15:09:13 -0700
committerJohn Forte <John.Forte@Sun.COM>2008-10-14 15:09:13 -0700
commitfcf3ce441efd61da9bb2884968af01cb7c1452cc (patch)
tree0e80d59ad41702571586195bf099ccc14222ce02 /usr/src/lib/cfgadm_plugins
parent247b82a1f1cb5ebd2d163bd9afdb1a3065611962 (diff)
downloadillumos-joyent-fcf3ce441efd61da9bb2884968af01cb7c1452cc.tar.gz
6745433 Merge NWS consolidation into OS/Net consolidation
Diffstat (limited to 'usr/src/lib/cfgadm_plugins')
-rw-r--r--usr/src/lib/cfgadm_plugins/Makefile2
-rw-r--r--usr/src/lib/cfgadm_plugins/fp/Makefile73
-rw-r--r--usr/src/lib/cfgadm_plugins/fp/Makefile.com101
-rw-r--r--usr/src/lib/cfgadm_plugins/fp/amd64/Makefile32
-rw-r--r--usr/src/lib/cfgadm_plugins/fp/common/cfga_cs.c1606
-rw-r--r--usr/src/lib/cfgadm_plugins/fp/common/cfga_cvt.c788
-rw-r--r--usr/src/lib/cfgadm_plugins/fp/common/cfga_fp.c404
-rw-r--r--usr/src/lib/cfgadm_plugins/fp/common/cfga_fp.h547
-rw-r--r--usr/src/lib/cfgadm_plugins/fp/common/cfga_list.c3742
-rw-r--r--usr/src/lib/cfgadm_plugins/fp/common/cfga_rcm.c613
-rw-r--r--usr/src/lib/cfgadm_plugins/fp/common/cfga_rep.c614
-rw-r--r--usr/src/lib/cfgadm_plugins/fp/common/cfga_utils.c1372
-rw-r--r--usr/src/lib/cfgadm_plugins/fp/common/devices-fc-fabric.xml89
-rw-r--r--usr/src/lib/cfgadm_plugins/fp/common/fc-fabric45
-rw-r--r--usr/src/lib/cfgadm_plugins/fp/common/mapfile-vers39
-rw-r--r--usr/src/lib/cfgadm_plugins/fp/fp.xcl47
-rw-r--r--usr/src/lib/cfgadm_plugins/fp/i386/Makefile30
-rw-r--r--usr/src/lib/cfgadm_plugins/fp/sparc/Makefile31
-rw-r--r--usr/src/lib/cfgadm_plugins/fp/sparcv9/Makefile33
19 files changed, 10207 insertions, 1 deletions
diff --git a/usr/src/lib/cfgadm_plugins/Makefile b/usr/src/lib/cfgadm_plugins/Makefile
index 7611450bcc..39543eabd3 100644
--- a/usr/src/lib/cfgadm_plugins/Makefile
+++ b/usr/src/lib/cfgadm_plugins/Makefile
@@ -27,7 +27,7 @@
include $(SRC)/Makefile.master
-COMMON_SUBDIRS= scsi sdcard pci usb ib
+COMMON_SUBDIRS= scsi sdcard pci usb ib fp
sparc_SUBDIRS= sbd ac sysctrl
i386_SUBDIRS= sata
diff --git a/usr/src/lib/cfgadm_plugins/fp/Makefile b/usr/src/lib/cfgadm_plugins/fp/Makefile
new file mode 100644
index 0000000000..22bf6ff9df
--- /dev/null
+++ b/usr/src/lib/cfgadm_plugins/fp/Makefile
@@ -0,0 +1,73 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# lib/cfgadm_plugins/fp/Makefile
+#
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#
+
+include ../../Makefile.lib
+
+SUBDIRS = $(MACH)
+$(BUILD64)SUBDIRS += $(MACH64)
+
+all := TARGET= all
+clean := TARGET= clean
+clobber := TARGET= clobber
+install := TARGET= install
+lint := TARGET= lint
+_msg := TARGET= _msg
+
+TEXT_DOMAIN= SUNW_OST_OSLIB
+XGETFLAGS= -a -x fp.xcl
+POFILE= fp.po
+POFILES= generic.po
+
+SED= sed
+GREP= grep
+CP= cp
+
+.KEEP_STATE:
+
+all lint clean clobber delete install package: $(SUBDIRS)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+_msg: $(MSGDOMAIN) $(POFILE)
+ $(RM) $(MSGDOMAIN)/$(POFILE)
+ $(CP) $(POFILE) $(MSGDOMAIN)
+
+$(POFILE): $(POFILES)
+ $(RM) $@
+ $(CAT) $(POFILES) > $@
+
+$(POFILES):
+ $(RM) messages.po
+ $(XGETTEXT) $(XGETFLAGS) `$(GREP) -l gettext */*.[ch]`
+ $(SED) -e '/^# msg/d' -e '/^domain/d' messages.po > $@
+ $(RM) messages.po
+
+FRC:
+
+include ../../Makefile.targ
diff --git a/usr/src/lib/cfgadm_plugins/fp/Makefile.com b/usr/src/lib/cfgadm_plugins/fp/Makefile.com
new file mode 100644
index 0000000000..02697d0db7
--- /dev/null
+++ b/usr/src/lib/cfgadm_plugins/fp/Makefile.com
@@ -0,0 +1,101 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#
+
+LIBRARY= fp.a
+VERS= .1
+
+OBJECTS = cfga_fp.o cfga_rcm.o cfga_cs.o cfga_utils.o
+OBJECTS += cfga_cvt.o cfga_list.o cfga_rep.o
+
+# include library definitions
+include ../../../Makefile.lib
+
+SRCDIR = ../common
+ROOTLIBDIR= $(ROOT)/usr/lib/cfgadm
+ROOTLIBDIR64= $(ROOTLIBDIR)/$(MACH64)
+
+LIBS= $(DYNLIB)
+
+LINTFLAGS += -DDEBUG
+LINTFLAGS64 += -DDEBUG
+
+CFLAGS += $(CCVERBOSE)
+CFLAGS64 += $(CCVERBOSE)
+
+CPPFLAGS += -D_POSIX_PTHREAD_SEMANTICS
+
+LINTFLAGS += -erroff=E_SEC_SPRINTF_UNBOUNDED_COPY
+LINTFLAGS += -erroff=E_FUNC_RET_MAYBE_IGNORED2
+LINTFLAGS += -erroff=E_FUNC_RET_ALWAYS_IGNOR2
+LINTFLAGS += -erroff=E_BAD_FORMAT_ARG_TYPE2
+LINTFLAGS += -erroff=E_SEC_CREAT_WITHOUT_MODE
+LINTFLAGS64 += $(LINTFLAGS)
+
+LDLIBS += -lc -ldevice -ldevinfo -lrcm
+LDLIBS += -lHBAAPI -lgen
+
+MANIFEST= devices-fc-fabric.xml
+ROOTMANIFESTDIR= $(ROOT)/var/svc/manifest/system/device
+ROOTMANIFEST= $(MANIFEST:%=$(ROOTMANIFESTDIR)/%)
+$(ROOTMANIFEST) := FILEMODE= 444
+
+SVCMETHOD= fc-fabric
+ROOTSVCMETHODDIR= $(ROOT)/lib/svc/method
+ROOTSVCMETHOD= $(SVCMETHOD:%=$(ROOTSVCMETHODDIR)/%)
+$(ROOTSVCMETHOD):= FILEMODE= 555
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+lint: lintcheck
+
+# Install rules
+
+$(ROOTLIBDIR)/%: % $(ROOTLIBDIR)
+ $(INS.file)
+
+$(ROOTLIBDIR64)/%: % $(ROOTLIBDIR64)
+ $(INS.file)
+
+$(ROOTLIBDIR) $(ROOTLIBDIR64):
+ $(INS.dir)
+
+$(ROOTMANIFESTDIR)/%: ../common/% $(ROOTMANIFESTDIR)
+ $(INS.file)
+
+$(ROOTSVCMETHODDIR)/%: ../common/% $(ROOTSVCMETHODDIR)
+ $(INS.file)
+
+$(ROOTSVCMETHODDIR) $(ROOTMANIFESTDIR):
+ $(INS.dir)
+
+# include library targets
+include ../../../Makefile.targ
+
+objs/%.o pics/%.o: ../common/%.c
+ $(COMPILE.c) -o $@ $<
+ $(POST_PROCESS_O)
diff --git a/usr/src/lib/cfgadm_plugins/fp/amd64/Makefile b/usr/src/lib/cfgadm_plugins/fp/amd64/Makefile
new file mode 100644
index 0000000000..4c3452781e
--- /dev/null
+++ b/usr/src/lib/cfgadm_plugins/fp/amd64/Makefile
@@ -0,0 +1,32 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#
+
+include ../Makefile.com
+include ../../../Makefile.lib.64
+
+.KEEP_STATE:
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64)
diff --git a/usr/src/lib/cfgadm_plugins/fp/common/cfga_cs.c b/usr/src/lib/cfgadm_plugins/fp/common/cfga_cs.c
new file mode 100644
index 0000000000..a0bd3741d7
--- /dev/null
+++ b/usr/src/lib/cfgadm_plugins/fp/common/cfga_cs.c
@@ -0,0 +1,1606 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#include "cfga_fp.h"
+
+/* define */
+#define ALL_APID_LUNS_UNUSABLE 0x10
+
+#define DEFAULT_LUN_COUNT 1024
+#define LUN_SIZE 8
+#define LUN_HEADER_SIZE 8
+#define DEFAULT_LUN_LENGTH DEFAULT_LUN_COUNT * \
+ LUN_SIZE + \
+ LUN_HEADER_SIZE
+
+/* Some forward declarations */
+static fpcfga_ret_t do_devctl_dev_create(apid_t *, char *, int,
+ uchar_t, char **);
+static fpcfga_ret_t dev_rcm_online(apid_t *, int, cfga_flags_t, char **);
+static void dev_rcm_online_nonoperationalpath(apid_t *, cfga_flags_t, char **);
+static fpcfga_ret_t dev_rcm_offline(apid_t *, cfga_flags_t, char **);
+static fpcfga_ret_t dev_rcm_remove(apid_t *, cfga_flags_t, char **);
+static fpcfga_ret_t lun_unconf(char *, int, char *, char *, char **);
+static fpcfga_ret_t dev_unconf(apid_t *, char **, uchar_t *);
+static fpcfga_ret_t is_xport_phys_in_pathlist(apid_t *, char **);
+static void copy_pwwn_data_to_str(char *, const uchar_t *);
+static fpcfga_ret_t unconf_vhci_nodes(di_path_t, di_node_t, char *,
+ char *, int *, int *, char **, cfga_flags_t);
+static fpcfga_ret_t unconf_non_vhci_nodes(di_node_t, char *, char *,
+ int *, int *, char **, cfga_flags_t);
+static fpcfga_ret_t unconf_any_devinfo_nodes(apid_t *, cfga_flags_t, char **,
+ int *, int *);
+static fpcfga_ret_t handle_devs(cfga_cmd_t, apid_t *, cfga_flags_t,
+ char **, HBA_HANDLE, int, HBA_PORTATTRIBUTES);
+
+
+/*
+ * This function initiates the creation of the new device node for a given
+ * port WWN.
+ * So, apidt->dyncomp CANNOT be NULL
+ */
+static fpcfga_ret_t
+do_devctl_dev_create(apid_t *apidt, char *dev_path, int pathlen,
+ uchar_t dev_dtype, char **errstring)
+{
+ devctl_ddef_t ddef_hdl;
+ devctl_hdl_t bus_hdl, dev_hdl;
+ char *drvr_name = "dummy";
+ la_wwn_t pwwn;
+
+ *dev_path = NULL;
+ if ((ddef_hdl = devctl_ddef_alloc(drvr_name, 0)) == NULL) {
+ cfga_err(errstring, errno, ERRARG_DC_DDEF_ALLOC, drvr_name, 0);
+ return (FPCFGA_LIB_ERR);
+ }
+
+ if (cvt_dyncomp_to_lawwn(apidt->dyncomp, &pwwn)) {
+ devctl_ddef_free(ddef_hdl);
+ cfga_err(errstring, 0, ERR_APID_INVAL, 0);
+ return (FPCFGA_LIB_ERR);
+ }
+
+ if (devctl_ddef_byte_array(ddef_hdl, PORT_WWN_PROP, FC_WWN_SIZE,
+ pwwn.raw_wwn) == -1) {
+ devctl_ddef_free(ddef_hdl);
+ cfga_err(errstring, errno, ERRARG_DC_BYTE_ARRAY,
+ PORT_WWN_PROP, 0);
+ return (FPCFGA_LIB_ERR);
+ }
+
+ if ((bus_hdl = devctl_bus_acquire(apidt->xport_phys, 0)) == NULL) {
+ devctl_ddef_free(ddef_hdl);
+ cfga_err(errstring, errno, ERRARG_DC_BUS_ACQUIRE,
+ apidt->xport_phys, 0);
+ return (FPCFGA_LIB_ERR);
+ }
+
+ /* Let driver handle creation of the new path */
+ if (devctl_bus_dev_create(bus_hdl, ddef_hdl, 0, &dev_hdl)) {
+ devctl_ddef_free(ddef_hdl);
+ devctl_release(bus_hdl);
+ if (dev_dtype == DTYPE_UNKNOWN) {
+ /*
+ * Unknown DTYPES are devices such as another system's
+ * FC HBA port. We have tried to configure it but
+ * have failed. Since devices with no device type
+ * or an unknown dtype cannot be configured, we will
+ * return an appropriate error message.
+ */
+ cfga_err(errstring, errno,
+ ERRARG_BUS_DEV_CREATE_UNKNOWN, apidt->dyncomp, 0);
+ } else {
+ cfga_err(errstring, errno, ERRARG_BUS_DEV_CREATE,
+ apidt->dyncomp, 0);
+ }
+ return (FPCFGA_LIB_ERR);
+ }
+ devctl_release(bus_hdl);
+ devctl_ddef_free(ddef_hdl);
+
+ devctl_get_pathname(dev_hdl, dev_path, pathlen);
+ devctl_release(dev_hdl);
+
+ return (FPCFGA_OK);
+}
+
+/*
+ * Online, in RCM, all the LUNs for a particular device.
+ * Caller can specify the # of luns in the lunlist that have to be onlined
+ * by passing a count that is not -ve.
+ *
+ * INPUT :
+ * apidt - this is expected to have the list of luns for the device and so
+ * is assumed to be filled in prior to this call
+ * count - # of LUNs in the list that have to be onlined.
+ * errstring - If non-NULL, it will hold any error messages
+ *
+ * RETURNS :
+ * 0 on success
+ * non-zero otherwise
+ */
+static fpcfga_ret_t
+dev_rcm_online(apid_t *apidt, int count, cfga_flags_t flags, char **errstring)
+{
+ luninfo_list_t *lunlistp;
+ int i = 0, ret = 0;
+ fpcfga_ret_t retval = FPCFGA_OK;
+
+ /* This check may be redundant, but safer this way */
+ if ((apidt->flags & FLAG_DISABLE_RCM) != 0) {
+ /* User has requested not to notify RCM framework */
+ return (FPCFGA_OK);
+ }
+
+ lunlistp = apidt->lunlist;
+
+ for (lunlistp = apidt->lunlist; lunlistp != NULL;
+ i++, lunlistp = lunlistp->next) {
+ if ((count >= 0) && (i >= count))
+ break;
+ if (fp_rcm_online(lunlistp->path, errstring, flags) !=
+ FPCFGA_OK) {
+ ret++;
+ }
+ }
+
+ if (ret > 0)
+ retval = FPCFGA_LIB_ERR;
+
+ return (retval);
+}
+
+/*
+ * Online in RCM for devices which only have paths
+ * not in ONLINE/STANDBY state
+ */
+void
+dev_rcm_online_nonoperationalpath(apid_t *apidt, cfga_flags_t flags,
+ char **errstring)
+{
+ luninfo_list_t *lunlistp;
+
+ if ((apidt->flags & FLAG_DISABLE_RCM) != 0) {
+ return;
+ }
+
+ lunlistp = apidt->lunlist;
+
+ for (lunlistp = apidt->lunlist; lunlistp != NULL;
+ lunlistp = lunlistp->next) {
+ if ((lunlistp->lun_flag & FLAG_SKIP_ONLINEOTHERS) != 0) {
+ continue;
+ }
+ (void) fp_rcm_online(lunlistp->path, errstring, flags);
+ }
+}
+
+/*
+ * Offline, in RCM, all the LUNs for a particular device.
+ * This function should not be called for the MPXIO case.
+ *
+ * INPUT :
+ * apidt - this is expected to have the list of luns for the device and so
+ * is assumed to be filled in prior to this call
+ * errstring - If non-NULL, it will hold any error messages
+ *
+ * RETURNS :
+ * FPCFGA_OK on success
+ * error code otherwise
+ */
+static fpcfga_ret_t
+dev_rcm_offline(apid_t *apidt, cfga_flags_t flags, char **errstring)
+{
+ int count = 0;
+ luninfo_list_t *lunlistp;
+
+ if ((apidt->flags & FLAG_DISABLE_RCM) != 0) {
+ /* User has requested not to notify RCM framework */
+ return (FPCFGA_OK);
+ }
+
+ for (lunlistp = apidt->lunlist; lunlistp != NULL;
+ lunlistp = lunlistp->next) {
+ if ((lunlistp->lun_flag & FLAG_SKIP_RCMOFFLINE) != 0) {
+ continue;
+ }
+ if ((apidt->flags & FLAG_REMOVE_UNUSABLE_FCP_DEV) ==
+ FLAG_REMOVE_UNUSABLE_FCP_DEV) {
+ int ret = strncmp(lunlistp->path, SCSI_VHCI_ROOT,
+ strlen(SCSI_VHCI_ROOT));
+
+ if (((ret == 0) &&
+ (lunlistp->node_state == DI_PATH_STATE_OFFLINE)) ||
+ ((ret != 0) &&
+ ((lunlistp->node_state & DI_DEVICE_OFFLINE) ==
+ DI_DEVICE_OFFLINE))) {
+ /* Offline the device through RCM */
+ if (fp_rcm_offline(lunlistp->path, errstring,
+ flags) != 0) {
+ /*
+ * Bring everything back online in
+ * rcm and return
+ */
+ (void) dev_rcm_online(apidt, count,
+ flags, NULL);
+ return (FPCFGA_LIB_ERR);
+ }
+ count++;
+ }
+ } else {
+ /* Offline the device through RCM */
+ if (fp_rcm_offline(lunlistp->path, errstring,
+ flags) != 0) {
+ /*
+ * Bring everything back online in
+ * rcm and return
+ */
+ (void) dev_rcm_online(apidt, count, flags,
+ NULL);
+ return (FPCFGA_LIB_ERR);
+ }
+ count++;
+ }
+ }
+ return (FPCFGA_OK);
+}
+
+/*
+ * Remove, in RCM, all the LUNs for a particular device.
+ * This function should not be called for the MPXIO case.
+ *
+ * INPUT :
+ * apidt - this is expected to have the list of luns for the device and so
+ * is assumed to be filled in prior to this call
+ * errstring - If non-NULL, it will hold any error messages
+ *
+ * RETURNS :
+ * FPCFGA_OK on success
+ * error code otherwise
+ */
+static fpcfga_ret_t
+dev_rcm_remove(apid_t *apidt, cfga_flags_t flags, char **errstring)
+{
+ int count = 0;
+ luninfo_list_t *lunlistp;
+
+ if ((apidt->flags & FLAG_DISABLE_RCM) != 0) {
+ /* User has requested not to notify RCM framework */
+ return (FPCFGA_OK);
+ }
+
+ for (lunlistp = apidt->lunlist; lunlistp != NULL;
+ lunlistp = lunlistp->next) {
+ if ((lunlistp->lun_flag & FLAG_SKIP_RCMREMOVE) != 0)
+ continue;
+ if ((apidt->flags & FLAG_REMOVE_UNUSABLE_FCP_DEV) ==
+ FLAG_REMOVE_UNUSABLE_FCP_DEV) {
+ int ret = strncmp(lunlistp->path, SCSI_VHCI_ROOT,
+ strlen(SCSI_VHCI_ROOT));
+
+ if (((ret == 0) &&
+ (lunlistp->node_state == DI_PATH_STATE_OFFLINE)) ||
+ ((ret != 0) &&
+ ((lunlistp->node_state & DI_DEVICE_OFFLINE) ==
+ DI_DEVICE_OFFLINE))) {
+ /* remove the device through RCM */
+ if (fp_rcm_remove(lunlistp->path, errstring,
+ flags) != 0) {
+ /*
+ * Bring everything back online in
+ * rcm and return
+ */
+ (void) dev_rcm_online(apidt, count,
+ flags, NULL);
+ return (FPCFGA_LIB_ERR);
+ }
+ count++;
+ }
+ } else {
+ /* remove the device through RCM */
+ if (fp_rcm_remove(lunlistp->path, errstring,
+ flags) != 0) {
+ /*
+ * Bring everything back online in rcm and
+ * return
+ */
+ (void) dev_rcm_online(apidt, count, flags,
+ NULL);
+ return (FPCFGA_LIB_ERR);
+ }
+ count++;
+ }
+ }
+ return (FPCFGA_OK);
+}
+
+static fpcfga_ret_t
+lun_unconf(char *path, int lunnum, char *xport_phys, char *dyncomp,
+ char **errstring)
+{
+ devctl_hdl_t hdl;
+ char *ptr; /* To use as scratch/temp pointer */
+ char pathname[MAXPATHLEN];
+
+ if (path == NULL)
+ return (FPCFGA_OK);
+
+ if (strncmp(path, SCSI_VHCI_ROOT, strlen(SCSI_VHCI_ROOT)) == 0) {
+ /*
+ * We have an MPXIO managed device here.
+ * So, we have to concoct a path for the device.
+ *
+ * xport_phys looks like :
+ * /devices/pci@b,2000/pci@1/SUNW,qlc@5/fp@0,0:fc
+ */
+ (void) strlcpy(pathname, xport_phys, MAXPATHLEN);
+ if ((ptr = strrchr(pathname, ':')) != NULL) {
+ *ptr = '\0';
+ }
+
+ /*
+ * Get pointer to driver name from VHCI path
+ * So, if lunlistp->path is
+ * /devices/scsi_vhci/ssd@g220000203707a417,
+ * we need a pointer to the last '/'
+ *
+ * Assumption:
+ * With MPXIO there will be only one entry per lun
+ * So, there will only be one entry in the linked list
+ * apidt->lunlist
+ */
+ if ((ptr = strrchr(path, '/')) == NULL) {
+ /* This shouldn't happen, but anyways ... */
+ cfga_err(errstring, 0, ERRARG_INVALID_PATH, path, 0);
+ return (FPCFGA_LIB_ERR);
+ }
+
+ /*
+ * Make pathname to look something like :
+ * /devices/pci@x,xxxx/pci@x/SUNW,qlc@x/fp@x,x/ssd@w...
+ */
+ strcat(pathname, ptr);
+
+ /*
+ * apidt_create() will make sure that lunlist->path
+ * has a "@<something>" at the end even if the driver
+ * state is "detached"
+ */
+ if ((ptr = strrchr(pathname, '@')) == NULL) {
+ /* This shouldn't happen, but anyways ... */
+ cfga_err(errstring, 0, ERRARG_INVALID_PATH,
+ pathname, 0);
+ return (FPCFGA_LIB_ERR);
+ }
+ *ptr = '\0';
+
+ /* Now, concoct the path */
+ sprintf(&pathname[strlen(pathname)], "@w%s,%x",
+ dyncomp, lunnum);
+ ptr = pathname;
+ } else {
+ /*
+ * non-MPXIO path, use the path that is passed in
+ */
+ ptr = path;
+ }
+
+ if ((hdl = devctl_device_acquire(ptr, 0)) == NULL) {
+ cfga_err(errstring, errno, ERRARG_DEV_ACQUIRE, ptr, 0);
+ return (FPCFGA_LIB_ERR);
+ }
+
+ if (devctl_device_remove(hdl) != 0) {
+ devctl_release(hdl);
+ cfga_err(errstring, errno, ERRARG_DEV_REMOVE, ptr, 0);
+ return (FPCFGA_LIB_ERR);
+ }
+ devctl_release(hdl);
+
+ return (FPCFGA_OK);
+}
+
+static fpcfga_ret_t
+dev_unconf(apid_t *apidt, char **errstring, uchar_t *flag)
+{
+ luninfo_list_t *lunlistp;
+ fpcfga_ret_t ret = FPCFGA_OK;
+ int lun_cnt = 0, unusable_lun_cnt = 0;
+
+ for (lunlistp = apidt->lunlist; lunlistp != NULL;
+ lunlistp = lunlistp->next) {
+ lun_cnt++;
+ /*
+ * Unconfigure each LUN.
+ * Note that for MPXIO devices, lunlistp->path will be a
+ * vHCI path
+ */
+ if ((apidt->flags & FLAG_REMOVE_UNUSABLE_FCP_DEV) ==
+ FLAG_REMOVE_UNUSABLE_FCP_DEV) {
+ if (strncmp(lunlistp->path, SCSI_VHCI_ROOT,
+ strlen(SCSI_VHCI_ROOT)) == 0) {
+ if (lunlistp->node_state ==
+ DI_PATH_STATE_OFFLINE) {
+ unusable_lun_cnt++;
+ if ((ret = lun_unconf(lunlistp->path,
+ lunlistp->lunnum, apidt->xport_phys,
+ apidt->dyncomp, errstring)) != FPCFGA_OK) {
+ return (ret);
+ }
+ }
+ } else {
+ if ((lunlistp->node_state & DI_DEVICE_OFFLINE) ==
+ DI_DEVICE_OFFLINE) {
+ unusable_lun_cnt++;
+ if ((ret = lun_unconf(lunlistp->path,
+ lunlistp->lunnum, apidt->xport_phys,
+ apidt->dyncomp, errstring)) != FPCFGA_OK) {
+ return (ret);
+ }
+ }
+ }
+ } else {
+ /*
+ * Unconfigure each LUN.
+ * Note that for MPXIO devices, lunlistp->path will be a
+ * vHCI path
+ */
+ if ((ret = lun_unconf(lunlistp->path, lunlistp->lunnum,
+ apidt->xport_phys, apidt->dyncomp,
+ errstring)) != FPCFGA_OK) {
+ return (ret);
+ }
+ }
+ }
+
+ if ((apidt->flags & FLAG_REMOVE_UNUSABLE_FCP_DEV) ==
+ FLAG_REMOVE_UNUSABLE_FCP_DEV) {
+ /*
+ * when all luns are unconfigured
+ * indicate to remove repository entry.
+ */
+ if (lun_cnt == unusable_lun_cnt) {
+ *flag = ALL_APID_LUNS_UNUSABLE;
+ }
+ }
+
+ return (ret);
+}
+
+/*
+ * Check if the given physical path (the xport_phys) is part of the
+ * pHCI list and if the RCM should be done for a particular pHCI.
+ * Skip non-MPxIO dev node if any.
+ */
+static fpcfga_ret_t
+is_xport_phys_in_pathlist(apid_t *apidt, char **errstring)
+{
+ di_node_t root, vhci, node, phci;
+ di_path_t path = DI_PATH_NIL;
+ int num_active_paths, found = 0;
+ char *vhci_path_ptr, *pathname_ptr, pathname[MAXPATHLEN];
+ char *phci_path, *node_path;
+ char phci_addr[MAXPATHLEN];
+ char *xport_phys, *vhci_path, *dyncomp;
+ luninfo_list_t *lunlistp, *temp;
+ int non_operational_path_count;
+
+ /* a safety check */
+ if ((apidt->dyncomp == NULL) || (*apidt->dyncomp == '\0')) {
+ return (FPCFGA_LIB_ERR);
+ }
+
+ xport_phys = apidt->xport_phys;
+ dyncomp = apidt->dyncomp;
+
+ lunlistp = apidt->lunlist;
+ for (lunlistp = apidt->lunlist; lunlistp != NULL;
+ lunlistp = lunlistp->next) {
+
+ if (strncmp(lunlistp->path, SCSI_VHCI_ROOT,
+ strlen(SCSI_VHCI_ROOT)) != 0) {
+ lunlistp->lun_flag |= FLAG_SKIP_ONLINEOTHERS;
+ continue;
+ }
+
+ vhci_path = lunlistp->path;
+
+ num_active_paths = 0; /* # of paths in ONLINE/STANDBY */
+ non_operational_path_count = 0;
+
+ if (xport_phys == NULL || vhci_path == NULL) {
+ cfga_err(errstring, 0, ERRARG_XPORT_NOT_IN_PHCI_LIST,
+ xport_phys, 0);
+ return (FPCFGA_LIB_ERR);
+ }
+
+ (void) strlcpy(pathname, xport_phys, MAXPATHLEN);
+ if ((pathname_ptr = strrchr(pathname, ':')) != NULL) {
+ *pathname_ptr = '\0';
+ }
+ /* strip off the /devices/from the path */
+ pathname_ptr = pathname + strlen(DEVICES_DIR);
+
+ root = di_init("/", DINFOCPYALL|DINFOPATH);
+
+ if (root == DI_NODE_NIL) {
+ return (FPCFGA_LIB_ERR);
+ }
+
+ vhci_path_ptr = vhci_path + strlen(DEVICES_DIR);
+ if ((vhci = di_drv_first_node(SCSI_VHCI_DRVR, root)) ==
+ DI_NODE_NIL) {
+ return (FPCFGA_LIB_ERR);
+ }
+ found = 0;
+ for (node = di_child_node(vhci); node != DI_NODE_NIL;
+ node = di_sibling_node(node)) {
+ if ((node_path = di_devfs_path(node)) != NULL) {
+ if (strncmp(vhci_path_ptr, node_path,
+ strlen(node_path)) != 0) {
+ di_devfs_path_free(node_path);
+ } else {
+ found = 1;
+ break;
+ }
+ }
+ }
+ if (found == 0) {
+ cfga_err(errstring, 0, ERRARG_XPORT_NOT_IN_PHCI_LIST,
+ xport_phys, 0);
+ di_fini(root);
+ return (FPCFGA_LIB_ERR);
+ }
+ /* found vhci_path we are looking for */
+ di_devfs_path_free(node_path);
+ found = 0;
+ for (path = di_path_next_phci(node, DI_PATH_NIL);
+ path != DI_PATH_NIL;
+ path = di_path_next_phci(node, path)) {
+ if ((phci = di_path_phci_node(path)) == DI_NODE_NIL) {
+ cfga_err(errstring, 0,
+ ERRARG_XPORT_NOT_IN_PHCI_LIST,
+ xport_phys, 0);
+ di_fini(root);
+ return (FPCFGA_LIB_ERR);
+ }
+ if ((phci_path = di_devfs_path(phci)) == NULL) {
+ cfga_err(errstring, 0,
+ ERRARG_XPORT_NOT_IN_PHCI_LIST,
+ xport_phys, 0);
+ di_fini(root);
+ return (FPCFGA_LIB_ERR);
+ }
+ (void) di_path_addr(path, (char *)phci_addr);
+ if ((phci_addr == NULL) || (*phci_addr == '\0')) {
+ cfga_err(errstring, 0,
+ ERRARG_XPORT_NOT_IN_PHCI_LIST,
+ xport_phys, 0);
+ di_devfs_path_free(phci_path);
+ di_fini(root);
+ return (FPCFGA_LIB_ERR);
+ }
+ /*
+ * Check if the phci path has the same
+ * xport addr and the target addr with current lun
+ */
+ if ((strncmp(phci_path, pathname_ptr,
+ strlen(pathname_ptr)) == 0) &&
+ (strstr(phci_addr, dyncomp) != NULL)) {
+ /* SUCCESS Found xport_phys */
+ found = 1;
+ } else if ((di_path_state(path) ==
+ DI_PATH_STATE_ONLINE) ||
+ (di_path_state(path) == DI_PATH_STATE_STANDBY)) {
+ num_active_paths++;
+ } else {
+ /*
+ * We have another path not in ONLINE/STANDBY
+ * state now, so should do a RCM online after
+ * the unconfiguration of current path.
+ */
+ non_operational_path_count++;
+ }
+ di_devfs_path_free(phci_path);
+ }
+ di_fini(root);
+ if (found == 1) {
+ if (num_active_paths != 0) {
+ /*
+ * There are other ONLINE/STANDBY paths,
+ * so no need to do the RCM
+ */
+ lunlistp->lun_flag |= FLAG_SKIP_RCMREMOVE;
+ lunlistp->lun_flag |= FLAG_SKIP_RCMOFFLINE;
+ }
+ if (non_operational_path_count == 0) {
+ lunlistp->lun_flag |= FLAG_SKIP_ONLINEOTHERS;
+ }
+ } else {
+ /*
+ * Fail all operations here
+ */
+ cfga_err(errstring, 0, ERRARG_XPORT_NOT_IN_PHCI_LIST,
+ xport_phys, 0);
+ return (FPCFGA_APID_NOEXIST);
+ }
+ }
+
+ /* Mark duplicated paths for same vhci in the list */
+ for (lunlistp = apidt->lunlist; lunlistp != NULL;
+ lunlistp = lunlistp->next) {
+ if (strncmp(lunlistp->path, SCSI_VHCI_ROOT,
+ strlen(SCSI_VHCI_ROOT)) != 0) {
+ continue;
+ }
+ for (temp = lunlistp->next; temp != NULL;
+ temp = temp->next) {
+ if (strcmp(lunlistp->path, temp->path) == 0) {
+ /*
+ * don't do RCM for dup
+ */
+ lunlistp->lun_flag |= FLAG_SKIP_RCMREMOVE;
+ lunlistp->lun_flag |= FLAG_SKIP_RCMOFFLINE;
+ lunlistp->lun_flag |= FLAG_SKIP_ONLINEOTHERS;
+ }
+ }
+ }
+ return (FPCFGA_OK);
+}
+/*
+ * apidt->dyncomp has to be non-NULL by the time this routine is called
+ */
+fpcfga_ret_t
+dev_change_state(cfga_cmd_t state_change_cmd, apid_t *apidt, la_wwn_t *pwwn,
+ cfga_flags_t flags, char **errstring, HBA_HANDLE handle,
+ HBA_PORTATTRIBUTES portAttrs)
+{
+ char dev_path[MAXPATHLEN];
+ char *update_str, *t_apid;
+ int optflag = apidt->flags;
+ int no_config_attempt = 0;
+ fpcfga_ret_t ret;
+ apid_t my_apidt;
+ uchar_t unconf_flag = 0, peri_qual;
+ HBA_STATUS status;
+ HBA_PORTATTRIBUTES discPortAttrs;
+ uint64_t lun = 0;
+ struct scsi_inquiry inq;
+ struct scsi_extended_sense sense;
+ HBA_UINT8 scsiStatus;
+ uint32_t inquirySize = sizeof (inq),
+ senseSize = sizeof (sense);
+ report_lun_resp_t *resp_buf;
+ int i, l_errno, num_luns = 0;
+ uchar_t *lun_string;
+
+ if ((apidt->dyncomp == NULL) || (*apidt->dyncomp == '\0')) {
+ /*
+ * No dynamic component specified. Just return success.
+ * Should not see this case. Just a safety check.
+ */
+ return (FPCFGA_OK);
+ }
+
+ /* Now construct the string we are going to put in the repository */
+ if ((update_str = calloc(1, (strlen(apidt->xport_phys) +
+ strlen(DYN_SEP) + strlen(apidt->dyncomp) + 1))) == NULL) {
+ cfga_err(errstring, errno, ERR_MEM_ALLOC, 0);
+ return (FPCFGA_LIB_ERR);
+ }
+ strcpy(update_str, apidt->xport_phys);
+ strcat(update_str, DYN_SEP);
+ strcat(update_str, apidt->dyncomp);
+
+ /* If force update of repository is sought, do it first */
+ if (optflag & FLAG_FORCE_UPDATE_REP) {
+ /* Ignore any failure in rep update */
+ (void) update_fabric_wwn_list(
+ ((state_change_cmd == CFGA_CMD_CONFIGURE) ?
+ ADD_ENTRY : REMOVE_ENTRY),
+ update_str, errstring);
+ }
+
+ memset(&sense, 0, sizeof (sense));
+ if ((ret = get_report_lun_data(apidt->xport_phys, apidt->dyncomp,
+ &num_luns, &resp_buf, &sense, &l_errno)) != FPCFGA_OK) {
+ /*
+ * Checking the sense key data as well as the additional
+ * sense key. The SES Node is not required to repond
+ * to Report LUN. In the case of Minnow, the SES node
+ * returns with KEY_ILLEGAL_REQUEST and the additional
+ * sense key of 0x20. In this case we will blindly
+ * send the SCSI Inquiry call to lun 0
+ *
+ * if we get any other error we will set the inq_type
+ * appropriately
+ */
+ if ((sense.es_key == KEY_ILLEGAL_REQUEST) &&
+ (sense.es_add_code == 0x20)) {
+ lun = 0;
+ } else {
+ if (ret == FPCFGA_FCP_SEND_SCSI_DEV_NOT_TGT) {
+ inq.inq_dtype = DTYPE_UNKNOWN;
+ } else {
+ /*
+ * Failed to get the LUN data for the device
+ * If we find that there is a lunlist for this
+ * device it could mean that there are dangling
+ * devinfo nodes. So, we will go ahead and try
+ * to unconfigure them.
+ */
+ if ((apidt->lunlist == NULL) ||
+ (state_change_cmd == CFGA_CMD_CONFIGURE)) {
+ S_FREE(update_str);
+ status = getPortAttrsByWWN(handle,
+ *((HBA_WWN *)(pwwn)),
+ &discPortAttrs);
+ if (status ==
+ HBA_STATUS_ERROR_ILLEGAL_WWN) {
+ return (FPCFGA_APID_NOEXIST);
+ } else {
+ cfga_err(errstring, 0,
+ ERRARG_FC_REP_LUNS,
+ apidt->dyncomp, 0);
+ return (FPCFGA_LIB_ERR);
+ }
+ } else {
+ /* unconfig with lunlist not empty */
+ no_config_attempt++;
+ }
+ }
+ }
+ }
+ for (i = 0; i < num_luns; i++) {
+ /*
+ * issue the inquiry to the first valid lun found
+ * in the lun_string
+ */
+ lun_string = (uchar_t *)&(resp_buf->lun_string[i]);
+ memcpy(&lun, lun_string, sizeof (lun));
+
+ memset(&sense, 0, sizeof (sense));
+ status = HBA_ScsiInquiryV2(handle, portAttrs.PortWWN,
+ *(HBA_WWN *)(pwwn), lun, 0, 0, &inq, &inquirySize,
+ &scsiStatus, &sense, &senseSize);
+ /*
+ * if Inquiry is returned correctly, check the
+ * peripheral qualifier for the lun. if it is non-zero
+ * then try the SCSI Inquiry on the next lun
+ */
+ if (status == HBA_STATUS_OK) {
+ peri_qual = inq.inq_dtype & FP_PERI_QUAL_MASK;
+ if (peri_qual == DPQ_POSSIBLE) {
+ break;
+ }
+ }
+ }
+
+ if (ret == FPCFGA_OK)
+ S_FREE(resp_buf);
+
+ /*
+ * If there are no luns on this target, we will attempt to send
+ * the SCSI Inquiry to lun 0
+ */
+ if (num_luns == 0) {
+ lun = 0;
+ status = HBA_ScsiInquiryV2(handle, portAttrs.PortWWN,
+ *(HBA_WWN *)(pwwn), lun, 0, 0, &inq, &inquirySize,
+ &scsiStatus, &sense, &senseSize);
+ }
+
+ if (status != HBA_STATUS_OK) {
+ if (status == HBA_STATUS_ERROR_NOT_A_TARGET) {
+ inq.inq_dtype = DTYPE_UNKNOWN;
+ } else if (status == HBA_STATUS_ERROR_ILLEGAL_WWN) {
+ free(update_str);
+ return (FPCFGA_APID_NOEXIST);
+ } else {
+ /*
+ * Failed to get the inq_dtype of device
+ * If we find that there is a lunlist for this
+ * device it could mean that there dangling
+ * devinfo nodes. So, we will go ahead and try
+ * to unconfigure them. We'll just set the
+ * inq_dtype to some invalid value (0xFF)
+ */
+ if ((apidt->lunlist == NULL) ||
+ (state_change_cmd == CFGA_CMD_CONFIGURE)) {
+ cfga_err(errstring, 0,
+ ERRARG_FC_INQUIRY,
+ apidt->dyncomp, 0);
+ free(update_str);
+ return (FPCFGA_LIB_ERR);
+ } else {
+ /* unconfig with lunlist not empty */
+ no_config_attempt++;
+ }
+ }
+ }
+ switch (state_change_cmd) {
+ case CFGA_CMD_CONFIGURE:
+ if (portAttrs.PortType != HBA_PORTTYPE_NLPORT &&
+ portAttrs.PortType != HBA_PORTTYPE_NPORT) {
+ free(update_str);
+ return (FPCFGA_OK);
+ }
+
+ if (((inq.inq_dtype & DTYPE_MASK) == DTYPE_UNKNOWN) &&
+ ((flags & CFGA_FLAG_FORCE) == 0)) {
+ /*
+ * We assume all DTYPE_UNKNOWNs are HBAs and we wont
+ * waste time trying to config them. If they are not
+ * HBAs, then there is something wrong since they should
+ * have had a valid dtype.
+ *
+ * However, if the force flag is set (cfgadm -f), we
+ * go ahead and try to configure.
+ *
+ * In this path, however, the force flag is not set.
+ */
+ free(update_str);
+ return (FPCFGA_OK);
+ }
+
+ errno = 0;
+ /*
+ * We'll issue the devctl_bus_dev_create() call even if the
+ * path exists in the devinfo tree. This is to take care of
+ * the situation where the device may be in a state other
+ * than the online and attached state.
+ */
+ if ((ret = do_devctl_dev_create(apidt, dev_path, MAXPATHLEN,
+ inq.inq_dtype, errstring)) != FPCFGA_OK) {
+ /*
+ * Could not configure device. To provide a more
+ * meaningful error message, first see if the supplied port
+ * WWN is there on the fabric. Otherwise print the error
+ * message using the information received from the driver
+ */
+ status = getPortAttrsByWWN(handle, *((HBA_WWN *)(pwwn)),
+ &discPortAttrs);
+ S_FREE(update_str);
+ if (status == HBA_STATUS_ERROR_ILLEGAL_WWN) {
+ return (FPCFGA_APID_NOEXIST);
+ } else {
+ return (FPCFGA_LIB_ERR);
+ }
+ }
+
+ if (((optflag & (FLAG_FORCE_UPDATE_REP|FLAG_NO_UPDATE_REP)) == 0) &&
+ update_fabric_wwn_list(ADD_ENTRY, update_str, errstring)) {
+ cfga_err(errstring, 0, ERR_CONF_OK_UPD_REP, 0);
+ }
+
+ S_FREE(update_str);
+
+ if ((apidt->flags & FLAG_DISABLE_RCM) == 0) {
+ /*
+ * There may be multiple LUNs associated with the
+ * WWN we created nodes for. So, we'll call
+ * apidt_create() again and let it build a list of
+ * all the LUNs for this WWN using the devinfo tree.
+ * We will then online all those devices in RCM
+ */
+ if ((t_apid = calloc(1, strlen(apidt->xport_phys) +
+ strlen(DYN_SEP) +
+ strlen(apidt->dyncomp) + 1)) == NULL) {
+ cfga_err(errstring, errno, ERR_MEM_ALLOC, 0);
+ return (FPCFGA_LIB_ERR);
+ }
+ sprintf(t_apid, "%s%s%s", apidt->xport_phys, DYN_SEP,
+ apidt->dyncomp);
+ if ((ret = apidt_create(t_apid, &my_apidt,
+ errstring)) != FPCFGA_OK) {
+ free(t_apid);
+ return (ret);
+ }
+
+ my_apidt.flags = apidt->flags;
+ if ((ret = dev_rcm_online(&my_apidt, -1, flags,
+ NULL)) != FPCFGA_OK) {
+ cfga_err(errstring, 0, ERRARG_RCM_ONLINE,
+ apidt->lunlist->path, 0);
+ apidt_free(&my_apidt);
+ free(t_apid);
+ return (ret);
+ }
+ S_FREE(t_apid);
+ apidt_free(&my_apidt);
+ }
+ return (FPCFGA_OK);
+
+ case CFGA_CMD_UNCONFIGURE:
+ if (portAttrs.PortType != HBA_PORTTYPE_NLPORT &&
+ portAttrs.PortType != HBA_PORTTYPE_NPORT) {
+ free(update_str);
+ return (FPCFGA_OPNOTSUPP);
+ }
+
+ status = getPortAttrsByWWN(handle, *((HBA_WWN *)(pwwn)),
+ &discPortAttrs);
+ if (apidt->lunlist == NULL) {
+ /*
+ * But first, remove entry from the repository if it is
+ * there ... provided the force update flag is not set
+ * (in which case the update is already done) or if
+ * the no-update flag is not set.
+ */
+ if ((optflag &
+ (FLAG_FORCE_UPDATE_REP|FLAG_NO_UPDATE_REP)) == 0) {
+ if (update_fabric_wwn_list(REMOVE_ENTRY,
+ update_str, errstring)) {
+ free(update_str);
+ cfga_err(errstring, 0,
+ ERR_UNCONF_OK_UPD_REP, 0);
+ return
+ (FPCFGA_UNCONF_OK_UPD_REP_FAILED);
+ }
+ }
+ S_FREE(update_str);
+ if (status == HBA_STATUS_ERROR_ILLEGAL_WWN) {
+ return (FPCFGA_APID_NOEXIST);
+ }
+ return (FPCFGA_OK);
+ }
+ /*
+ * If there are multiple paths to the mpxio
+ * device, we will not check in RCM ONLY when there
+ * is atleast one other ONLINE/STANDBY path
+ */
+ if (is_xport_phys_in_pathlist(apidt, errstring) !=
+ FPCFGA_OK) {
+ free(update_str);
+ return (FPCFGA_XPORT_NOT_IN_PHCI_LIST);
+ }
+
+ /*
+ * dev_rcm_offline() updates errstring
+ */
+ if ((ret = dev_rcm_offline(apidt, flags, errstring)) !=
+ FPCFGA_OK) {
+ free(update_str);
+ return (ret);
+ }
+ if ((ret = dev_unconf(apidt, errstring, &unconf_flag)) !=
+ FPCFGA_OK) {
+ /* when inq failed don't attempt to reconfigure */
+ if (!no_config_attempt) {
+ (void) do_devctl_dev_create(apidt, dev_path, MAXPATHLEN,
+ inq.inq_dtype, NULL);
+ (void) dev_rcm_online(apidt, -1, flags, NULL);
+ }
+ free(update_str);
+ return (ret);
+ }
+ if ((ret = dev_rcm_remove(apidt, flags, errstring)) !=
+ FPCFGA_OK) {
+ (void) do_devctl_dev_create(apidt, dev_path, MAXPATHLEN,
+ inq.inq_dtype, NULL);
+ (void) dev_rcm_online(apidt, -1, flags, NULL);
+ free(update_str);
+ return (ret);
+ }
+ /*
+ * If we offlined a lun in RCM when there are multiple paths but
+ * none of them are ONLINE/STANDBY, we have to online it back
+ * in RCM now. This is a try best, will not fail for it.
+ */
+ dev_rcm_online_nonoperationalpath(apidt, flags, NULL);
+
+ /* Update the repository if we havent already done it */
+ if ((optflag &
+ (FLAG_FORCE_UPDATE_REP|FLAG_NO_UPDATE_REP)) == 0) {
+ if (((optflag & FLAG_REMOVE_UNUSABLE_FCP_DEV) !=
+ FLAG_REMOVE_UNUSABLE_FCP_DEV) ||
+ (((optflag & FLAG_REMOVE_UNUSABLE_FCP_DEV) ==
+ FLAG_REMOVE_UNUSABLE_FCP_DEV) &&
+ (unconf_flag == ALL_APID_LUNS_UNUSABLE))) {
+ if (update_fabric_wwn_list(REMOVE_ENTRY,
+ update_str, errstring)) {
+ free(update_str);
+ cfga_err(errstring, errno,
+ ERR_UNCONF_OK_UPD_REP, 0);
+ return (FPCFGA_UNCONF_OK_UPD_REP_FAILED);
+ }
+ }
+ }
+ free(update_str);
+ return (FPCFGA_OK);
+
+ default:
+ free(update_str);
+ return (FPCFGA_OPNOTSUPP);
+ }
+}
+
+/*
+ * This function copies a port_wwn got by reading the property on a device
+ * node (from_ptr in the function below) on to an array (to_ptr) so that it is
+ * readable.
+ *
+ * Caller responsible to allocate enough memory in "to_ptr"
+ */
+static void
+copy_pwwn_data_to_str(char *to_ptr, const uchar_t *from_ptr)
+{
+ if ((to_ptr == NULL) || (from_ptr == NULL))
+ return;
+
+ (void) sprintf(to_ptr, "%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x",
+ from_ptr[0], from_ptr[1], from_ptr[2], from_ptr[3],
+ from_ptr[4], from_ptr[5], from_ptr[6], from_ptr[7]);
+}
+
+static fpcfga_ret_t
+unconf_vhci_nodes(di_path_t pnode, di_node_t fp_node, char *xport_phys,
+ char *dyncomp, int *num_devs, int *failure_count, char **errstring,
+ cfga_flags_t flags)
+{
+ int iret1, iret2, *lunnump;
+ char *ptr; /* scratch pad */
+ char *node_path, *vhci_path, *update_str;
+ char port_wwn[WWN_SIZE*2+1], pathname[MAXPATHLEN];
+ uchar_t *port_wwn_data = NULL;
+ di_node_t client_node;
+
+ while (pnode != DI_PATH_NIL) {
+
+ (*num_devs)++;
+
+
+ if ((node_path = di_devfs_path(fp_node)) == NULL) {
+ cfga_err(errstring, 0, ERRARG_DEVINFO,
+ xport_phys, 0);
+ (*failure_count)++;
+ pnode = di_path_next_client(fp_node, pnode);
+ continue;
+ }
+
+ iret1 = di_path_prop_lookup_bytes(pnode, PORT_WWN_PROP,
+ &port_wwn_data);
+
+ iret2 = di_path_prop_lookup_ints(pnode, LUN_PROP, &lunnump);
+
+ if ((iret1 == -1) || (iret2 == -1)) {
+ cfga_err(errstring, 0, ERRARG_DI_GET_PROP,
+ node_path, 0);
+ di_devfs_path_free(node_path);
+ node_path = NULL;
+ (*failure_count)++;
+ pnode = di_path_next_client(fp_node, pnode);
+ continue;
+ }
+
+ copy_pwwn_data_to_str(port_wwn, port_wwn_data);
+
+ if ((client_node = di_path_client_node(pnode)) ==
+ DI_NODE_NIL) {
+ (*failure_count)++;
+ di_devfs_path_free(node_path);
+ node_path = NULL;
+ pnode = di_path_next_client(fp_node, pnode);
+ continue;
+ }
+
+ if ((vhci_path = di_devfs_path(client_node)) == NULL) {
+ (*failure_count)++;
+ di_devfs_path_free(node_path);
+ node_path = NULL;
+ pnode = di_path_next_client(fp_node, pnode);
+ continue;
+ }
+
+ if ((ptr = strrchr(vhci_path, '@')) != NULL) {
+ *ptr = '\0';
+ }
+
+ if ((ptr = strrchr(vhci_path, '/')) == NULL) {
+ (*failure_count)++;
+ di_devfs_path_free(node_path);
+ node_path = NULL;
+ pnode = di_path_next_client(fp_node, pnode);
+ continue;
+ }
+
+ sprintf(pathname, "%s%s/%s@w%s,%x", DEVICES_DIR, node_path,
+ ++ptr, port_wwn, *lunnump);
+
+ di_devfs_path_free(node_path);
+ di_devfs_path_free(vhci_path);
+ node_path = vhci_path = NULL;
+
+ /*
+ * Try to offline in RCM first and if that is successful,
+ * unconfigure the LUN. If offlining in RCM fails, then
+ * update the failure_count which gets passed back to caller
+ */
+ if (fp_rcm_offline(pathname, errstring, flags) != 0) {
+ (*failure_count)++;
+ pnode = di_path_next_client(fp_node, pnode);
+ continue;
+ } else if (lun_unconf(pathname, *lunnump, xport_phys,
+ dyncomp, errstring) != FPCFGA_OK) {
+ (void) fp_rcm_online(pathname, NULL, flags);
+ (*failure_count)++;
+ pnode = di_path_next_client(fp_node, pnode);
+ continue;
+ } else if (fp_rcm_remove(pathname, errstring, flags) != 0) {
+ /*
+ * Bring everything back online in rcm and continue
+ */
+ (void) fp_rcm_online(pathname, NULL, flags);
+ (*failure_count)++;
+ pnode = di_path_next_client(fp_node, pnode);
+ continue;
+ }
+
+ /* Update the repository only on a successful unconfigure */
+ if ((update_str = calloc(1, strlen(xport_phys) +
+ strlen(DYN_SEP) +
+ strlen(port_wwn) + 1)) == NULL) {
+ cfga_err(errstring, errno, ERR_UNCONF_OK_UPD_REP, 0);
+ (*failure_count)++;
+ pnode = di_path_next_client(fp_node, pnode);
+ continue;
+ }
+
+ /* Init the string to be removed from repository */
+ sprintf(update_str, "%s%s%s", xport_phys, DYN_SEP, port_wwn);
+
+ if (update_fabric_wwn_list(REMOVE_ENTRY, update_str,
+ errstring)) {
+ S_FREE(update_str);
+ cfga_err(errstring, errno,
+ ERR_UNCONF_OK_UPD_REP, 0);
+ (*failure_count)++;
+ /* Cleanup and continue from here just for clarity */
+ pnode = di_path_next_client(fp_node, pnode);
+ continue;
+ }
+
+ S_FREE(update_str);
+ pnode = di_path_next_client(fp_node, pnode);
+ }
+
+ return (FPCFGA_OK);
+}
+
+static fpcfga_ret_t
+unconf_non_vhci_nodes(di_node_t dnode, char *xport_phys, char *dyncomp,
+ int *num_devs, int *failure_count, char **errstring, cfga_flags_t flags)
+{
+ int ret1, ret2, *lunnump;
+ char pathname[MAXPATHLEN];
+ char *node_path, *update_str;
+ char port_wwn[WWN_SIZE*2+1];
+ uchar_t *port_wwn_data = NULL;
+
+ while (dnode != DI_NODE_NIL) {
+
+ (*num_devs)++;
+
+ /* Get the physical path for this node */
+ if ((node_path = di_devfs_path(dnode)) == NULL) {
+ /*
+ * We don't try to offline in RCM here because we
+ * don't know the path to offline. Just continue to
+ * the next node.
+ */
+ cfga_err(errstring, 0, ERRARG_DEVINFO, xport_phys, 0);
+ (*failure_count)++;
+ dnode = di_sibling_node(dnode);
+ continue;
+ }
+
+ /* Now get the LUN # of this device thru the property */
+ ret1 = di_prop_lookup_ints(DDI_DEV_T_ANY, dnode,
+ LUN_PROP, &lunnump);
+
+ /* Next get the port WWN of the device */
+ ret2 = di_prop_lookup_bytes(DDI_DEV_T_ANY, dnode,
+ PORT_WWN_PROP, &port_wwn_data);
+
+ /* A failure in any of the above is not good */
+ if ((ret1 == -1) || (ret2 == -1)) {
+ /*
+ * We don't try to offline in RCM here because we
+ * don't know the path to offline. Just continue to
+ * the next node.
+ */
+ cfga_err(errstring, 0,
+ ERRARG_DI_GET_PROP, node_path, 0);
+ di_devfs_path_free(node_path);
+ node_path = NULL;
+ (*failure_count)++;
+ dnode = di_sibling_node(dnode);
+ continue;
+ }
+
+ /* Prepend the "/devices" prefix to the path and copy it */
+ sprintf(pathname, "%s%s", DEVICES_DIR, node_path);
+ di_devfs_path_free(node_path);
+ node_path = NULL;
+
+ copy_pwwn_data_to_str(port_wwn, port_wwn_data);
+
+ if (strstr(pathname, "@w") == NULL) {
+ /*
+ * If the driver is detached, some part of the path
+ * may be missing and so we'll manually construct it
+ */
+ sprintf(&pathname[strlen(pathname)], "@w%s,%x",
+ port_wwn, *lunnump);
+ }
+
+ /*
+ * Try to offline in RCM first and if that is successful,
+ * unconfigure the LUN. If offlining in RCM fails, then
+ * update the failure count
+ */
+ if (fp_rcm_offline(pathname, errstring, flags) != 0) {
+ (*failure_count)++;
+ dnode = di_sibling_node(dnode);
+ continue;
+ } else if (lun_unconf(pathname, *lunnump, xport_phys,
+ dyncomp, errstring) != FPCFGA_OK) {
+ (void) fp_rcm_online(pathname, NULL, flags);
+ (*failure_count)++;
+ dnode = di_sibling_node(dnode);
+ continue;
+ } else if (fp_rcm_remove(pathname, errstring, flags) != 0) {
+ /*
+ * Bring everything back online in rcm and continue
+ */
+ (void) fp_rcm_online(pathname, NULL, flags);
+ (*failure_count)++;
+ dnode = di_sibling_node(dnode);
+ continue;
+ }
+
+ /* Update the repository only on a successful unconfigure */
+ if ((update_str = calloc(1, strlen(xport_phys) +
+ strlen(DYN_SEP) +
+ strlen(port_wwn) + 1)) == NULL) {
+ cfga_err(errstring, errno, ERR_UNCONF_OK_UPD_REP, 0);
+ (*failure_count)++;
+ dnode = di_sibling_node(dnode);
+ continue;
+ }
+
+ /* Init the string to be removed from repository */
+ sprintf(update_str, "%s%s%s", xport_phys, DYN_SEP, port_wwn);
+
+ if (update_fabric_wwn_list(REMOVE_ENTRY, update_str,
+ errstring)) {
+ S_FREE(update_str);
+ cfga_err(errstring, errno, ERR_UNCONF_OK_UPD_REP, 0);
+ (*failure_count)++;
+ dnode = di_sibling_node(dnode);
+ continue;
+ }
+
+ S_FREE(update_str);
+ dnode = di_sibling_node(dnode);
+ }
+
+ return (FPCFGA_OK);
+}
+
+/*
+ * INPUT:
+ * apidt - Pointer to apid_t structure with data filled in
+ * flags - Flags for special handling
+ *
+ * OUTPUT:
+ * errstring - Applicable only on a failure from plugin
+ * num_devs - Incremented per lun
+ * failure_count - Incremented on any failed operation on lun
+ *
+ * RETURNS:
+ * non-FPCFGA_OK on any validation check error. If this value is returned, no
+ * devices were handled. Consequently num_devs and failure_count
+ * will not be incremented.
+ * FPCFGA_OK This return value doesn't mean that all devices were successfully
+ * unconfigured, you have to check failure_count.
+ */
+static fpcfga_ret_t
+unconf_any_devinfo_nodes(apid_t *apidt, cfga_flags_t flags, char **errstring,
+ int *num_devs, int *failure_count)
+{
+ char *node_path = NULL;
+ char pathname[MAXPATHLEN], *ptr; /* scratch pad */
+ di_node_t root_node, direct_node, fp_node;
+ di_path_t path_node = DI_PATH_NIL;
+
+ /*
+ * apidt->xport_phys is something like :
+ * /devices/pci@.../SUNW,qlc@../fp@0,0:fc
+ * Make sure we copy both the devinfo and pathinfo nodes
+ */
+ (void) strlcpy(pathname, apidt->xport_phys, MAXPATHLEN);
+
+ /* Now get rid of the ':' at the end */
+ if ((ptr = strstr(pathname, MINOR_SEP)) != NULL)
+ *ptr = '\0';
+
+ if (strncmp(pathname, DEVICES_DIR, strlen(DEVICES_DIR))) {
+ cfga_err(errstring, 0, ERRARG_INVALID_PATH, pathname, 0);
+ return (FPCFGA_INVALID_PATH);
+ }
+
+ if ((root_node = di_init("/", DINFOCPYALL | DINFOPATH)) ==
+ DI_NODE_NIL) {
+ cfga_err(errstring, errno, ERRARG_DEVINFO,
+ apidt->xport_phys, 0);
+ return (FPCFGA_LIB_ERR);
+ }
+
+ if ((fp_node = di_drv_first_node("fp", root_node)) == DI_NODE_NIL) {
+ cfga_err(errstring, errno, ERRARG_DEVINFO,
+ apidt->xport_phys, 0);
+ di_fini(root_node);
+ return (FPCFGA_LIB_ERR);
+ }
+
+ /*
+ * Search all the fp nodes to see if any match the one we are trying
+ * to unconfigure
+ */
+
+ /* Skip the "/devices" prefix */
+ ptr = pathname + strlen(DEVICES_DIR);
+
+ while (fp_node != DI_NODE_NIL) {
+ node_path = di_devfs_path(fp_node);
+ if (strcmp(node_path, ptr) == 0) {
+ /* Found the fp node. 'pathname' has the full path */
+ di_devfs_path_free(node_path);
+ node_path = NULL;
+ break;
+ }
+ fp_node = di_drv_next_node(fp_node);
+ di_devfs_path_free(node_path);
+ }
+
+ if (fp_node == DI_NODE_NIL) {
+ cfga_err(errstring, 0, ERRARG_NOT_IN_DEVINFO,
+ apidt->xport_phys, 0);
+ di_fini(root_node);
+ return (FPCFGA_LIB_ERR);
+ }
+
+ direct_node = di_child_node(fp_node);
+ path_node = di_path_next_client(fp_node, path_node);
+
+ if ((direct_node == DI_NODE_NIL) && (path_node == DI_PATH_NIL)) {
+ /* No devinfo or pathinfo nodes. Great ! Just return success */
+ di_fini(root_node);
+ return (FPCFGA_OK);
+ }
+
+ /* First unconfigure any non-MPXIO nodes */
+ unconf_non_vhci_nodes(direct_node, apidt->xport_phys, apidt->dyncomp,
+ num_devs, failure_count, errstring, flags);
+
+ /*
+ * Now we will traverse any path info nodes that are there
+ *
+ * Only MPXIO devices have pathinfo nodes
+ */
+ unconf_vhci_nodes(path_node, fp_node, apidt->xport_phys, apidt->dyncomp,
+ num_devs, failure_count, errstring, flags);
+
+ di_fini(root_node);
+
+ /*
+ * We don't want to check the return value of unconf_non_vhci_nodes()
+ * and unconf_vhci_nodes(). But instead, we are interested only in
+ * consistently incrementing num_devs and failure_count so that we can
+ * compare them.
+ */
+ return (FPCFGA_OK);
+}
+
+/*
+ * This function handles configuring/unconfiguring all the devices w.r.t
+ * the FCA port specified by apidt.
+ *
+ * In the unconfigure case, it first unconfigures all the devices that are
+ * seen through the given port at that moment and then unconfigures all the
+ * devices that still (somehow) have devinfo nodes on the system for that FCA
+ * port.
+ *
+ * INPUT:
+ * cmd - CFGA_CMD_CONFIGURE or CFGA_CMD_UNCONFIGURE
+ * apidt - Pointer to apid_t structure with data filled in
+ * flags - Flags for special handling
+ *
+ * OUTPUT:
+ * errstring - Applicable only on a failure from plugin
+ *
+ * RETURNS:
+ * FPCFGA_OK on success
+ * non-FPCFGA_OK otherwise
+ */
+static fpcfga_ret_t
+handle_devs(cfga_cmd_t cmd, apid_t *apidt, cfga_flags_t flags,
+ char **errstring, HBA_HANDLE handle, int portIndex,
+ HBA_PORTATTRIBUTES portAttrs)
+{
+ int num_devs = 0, dev_cs_failed = 0;
+ char port_wwn[WWN_S_LEN];
+ la_wwn_t pwwn;
+ apid_t my_apidt = {NULL};
+ char *my_apid;
+ HBA_PORTATTRIBUTES discPortAttrs;
+ int discIndex;
+ fpcfga_ret_t rval = FPCFGA_OK;
+
+ if ((my_apid = calloc(
+ 1, strlen(apidt->xport_phys) + strlen(DYN_SEP) +
+ (2 * FC_WWN_SIZE) + 1)) == NULL) {
+ cfga_err(errstring, errno, ERR_MEM_ALLOC, 0);
+ return (FPCFGA_LIB_ERR);
+ }
+
+ num_devs = portAttrs.NumberofDiscoveredPorts;
+ for (discIndex = 0; discIndex < portAttrs.NumberofDiscoveredPorts;
+ discIndex++) {
+ if (getDiscPortAttrs(handle, portIndex,
+ discIndex, &discPortAttrs)) {
+ dev_cs_failed++;
+ /* Move on to the next target */
+ continue;
+ }
+ (void) sprintf(port_wwn, "%016llx",
+ wwnConversion(discPortAttrs.PortWWN.wwn));
+ /*
+ * Construct a fake apid string similar to the one the
+ * plugin gets from the framework and have apidt_create()
+ * fill in the apid_t structure.
+ */
+ strcpy(my_apid, apidt->xport_phys);
+ strcat(my_apid, DYN_SEP);
+ strcat(my_apid, port_wwn);
+ if (apidt_create(my_apid, &my_apidt, errstring) != FPCFGA_OK) {
+ dev_cs_failed++;
+ continue;
+ }
+ my_apidt.flags = apidt->flags;
+
+ memcpy(&pwwn, &(discPortAttrs.PortWWN), sizeof (la_wwn_t));
+ if (dev_change_state(cmd, &my_apidt, &pwwn,
+ flags, errstring, handle, portAttrs) != FPCFGA_OK) {
+ dev_cs_failed++;
+ }
+ apidt_free(&my_apidt);
+ }
+
+ S_FREE(my_apid);
+
+ /*
+ * We have now handled all the devices that are currently visible
+ * through the given FCA port. But, it is possible that there are
+ * some devinfo nodes hanging around. For the unconfigure operation,
+ * this has to be looked into too.
+ */
+ if (cmd == CFGA_CMD_UNCONFIGURE) {
+ /* dev_cs_failed will be updated to indicate any failures */
+ rval = unconf_any_devinfo_nodes(apidt, flags, errstring,
+ &num_devs, &dev_cs_failed);
+ }
+
+ if (rval == FPCFGA_OK) {
+ if (dev_cs_failed == 0)
+ return (FPCFGA_OK);
+
+ /*
+ * For the discovered ports, num_devs is counted on target
+ * basis, but for invisible targets, num_devs is counted on
+ * lun basis.
+ *
+ * But if dev_cs_failed and num_devs are incremented
+ * consistently, comparation of these two counters is still
+ * meaningful.
+ */
+ if (dev_cs_failed == num_devs) {
+ /* Failed on all devices seen through this FCA port */
+ cfga_err(errstring, 0,
+ ((cmd == CFGA_CMD_CONFIGURE) ?
+ ERR_FCA_CONFIGURE : ERR_FCA_UNCONFIGURE), 0);
+ return (FPCFGA_LIB_ERR);
+ } else {
+ /* Failed only on some of the devices */
+ cfga_err(errstring, 0, ERR_PARTIAL_SUCCESS, 0);
+ return (FPCFGA_LIB_ERR);
+ }
+ } else {
+ if (dev_cs_failed == num_devs) {
+ /* Failed on all devices seen through this FCA port */
+ cfga_err(errstring, 0,
+ ((cmd == CFGA_CMD_CONFIGURE) ?
+ ERR_FCA_CONFIGURE : ERR_FCA_UNCONFIGURE), 0);
+ return (FPCFGA_LIB_ERR);
+ } else {
+ /* Failed only on some of the devices */
+ cfga_err(errstring, 0, ERR_PARTIAL_SUCCESS, 0);
+ return (FPCFGA_LIB_ERR);
+ }
+ }
+
+ /*
+ * Should never get here
+ */
+}
+
+fpcfga_ret_t
+fca_change_state(cfga_cmd_t state_change_cmd, apid_t *apidt,
+ cfga_flags_t flags, char **errstring)
+{
+ fpcfga_ret_t ret;
+ HBA_HANDLE handle;
+ HBA_PORTATTRIBUTES portAttrs;
+ int portIndex;
+
+ if ((ret = findMatchingAdapterPort(apidt->xport_phys, &handle,
+ &portIndex, &portAttrs, errstring)) != FPCFGA_OK) {
+ return (ret);
+ }
+
+ /*
+ * Bail out if not fabric/public loop
+ */
+ switch (state_change_cmd) {
+ case CFGA_CMD_CONFIGURE:
+ if (portAttrs.PortType != HBA_PORTTYPE_NLPORT &&
+ portAttrs.PortType != HBA_PORTTYPE_NPORT) {
+ HBA_CloseAdapter(handle);
+ HBA_FreeLibrary();
+ return (FPCFGA_OK);
+ }
+ break;
+
+ case CFGA_CMD_UNCONFIGURE:
+ if (portAttrs.PortType != HBA_PORTTYPE_NLPORT &&
+ portAttrs.PortType != HBA_PORTTYPE_NPORT) {
+ HBA_CloseAdapter(handle);
+ HBA_FreeLibrary();
+ return (FPCFGA_OPNOTSUPP);
+ }
+ break;
+ default:
+ HBA_CloseAdapter(handle);
+ HBA_FreeLibrary();
+ return (FPCFGA_LIB_ERR);
+ }
+ ret = (handle_devs(state_change_cmd, apidt, flags, errstring,
+ handle, portIndex, portAttrs));
+ HBA_CloseAdapter(handle);
+ HBA_FreeLibrary();
+ return (ret);
+}
diff --git a/usr/src/lib/cfgadm_plugins/fp/common/cfga_cvt.c b/usr/src/lib/cfgadm_plugins/fp/common/cfga_cvt.c
new file mode 100644
index 0000000000..deff5b826e
--- /dev/null
+++ b/usr/src/lib/cfgadm_plugins/fp/common/cfga_cvt.c
@@ -0,0 +1,788 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#include "cfga_fp.h"
+
+/* Function prototypes */
+
+static fpcfga_ret_t get_xport_devlink(const char *hba_phys,
+ char **hba_logpp, int *l_errnop);
+static char ctoi(char c);
+static fpcfga_ret_t is_apid_configured(const char *xport_phys,
+ const char *dyncomp, struct luninfo_list **lunlistpp, int *l_errnop);
+static fpcfga_ret_t insert_lun_to_lunlist(struct luninfo_list **lunlistpp,
+ const char *dyncomp, di_node_t devnode, int *l_errnop);
+static fpcfga_ret_t update_lunlist(struct luninfo_list **lunlistpp, int lun,
+ uint_t state, char *pathp, int *l_errnop);
+
+
+/* Globals */
+
+/* Various conversions routines */
+
+void
+cvt_lawwn_to_dyncomp(const la_wwn_t *pwwn, char **dyncomp, int *l_errnop)
+{
+ *dyncomp = calloc(1, WWN_SIZE*2 + 1);
+ if (*dyncomp == NULL) {
+ *l_errnop = errno;
+ }
+
+ (void) sprintf(*dyncomp, "%016llx",
+ (wwnConversion((uchar_t *)pwwn->raw_wwn)));
+}
+
+
+int
+cvt_dyncomp_to_lawwn(const char *dyncomp, la_wwn_t *port_wwn)
+{
+ int i;
+ char c, c1;
+ uchar_t *wwnp;
+
+ wwnp = port_wwn->raw_wwn;
+ for (i = 0; i < WWN_SIZE; i++, wwnp++) {
+
+ c = ctoi(*dyncomp++);
+ c1 = ctoi(*dyncomp++);
+ if (c == -1 || c1 == -1)
+ return (-1);
+ *wwnp = ((c << 4) + c1);
+ }
+
+ return (0);
+}
+
+
+static char
+ctoi(char c)
+{
+ if ((c >= '0') && (c <= '9'))
+ c -= '0';
+ else if ((c >= 'A') && (c <= 'F'))
+ c = c - 'A' + 10;
+ else if ((c >= 'a') && (c <= 'f'))
+ c = c - 'a' + 10;
+ else
+ c = -1;
+ return (c);
+}
+
+
+/*
+ * Generates the HBA logical ap_id from physical ap_id.
+ */
+fpcfga_ret_t
+make_xport_logid(const char *xport_phys, char **xport_logpp, int *l_errnop)
+{
+ if (*xport_logpp != NULL) {
+ return (FPCFGA_ERR);
+ }
+
+ /*
+ * A devlink for the XPORT should exist. Without the /dev/cfg link
+ * driver name and instance number based based link needs to be
+ * constructed for the minor node type of DDI_NT_FC_ATTACHMENT_POINT.
+ * sunddi.h defines DDI_NT_FC_ATTACHMENT_POINT for
+ * ddi_ctl:attachment_point:fc
+ */
+ if (get_xport_devlink(xport_phys, xport_logpp, l_errnop) == FPCFGA_OK) {
+ assert(*xport_logpp != NULL);
+ return (FPCFGA_OK);
+ } else {
+ return (FPCFGA_ERR);
+ }
+}
+
+static fpcfga_ret_t
+get_xport_devlink(const char *xport_phys, char **xport_logpp, int *l_errnop)
+{
+ int match_minor;
+ size_t len;
+ fpcfga_ret_t ret;
+
+ match_minor = 1;
+ ret = physpath_to_devlink(CFGA_DEV_DIR, (char *)xport_phys,
+ xport_logpp, l_errnop, match_minor);
+ if (ret != FPCFGA_OK) {
+ return (ret);
+ }
+
+ assert(*xport_logpp != NULL);
+
+ /* Remove the "/dev/cfg/" prefix */
+ len = strlen(CFGA_DEV_DIR SLASH);
+
+ (void) memmove(*xport_logpp, *xport_logpp + len,
+ strlen(*xport_logpp + len) + 1);
+
+ return (FPCFGA_OK);
+}
+
+
+/*
+ * Given a xport path and dynamic ap_id, returns the physical
+ * path in pathpp. If the dynamic ap is not configured pathpp set to NULL
+ * and returns FPCFGA_APID_NOCONFIGURE.
+ */
+fpcfga_ret_t
+dyn_apid_to_path(
+ const char *xport_phys,
+ const char *dyncomp,
+ struct luninfo_list **lunlistpp,
+ int *l_errnop)
+{
+ fpcfga_ret_t ret;
+
+ /* A device MUST have a dynamic component */
+ if (dyncomp == NULL) {
+ return (FPCFGA_LIB_ERR);
+ }
+
+ ret = is_apid_configured(xport_phys, dyncomp, lunlistpp, l_errnop);
+
+ assert(ret != FPCFGA_OK);
+
+ return (ret);
+}
+
+/*
+ * When both the transport and dynamic comp are given this function
+ * checks to see if the dynamic ap is configured on the dev tree.
+ * If it is configured the devfs path will be stored in pathpp.
+ * When the dynamic comp is null this function check to see if the transport
+ * node has any child.
+ *
+ * Retrun value: FPCFGA_OK if the apid is configured.
+ * FPCFGA_APID_NOCONFIGURE if the apid is not configured.
+ * FPCFGA_LIB_ERR for other errors.
+ */
+static fpcfga_ret_t
+is_apid_configured(
+ const char *xport_phys,
+ const char *dyncomp,
+ struct luninfo_list **lunlistpp,
+ int *l_errnop)
+{
+ char *devfs_phys, *devfs_fp_path, *client_path, *cp,
+ *pathp = NULL;
+ char path_name[MAXPATHLEN];
+ di_node_t tree_root, root, fpnode, dev_node, client_node;
+ di_path_t path = DI_PATH_NIL;
+ di_prop_t prop = DI_PROP_NIL;
+ uchar_t *port_wwn_data = NULL;
+ char *lun_guid = NULL;
+ char port_wwn[WWN_SIZE*2+1];
+ int count, *lunnump, devlen,
+ found_fp = 0;
+ uint_t state;
+ uint_t statep;
+ fpcfga_ret_t ret;
+
+ if (*lunlistpp != NULL) {
+ return (FPCFGA_LIB_ERR);
+ }
+
+ ret = FPCFGA_APID_NOCONFIGURE;
+
+ if ((devfs_phys = strdup(xport_phys)) == NULL) {
+ *l_errnop = errno;
+ return (FPCFGA_LIB_ERR);
+ }
+
+ if (strncmp(devfs_phys, DEVICES_DIR SLASH, strlen(DEVICES_DIR) +
+ strlen(SLASH)) == 0) {
+ cp = devfs_phys + strlen(DEVICES_DIR);
+ (void) memmove(devfs_phys, cp, strlen(cp) + 1);
+ }
+
+ if ((cp = strstr(devfs_phys, MINOR_SEP)) != NULL) {
+ *cp = '\0'; /* Terminate string. */
+ }
+
+ if ((tree_root = di_init("/", DINFOCPYALL | DINFOPATH))
+ == DI_NODE_NIL) {
+ *l_errnop = errno;
+ S_FREE(devfs_phys);
+ return (FPCFGA_LIB_ERR);
+ }
+
+ fpnode = di_drv_first_node("fp", tree_root);
+
+ while (fpnode) {
+ devfs_fp_path = di_devfs_path(fpnode);
+ if ((devfs_fp_path) && !(strncmp(devfs_fp_path,
+ devfs_phys, strlen(devfs_phys)))) {
+ found_fp = 1;
+ di_devfs_path_free(devfs_fp_path);
+ break;
+ }
+ di_devfs_path_free(devfs_fp_path);
+ fpnode = di_drv_next_node(fpnode);
+ }
+ if (!(found_fp)) {
+ ret = FPCFGA_LIB_ERR;
+ goto out;
+ } else {
+ root = fpnode;
+ }
+
+ /*
+ * when there is no child and path info node the
+ * FPCFGA_APID_NOCONFIGURE is returned
+ * regardless of the dynamic comp.
+ */
+ dev_node = di_child_node(root);
+ path = di_path_next_client(root, path);
+ if ((dev_node == DI_NODE_NIL) && (path == DI_PATH_NIL)) {
+ *l_errnop = errno;
+ ret = FPCFGA_APID_NOCONFIGURE;
+ goto out;
+ }
+
+ /*
+ * when dyn comp is null the function just checks if there is any
+ * child under fp transport attachment point.
+ */
+ if (dyncomp == NULL) {
+ ret = FPCFGA_OK;
+ goto out;
+ }
+
+ /*
+ * now checks the children node to find
+ * if dynamic ap is configured. if there are multiple luns
+ * store into lunlist.
+ */
+ if (dev_node != DI_NODE_NIL) {
+ do {
+ while ((prop = di_prop_next(dev_node, prop)) !=
+ DI_PROP_NIL) {
+ /* is property name port-wwn */
+ if ((!(strcmp(PORT_WWN_PROP,
+ di_prop_name(prop)))) &&
+ (di_prop_type(prop) ==
+ DI_PROP_TYPE_BYTE)) {
+ break;
+ }
+ }
+
+ if (prop != DI_PROP_NIL) {
+ count = di_prop_bytes(prop, &port_wwn_data);
+ if (count != WWN_SIZE) {
+ ret = FPCFGA_LIB_ERR;
+ goto out;
+ } else {
+ (void) sprintf(port_wwn,
+ "%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x",
+ port_wwn_data[0], port_wwn_data[1],
+ port_wwn_data[2], port_wwn_data[3],
+ port_wwn_data[4], port_wwn_data[5],
+ port_wwn_data[6], port_wwn_data[7]);
+ if (!(strncmp(port_wwn, dyncomp,
+ WWN_SIZE*2))) {
+ ret = insert_lun_to_lunlist(
+ lunlistpp, dyncomp,
+ dev_node, l_errnop);
+ if (ret != FPCFGA_OK) {
+ goto out;
+ }
+ }
+ }
+ }
+ dev_node = di_sibling_node(dev_node);
+ prop = DI_PROP_NIL;
+ } while (dev_node != DI_NODE_NIL);
+ }
+
+ /*
+ * now checks the path info node to find
+ * if dynamic ap is configured. if there are multiple luns
+ * store into lunlist.
+ */
+ if (path != DI_PATH_NIL) {
+ /*
+ * now parse the path info node.
+ */
+ do {
+ count = di_path_prop_lookup_bytes(path, PORT_WWN_PROP,
+ &port_wwn_data);
+ if (count != WWN_SIZE) {
+ ret = FPCFGA_LIB_ERR;
+ goto out;
+ }
+
+ (void) sprintf(port_wwn,
+ "%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x",
+ port_wwn_data[0], port_wwn_data[1],
+ port_wwn_data[2], port_wwn_data[3],
+ port_wwn_data[4], port_wwn_data[5],
+ port_wwn_data[6], port_wwn_data[7]);
+
+ /* if matches get the path of scsi_vhci child node. */
+ if (!(strncmp(port_wwn, dyncomp, WWN_SIZE*2))) {
+ client_node = di_path_client_node(path);
+ if (client_node == DI_NODE_NIL) {
+ ret = FPCFGA_LIB_ERR;
+ *l_errnop = errno;
+ goto out;
+ }
+ count = di_path_prop_lookup_ints(path,
+ LUN_PROP, &lunnump);
+ client_path = di_devfs_path(client_node);
+ strcpy(path_name, client_path);
+ di_devfs_path_free(client_path);
+ state = di_state(client_node);
+ statep = di_path_state(path);
+
+ /*
+ * If the node is
+ * state then check the devfs_path to
+ * see if it has a complete path.
+ * For non scsi_vhci node the path
+ * doesn't contain @w(portwwn) part
+ * consistently. For scsi_vhci
+ * this behavior may not be there.
+ * To be safe @g(guid) is attempted
+ * to be added here.
+ */
+ if ((state & DI_DRIVER_DETACHED) &&
+ (strstr(path_name, "@g") == NULL)) {
+ prop = DI_PROP_NIL;
+ while ((prop = di_prop_next(client_node,
+ prop)) != DI_PROP_NIL) {
+ /* is property name lun-wwn */
+ if ((!(strcmp(LUN_GUID_PROP,
+ di_prop_name(prop)))) &&
+ (di_prop_type(prop) ==
+ DI_PROP_TYPE_STRING)) {
+ break;
+ }
+ }
+
+ if (prop != DI_PROP_NIL) {
+ count = di_prop_strings(
+ prop, &lun_guid);
+ sprintf(&path_name[
+ strlen(path_name)],
+ "@g%s", lun_guid);
+ } else {
+ ret = FPCFGA_LIB_ERR;
+ goto out;
+ }
+ }
+
+ devlen = strlen(DEVICES_DIR) +
+ strlen(path_name) + 1;
+ if ((pathp = calloc(1, devlen))
+ == NULL) {
+ *l_errnop = errno;
+ return (FPCFGA_LIB_ERR);
+ } else {
+ (void) snprintf(pathp, devlen,
+ "%s%s", DEVICES_DIR, path_name);
+ }
+ if ((ret = (update_lunlist(lunlistpp, *lunnump,
+ statep, pathp, l_errnop))) !=
+ FPCFGA_OK) {
+ S_FREE(pathp);
+ goto out;
+ }
+ }
+ path = di_path_next_client(root, path);
+ } while (path != DI_PATH_NIL);
+ }
+
+out:
+ di_fini(tree_root);
+ S_FREE(devfs_phys);
+ return (ret);
+}
+
+static fpcfga_ret_t
+insert_lun_to_lunlist(
+ struct luninfo_list **lunlistpp,
+ const char *dyncomp,
+ di_node_t dev_node,
+ int *l_errnop)
+{
+ char path_name[MAXPATHLEN];
+ char *pathp, *dev_phys;
+ di_prop_t prop_lun = DI_PROP_NIL;
+ uint_t state;
+ int count, devlen;
+ int *lunp;
+
+ while ((prop_lun = di_prop_next(dev_node, prop_lun)) != DI_PROP_NIL) {
+ if (!(strcmp(LUN_PROP, di_prop_name(prop_lun))) &&
+ (di_prop_type(prop_lun) == DI_PROP_TYPE_INT)) {
+ count = di_prop_ints(prop_lun, &lunp);
+ if (count <= 0) {
+ return (FPCFGA_LIB_ERR);
+ }
+ break;
+ }
+ }
+
+ if (prop_lun == DI_PROP_NIL) {
+ return (FPCFGA_LIB_ERR);
+ }
+
+ /*
+ * stores state info in state.
+ * This information is used to get the
+ * validity of path.
+ * if driver_detached don't try to get
+ * the devfs_path since it is not
+ * complete. ex, /pci@1f,2000/pci@1/
+ * SUNW,qlc@5/fp@0,0/ssd
+ * which doesn't contain the port wwn
+ * part. The attached node looks like
+ * /pci@1f,2000/pci@1/SUNW,qlc@5/fp@0,0/
+ * ssd@w2100002037006b14,0
+ */
+ state = di_state(dev_node);
+
+ dev_phys = di_devfs_path(dev_node);
+ if (dev_phys == NULL) {
+ return (FPCFGA_LIB_ERR);
+ }
+
+ strcpy(path_name, dev_phys);
+
+ di_devfs_path_free(dev_phys);
+
+ if ((state & DI_DRIVER_DETACHED) &&
+ (strstr(path_name, "@w") == NULL)) {
+ sprintf(&path_name[strlen(path_name)], "@w%s,%x", dyncomp,
+ *lunp);
+ }
+
+ devlen = strlen(DEVICES_DIR) + strlen(path_name) + 1;
+
+ if ((pathp = calloc(1, devlen))
+ == NULL) {
+ *l_errnop = errno;
+ return (FPCFGA_LIB_ERR);
+ } else {
+ (void) snprintf(pathp, devlen, "%s%s", DEVICES_DIR, path_name);
+ }
+
+ return (update_lunlist(lunlistpp, *lunp, state, pathp, l_errnop));
+}
+
+static fpcfga_ret_t
+update_lunlist(
+ struct luninfo_list **lunlistpp,
+ int lun,
+ uint_t state,
+ char *pathp,
+ int *l_errnop)
+{
+ struct luninfo_list *newlun, *curlun, *prevlun;
+
+ newlun = curlun = prevlun = (struct luninfo_list *)NULL;
+
+ newlun = calloc(1, sizeof (struct luninfo_list));
+ if (newlun == NULL) {
+ *l_errnop = errno;
+ return (FPCFGA_LIB_ERR);
+ }
+
+ newlun->lunnum = lun;
+ newlun->node_state = state;
+ newlun->path = pathp;
+ newlun->next = (struct luninfo_list *)NULL;
+
+ /* if lunlist is empty add the new lun info and return. */
+ if (*lunlistpp == NULL) {
+ *lunlistpp = newlun;
+ return (FPCFGA_OK);
+ }
+
+ /* if the first lun in the list is the same as the new lun return. */
+ if ((*lunlistpp)->lunnum == lun) {
+ S_FREE(newlun);
+ return (FPCFGA_OK);
+ }
+
+ /*
+ * if the first lun in the list is less than the new lun add the
+ * new lun as the first lun and return.
+ */
+ if ((*lunlistpp)->lunnum < lun) {
+ newlun->next = *lunlistpp;
+ *lunlistpp = newlun;
+ return (FPCFGA_OK);
+ }
+
+ /*
+ * if the first lun in the list is greater than the new lun and
+ * there is a single lun add new lun after the first lun and return.
+ */
+ if ((*lunlistpp)->next == NULL) {
+ (*lunlistpp)->next = newlun;
+ return (FPCFGA_OK);
+ }
+
+ /*
+ * now there is more than two luns in the list and the first lun
+ * is greter than the input lun.
+ */
+ curlun = (*lunlistpp)->next;
+ prevlun = *lunlistpp;
+
+ while (curlun != NULL) {
+ if (curlun->lunnum == lun) {
+ S_FREE(newlun);
+ return (FPCFGA_OK);
+ } else if (curlun->lunnum < lun) {
+ newlun->next = curlun;
+ prevlun->next = newlun;
+ return (FPCFGA_OK);
+ } else {
+ prevlun = curlun;
+ curlun = curlun->next;
+ }
+ }
+
+ /* add the new lun at the end of list. */
+ prevlun->next = newlun;
+ return (FPCFGA_OK);
+
+}
+
+
+fpcfga_ret_t
+make_dyncomp_from_dinode(
+ const di_node_t node,
+ char **dyncompp,
+ int *l_errnop)
+{
+ di_prop_t prop = DI_PROP_NIL;
+ uchar_t *port_wwn_data;
+ int count;
+
+ *l_errnop = 0;
+ *dyncompp = calloc(1, WWN_SIZE*2 + 1);
+ if (*dyncompp == NULL) {
+ *l_errnop = errno;
+ return (FPCFGA_LIB_ERR);
+ }
+
+ /* now get port-wwn for the input node. */
+ while ((prop = di_prop_next(node, prop)) != DI_PROP_NIL) {
+ if (!(strcmp(PORT_WWN_PROP, di_prop_name(prop))) &&
+ (di_prop_type(prop) == DI_PROP_TYPE_BYTE)) {
+ break;
+ }
+ }
+
+ if (prop != DI_PROP_NIL) {
+ count = di_prop_bytes(prop, &port_wwn_data);
+ if (count != WWN_SIZE) {
+ S_FREE(*dyncompp);
+ return (FPCFGA_LIB_ERR);
+ }
+
+ (void) sprintf(*dyncompp, "%016llx",
+ (wwnConversion(port_wwn_data)));
+ } else {
+ *l_errnop = errno;
+ S_FREE(*dyncompp);
+ return (FPCFGA_LIB_ERR);
+ }
+
+ return (FPCFGA_OK);
+}
+
+fpcfga_ret_t
+make_portwwn_luncomp_from_dinode(
+ const di_node_t node,
+ char **dyncompp,
+ int **luncompp,
+ int *l_errnop)
+{
+ uchar_t *port_wwn_data;
+ int pwwn_ret, lun_ret;
+
+ *l_errnop = 0;
+
+ if ((dyncompp != NULL) &&
+ ((pwwn_ret = di_prop_lookup_bytes(DDI_DEV_T_ANY,
+ node, PORT_WWN_PROP, &port_wwn_data)) <= 0)) {
+ *l_errnop = errno;
+ }
+ if ((luncompp != NULL) &&
+ ((lun_ret = di_prop_lookup_ints(DDI_DEV_T_ANY,
+ node, LUN_PROP, luncompp)) <= 0)) {
+ *l_errnop = errno;
+ }
+
+ /*
+ * di_prop* returns the number of entries found or 0 if not found
+ * or -1 for othere failure.
+ */
+ if ((pwwn_ret <= 0) || (lun_ret <= 0)) {
+ return (FPCFGA_LIB_ERR);
+ }
+
+ *dyncompp = calloc(1, WWN_SIZE*2+1);
+ if (*dyncompp == NULL) {
+ *l_errnop = errno;
+ return (FPCFGA_LIB_ERR);
+ }
+
+ (void) sprintf(*dyncompp, "%016llx", (wwnConversion(port_wwn_data)));
+
+ return (FPCFGA_OK);
+}
+
+fpcfga_ret_t
+make_portwwn_luncomp_from_pinode(
+ const di_path_t pinode,
+ char **dyncompp,
+ int **luncompp,
+ int *l_errnop)
+{
+ uchar_t *port_wwn_data;
+ int pwwn_ret, lun_ret;
+
+ *l_errnop = 0;
+
+ if ((dyncompp != NULL) &&
+ ((pwwn_ret = di_path_prop_lookup_bytes(pinode,
+ PORT_WWN_PROP, &port_wwn_data)) <= 0)) {
+ *l_errnop = errno;
+ }
+ if ((luncompp != NULL) &&
+ ((lun_ret = di_path_prop_lookup_ints(pinode,
+ LUN_PROP, luncompp)) <= 0)) {
+ *l_errnop = errno;
+ }
+
+ /*
+ * di_prop* returns the number of entries found or 0 if not found
+ * or -1 for othere failure.
+ */
+ if ((pwwn_ret <= 0) || (lun_ret <= 0)) {
+ return (FPCFGA_LIB_ERR);
+ }
+
+ *dyncompp = calloc(1, WWN_SIZE*2+1);
+ if (*dyncompp == NULL) {
+ *l_errnop = errno;
+ return (FPCFGA_LIB_ERR);
+ }
+
+ (void) sprintf(*dyncompp, "%016llx", (wwnConversion(port_wwn_data)));
+
+ return (FPCFGA_OK);
+}
+
+fpcfga_ret_t
+construct_nodepath_from_dinode(
+ const di_node_t node,
+ char **node_pathp,
+ int *l_errnop)
+{
+ char *devfs_path, path_name[MAXPATHLEN], *lun_guid, *port_wwn;
+ uchar_t *port_wwn_data;
+ int is_scsi_vhci_dev, di_ret, devlen;
+ uint_t state;
+
+ devfs_path = di_devfs_path(node);
+ strcpy(path_name, devfs_path);
+ di_devfs_path_free(devfs_path);
+ state = di_state(node);
+
+ is_scsi_vhci_dev = (strstr(path_name, SCSI_VHCI_DRVR) != NULL) ? 1 : 0;
+
+ /*
+ * If the node is
+ * state then check the devfs_path to
+ * see if it has a complete path.
+ * For non scsi_vhci node the path
+ * doesn't contain @w(portwwn) part
+ * consistently. For scsi_vhci
+ * this behavior may not be there.
+ * To be safe @g(guid) is attempted
+ * to be added here.
+ */
+ if (state & DI_DRIVER_DETACHED) {
+ if (is_scsi_vhci_dev &&
+ (strstr(path_name, "@g") == NULL)) {
+ di_ret = di_prop_lookup_strings(DDI_DEV_T_ANY, node,
+ LUN_GUID_PROP, &lun_guid);
+ if (di_ret == -1) {
+ *l_errnop = errno;
+ return (FPCFGA_LIB_ERR);
+ } else {
+ sprintf(&path_name[strlen(path_name)],
+ "@g%s", lun_guid);
+ }
+ } else if (!is_scsi_vhci_dev &&
+ (strstr(path_name, "@w") == NULL)) {
+ di_ret = di_prop_lookup_bytes(DDI_DEV_T_ANY, node,
+ PORT_WWN_PROP, &port_wwn_data);
+ if (di_ret == -1) {
+ *l_errnop = errno;
+ return (FPCFGA_LIB_ERR);
+ } else {
+ if ((port_wwn = calloc(1, WWN_SIZE*2 + 1))
+ == NULL) {
+ *l_errnop = errno;
+ return (FPCFGA_LIB_ERR);
+ }
+
+ (void) sprintf(port_wwn, "%016llx",
+ (wwnConversion(port_wwn_data)));
+ (void) sprintf(&path_name[strlen(path_name)],
+ "@w%s", port_wwn);
+ S_FREE(port_wwn);
+ }
+ }
+ }
+
+ devlen = strlen(DEVICES_DIR) + strlen(path_name) + 1;
+ if ((*node_pathp = calloc(1, devlen)) == NULL) {
+ *l_errnop = errno;
+ return (FPCFGA_LIB_ERR);
+ } else {
+ (void) snprintf(*node_pathp, devlen,
+ "%s%s", DEVICES_DIR, path_name);
+ }
+
+ return (FPCFGA_OK);
+}
+
+u_longlong_t
+wwnConversion(uchar_t *wwn)
+{
+ u_longlong_t tmp;
+ memcpy(&tmp, wwn, sizeof (u_longlong_t));
+ return (ntohll(tmp));
+}
diff --git a/usr/src/lib/cfgadm_plugins/fp/common/cfga_fp.c b/usr/src/lib/cfgadm_plugins/fp/common/cfga_fp.c
new file mode 100644
index 0000000000..d24104c900
--- /dev/null
+++ b/usr/src/lib/cfgadm_plugins/fp/common/cfga_fp.c
@@ -0,0 +1,404 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+
+
+#include "cfga_fp.h"
+
+/*
+ * This file contains the entry points to the plug-in as defined in the
+ * config_admin(3X) man page.
+ */
+
+/*
+ * Set the version number
+ */
+int cfga_version = CFGA_HSL_V2;
+
+/*ARGSUSED*/
+cfga_err_t
+cfga_change_state(
+ cfga_cmd_t state_change_cmd,
+ const char *ap_id,
+ const char *options,
+ struct cfga_confirm *confp,
+ struct cfga_msg *msgp,
+ char **errstring,
+ cfga_flags_t flags)
+{
+ apid_t apidt = {NULL};
+ fpcfga_ret_t ret;
+ la_wwn_t pwwn;
+ char *value, *hw_option, *hw_option_p;
+ char *fp_cs_hw_opts[] = {"disable_rcm", "force_update",
+ "no_update", "unusable_SCSI_LUN", "unusable_FCP_dev", NULL};
+ HBA_HANDLE handle;
+ HBA_PORTATTRIBUTES portAttrs;
+ int portIndex;
+
+ if (errstring != NULL) {
+ *errstring = NULL;
+ }
+
+ /* Check for super user priveleges */
+ if (geteuid() != 0) {
+ return (CFGA_PRIV);
+ }
+
+ /* Only configure and unconfigure operations are supported */
+ if (state_change_cmd != CFGA_CMD_CONFIGURE &&
+ state_change_cmd != CFGA_CMD_UNCONFIGURE) {
+ return (CFGA_OPNOTSUPP);
+ }
+
+ if ((ret = apidt_create(ap_id, &apidt, errstring)) != CFGA_OK) {
+ return (err_cvt(ret));
+ }
+
+ if (options != NULL) {
+ hw_option = calloc(1, strlen(options) + 1);
+ (void) snprintf(hw_option, strlen(options) + 1, "%s", options);
+ hw_option_p = hw_option;
+ /* Use getsubopt() if more options get added */
+ while (*hw_option_p != '\0') {
+ switch (getsubopt(&hw_option_p, fp_cs_hw_opts, &value)) {
+ case OPT_DISABLE_RCM :
+ apidt.flags |= FLAG_DISABLE_RCM;
+ break;
+ case OPT_FORCE_UPDATE_REP :
+ apidt.flags |= FLAG_FORCE_UPDATE_REP;
+ break;
+ case OPT_NO_UPDATE_REP :
+ apidt.flags |= FLAG_NO_UPDATE_REP;
+ break;
+ case OPT_REMOVE_UNUSABLE_FCP_DEV :
+ case OPT_REMOVE_UNUSABLE_SCSI_LUN:
+ if (state_change_cmd != CFGA_CMD_UNCONFIGURE) {
+ cfga_err(errstring, 0, ERRARG_OPT_INVAL,
+ options, 0);
+ S_FREE(hw_option);
+ apidt_free(&apidt);
+ return (CFGA_ERROR);
+ }
+ apidt.flags |= FLAG_REMOVE_UNUSABLE_FCP_DEV;
+ break;
+ default :
+ /* process unknonw option. */
+ cfga_err(errstring, 0, ERRARG_OPT_INVAL,
+ options, 0);
+ S_FREE(hw_option);
+ apidt_free(&apidt);
+ return (CFGA_ERROR);
+ }
+ }
+ S_FREE(hw_option);
+ }
+
+ if (options != NULL && apidt.flags == 0) {
+ /* invalid option specified. */
+ cfga_err(errstring, 0, ERRARG_OPT_INVAL, options, 0);
+ apidt_free(&apidt);
+ return (CFGA_ERROR);
+ }
+
+ if (apidt.dyncomp != NULL) { /* Was there a port WWN passed ? */
+ /*
+ * Yes - so change state of the particular device
+ *
+ * First Get the WWN in la_wwn_t form
+ */
+ if (cvt_dyncomp_to_lawwn(apidt.dyncomp, &pwwn)) {
+ cfga_err(errstring, 0, ERR_APID_INVAL, 0);
+ return (err_cvt(FPCFGA_LIB_ERR));
+ }
+
+ if ((ret = findMatchingAdapterPort(apidt.xport_phys,
+ &handle, &portIndex, &portAttrs, errstring)) ==
+ FPCFGA_OK) {
+ ret = dev_change_state(state_change_cmd, &apidt, &pwwn,
+ flags, errstring, handle, portAttrs);
+ HBA_CloseAdapter(handle);
+ HBA_FreeLibrary();
+ }
+ } else {
+ /* Change state of all devices on FCA and the FCA itself */
+ ret = fca_change_state(state_change_cmd, &apidt,
+ flags, errstring);
+ }
+
+ apidt_free(&apidt);
+ return (err_cvt(ret));
+}
+
+
+/*ARGSUSED*/
+cfga_err_t
+cfga_private_func(
+ const char *func,
+ const char *ap_id,
+ const char *options,
+ struct cfga_confirm *confp,
+ struct cfga_msg *msgp,
+ char **errstring,
+ cfga_flags_t flags)
+{
+ if (errstring != NULL) {
+ *errstring = NULL;
+ }
+
+ if (geteuid() != 0) {
+ return (CFGA_PRIV);
+ }
+
+ return (CFGA_OPNOTSUPP);
+}
+
+
+/*ARGSUSED*/
+cfga_err_t
+cfga_test(
+ const char *ap_id,
+ const char *options,
+ struct cfga_msg *msgp,
+ char **errstring,
+ cfga_flags_t flags)
+{
+ if (errstring != NULL) {
+ *errstring = NULL;
+ }
+
+ if (geteuid() != 0) {
+ return (CFGA_PRIV);
+ }
+
+ return (CFGA_OPNOTSUPP);
+}
+
+
+/*ARGSUSED*/
+cfga_err_t
+cfga_list_ext(
+ const char *ap_id,
+ cfga_list_data_t **ap_id_list,
+ int *nlistp,
+ const char *options,
+ const char *listopts,
+ char **errstring,
+ cfga_flags_t flags)
+{
+ int fca, expand, nelem;
+ ldata_list_t *ldatalistp = NULL;
+ apid_t apidt = {NULL};
+ fpcfga_cmd_t cmd;
+ fpcfga_ret_t ret;
+ char *value, *hw_option, *hw_option_p;
+ uint_t fp_flags = 0;
+ char *fp_list_hw_opts[] = {"devinfo_force", "show_SCSI_LUN",
+ "show_FCP_dev", NULL};
+
+ if (errstring != NULL) {
+ *errstring = NULL;
+ }
+
+ /* Check for super user privileges */
+ if (geteuid() != 0) {
+ return (CFGA_PRIV);
+ }
+
+ if (ap_id_list == NULL || nlistp == NULL) {
+ return (CFGA_ERROR);
+ }
+
+ *ap_id_list = NULL;
+ *nlistp = 0;
+
+ if (options != NULL) {
+ hw_option = calloc(1, strlen(options) + 1);
+ (void) snprintf(hw_option, strlen(options) + 1, "%s", options);
+ hw_option_p = hw_option;
+ /* Use getsubopt() if more options get added */
+ while (*hw_option_p != '\0') {
+ switch (getsubopt(&hw_option_p, fp_list_hw_opts, &value)) {
+ case OPT_DEVINFO_FORCE :
+ fp_flags |= FLAG_DEVINFO_FORCE;
+ break;
+ case OPT_FCP_DEV :
+ case OPT_SHOW_SCSI_LUN:
+ fp_flags |= FLAG_FCP_DEV;
+ break;
+ default :
+ /* process unknonw option. */
+ cfga_err(errstring, 0, ERRARG_OPT_INVAL,
+ options, 0);
+ S_FREE(hw_option);
+ return (CFGA_ERROR);
+ }
+ }
+ S_FREE(hw_option);
+ }
+
+ /* if force_devinfo is specified check uid = 0 or not. */
+ if (((fp_flags & FLAG_DEVINFO_FORCE) == FLAG_DEVINFO_FORCE) &&
+ (geteuid() != 0)) {
+ return (CFGA_PRIV);
+ }
+
+ fca = 0;
+ if (GET_DYN(ap_id) == NULL) {
+ fca = 1;
+ }
+
+ expand = 0;
+ if ((flags & CFGA_FLAG_LIST_ALL) == CFGA_FLAG_LIST_ALL) {
+ expand = 1;
+ }
+
+ /*
+ * We expand published attachment points but not
+ * dynamic attachment points
+ */
+
+ if (!fca) { /* Stat a single device - no expansion for devices */
+ cmd = FPCFGA_STAT_FC_DEV;
+ } else if (!expand) { /* Stat only the HBA */
+ cmd = FPCFGA_STAT_FCA_PORT;
+ } else { /* Expand HBA attachment point */
+ cmd = FPCFGA_STAT_ALL;
+ }
+
+ ldatalistp = NULL;
+ nelem = 0;
+
+ if ((fp_flags & FLAG_FCP_DEV) == FLAG_FCP_DEV) {
+ ret = do_list_FCP_dev(ap_id, fp_flags, cmd, &ldatalistp, &nelem,
+ errstring);
+ if (ret != FPCFGA_OK) {
+ list_free(&ldatalistp);
+ return (err_cvt(ret));
+ }
+ } else {
+ if ((ret = apidt_create(ap_id, &apidt, errstring))
+ != FPCFGA_OK) {
+ return (err_cvt(ret));
+ }
+
+ if (options != NULL) {
+ apidt.flags |= fp_flags;
+ }
+
+ ret = do_list(&apidt, cmd, &ldatalistp, &nelem, errstring);
+ if (ret != FPCFGA_OK) {
+ list_free(&ldatalistp);
+ apidt_free(&apidt);
+ return (err_cvt(ret));
+ }
+ apidt_free(&apidt);
+ }
+
+ assert(ldatalistp != NULL);
+
+ if (list_ext_postprocess(&ldatalistp, nelem, ap_id_list, nlistp,
+ errstring) != FPCFGA_OK) {
+ assert(*ap_id_list == NULL && *nlistp == 0);
+ ret = FPCFGA_LIB_ERR;
+ } else {
+ assert(*ap_id_list != NULL && *nlistp == nelem);
+ ret = FPCFGA_OK;
+ }
+
+ list_free(&ldatalistp);
+ return (err_cvt(ret));
+}
+
+
+/*ARGSUSED*/
+cfga_err_t
+cfga_help(struct cfga_msg *msgp, const char *options, cfga_flags_t flags)
+{
+ cfga_msg(msgp, MSG_HELP_HDR, MSG_HELP_USAGE, 0);
+
+ return (CFGA_OK);
+
+}
+
+/*ARGSUSED*/
+int
+cfga_ap_id_cmp(const char *ap_id1, const char *ap_id2)
+{
+ int i = 0;
+ long long ret;
+
+ if (ap_id1 == ap_id2) {
+ return (0);
+ }
+
+ if (ap_id1 == NULL || ap_id2 == NULL) {
+ if (ap_id1 == NULL) {
+ /* Return a negative value */
+ return (0 - (uchar_t)ap_id2[0]);
+ } else {
+ return ((uchar_t)ap_id1[0]);
+ }
+ }
+
+ /*
+ * Search for first different char
+ */
+ while (ap_id1[i] == ap_id2[i] && ap_id1[i] != '\0')
+ i++;
+
+ if ((ap_id1[i] == '\0') &&
+ !(strncmp(&ap_id2[i], LUN_COMP_SEP, strlen(LUN_COMP_SEP)))) {
+ return (0);
+ } else if ((ap_id2[i] == '\0') &&
+ !(strncmp(&ap_id1[i], LUN_COMP_SEP, strlen(LUN_COMP_SEP)))) {
+ return (0);
+ }
+
+ /*
+ * If one of the char is a digit, back up to where the
+ * number started, compare the number.
+ */
+ if (isxdigit(ap_id1[i]) || isxdigit(ap_id2[i])) {
+ while ((i > 0) && isxdigit(ap_id1[i - 1]))
+ i--;
+
+ if (isxdigit(ap_id1[i]) && isxdigit(ap_id2[i])) {
+ ret = (strtoll((ap_id1 + i), NULL, 16)) -
+ (strtoll((ap_id2 + i), NULL, 16));
+ if (ret > 0) {
+ return (1);
+ } else if (ret < 0) {
+ return (-1);
+ } else {
+ return (0);
+ }
+ }
+ }
+
+ /* One of them isn't a number, compare the char */
+ return (ap_id1[i] - ap_id2[i]);
+}
diff --git a/usr/src/lib/cfgadm_plugins/fp/common/cfga_fp.h b/usr/src/lib/cfgadm_plugins/fp/common/cfga_fp.h
new file mode 100644
index 0000000000..a0f20c3f1b
--- /dev/null
+++ b/usr/src/lib/cfgadm_plugins/fp/common/cfga_fp.h
@@ -0,0 +1,547 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _CFGA_FP_H
+#define _CFGA_FP_H
+
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+#include <sys/mkdev.h>
+#include <stddef.h>
+#include <locale.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <locale.h>
+#include <langinfo.h>
+#include <time.h>
+#include <stdarg.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <sys/dditypes.h>
+#include <sys/modctl.h>
+#include <libdevinfo.h>
+#include <libdevice.h>
+#include <librcm.h>
+#include <dirent.h>
+#include <strings.h>
+
+
+#include <sys/ioctl.h>
+#include <sys/byteorder.h>
+#include <sys/scsi/scsi.h>
+#include <strings.h>
+#include <sys/vfstab.h>
+#include <sys/stat.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <hbaapi.h>
+#include <sys/fibre-channel/fcio.h>
+#include <sys/fibre-channel/ulp/fcp_util.h>
+
+#include <sys/uio.h>
+#include <sys/param.h>
+
+#include <synch.h>
+#include <thread.h>
+
+#include <limits.h>
+#include <ftw.h>
+
+#define CFGA_PLUGIN_LIB
+#include <config_admin.h>
+
+#if !defined(DEBUG)
+#define NDEBUG 1
+#else
+#undef NDEBUG
+#endif
+
+#include <assert.h>
+
+/* Return/error codes */
+typedef enum {
+ FPCFGA_ERR = -2,
+ FPCFGA_LIB_ERR = -1,
+ FPCFGA_OK = 0,
+ FPCFGA_ACCESS_OK,
+ FPCFGA_NACK,
+ FPCFGA_BUSY,
+ FPCFGA_SYSTEM_BUSY,
+ FPCFGA_APID_NOCONFIGURE,
+ FPCFGA_APID_NOACCESS,
+ FPCFGA_APID_NOEXIST,
+ FPCFGA_OPNOTSUPP,
+ FPCFGA_PRIV,
+ FPCFGA_UNLOCKED,
+ FPCFGA_NO_REC,
+ FPCFGA_OP_INTR,
+ FPCFGA_DB_INVAL,
+ FPCFGA_CONF_OK_UPD_REP_FAILED,
+ FPCFGA_UNCONF_OK_UPD_REP_FAILED,
+ FPCFGA_INVALID_PATH,
+ FPCFGA_VHCI_GET_PATHLIST_FAILED,
+ FPCFGA_XPORT_NOT_IN_PHCI_LIST,
+ FPCFGA_UNKNOWN_ERR,
+ FPCFGA_FCP_TGT_SEND_SCSI_FAILED,
+ FPCFGA_FCP_SEND_SCSI_DEV_NOT_TGT
+} fpcfga_ret_t;
+
+/* Commands used internally */
+typedef enum {
+ FPCFGA_INVAL_CMD = -1,
+ FPCFGA_DEV_OP = 0,
+ FPCFGA_BUS_OP,
+ FPCFGA_STAT_FC_DEV,
+ FPCFGA_STAT_FCA_PORT,
+ FPCFGA_STAT_ALL,
+ FPCFGA_GET_DEVPATH,
+ FPCFGA_INSERT_DEV,
+ FPCFGA_REMOVE_DEV,
+ FPCFGA_REPLACE_DEV,
+ FPCFGA_WALK_NODE,
+ FPCFGA_WALK_MINOR,
+ FPCFGA_BUS_QUIESCE,
+ FPCFGA_BUS_UNQUIESCE,
+ FPCFGA_BUS_GETSTATE,
+ FPCFGA_DEV_GETSTATE,
+ FPCFGA_BUS_CONFIGURE,
+ FPCFGA_BUS_UNCONFIGURE,
+ FPCFGA_DEV_CONFIGURE,
+ FPCFGA_DEV_UNCONFIGURE,
+ FPCFGA_DEV_REMOVE,
+ FPCFGA_RESET_DEV,
+ FPCFGA_RESET_BUS,
+ FPCFGA_RESET_ALL,
+ FPCFGA_READ,
+ FPCFGA_WRITE
+} fpcfga_cmd_t;
+
+typedef enum {
+ FPCFGA_TERMINATE = 0,
+ FPCFGA_CONTINUE
+} fpcfga_recur_t;
+
+
+/* Structures for tree walking code */
+
+typedef struct {
+ uint_t flags;
+ int (*fcn)(di_node_t node, void *argp);
+} walk_node_t;
+
+typedef struct {
+ const char *nodetype;
+ int (*fcn)(di_node_t node, di_minor_t minor, void *argp);
+} walk_minor_t;
+
+typedef union {
+ walk_node_t node_args;
+ walk_minor_t minor_args;
+} walkmode_t;
+
+typedef struct {
+ uint_t flags;
+ walkmode_t walkmode;
+} walkarg_t;
+
+typedef struct {
+ char *phys;
+ char *log;
+ fpcfga_ret_t ret;
+ int match_minor;
+ int l_errno;
+} pathm_t;
+
+typedef struct ldata_list {
+ cfga_list_data_t ldata;
+ struct ldata_list *next;
+} ldata_list_t;
+
+typedef struct {
+ struct cfga_confirm *confp;
+ struct cfga_msg *msgp;
+} prompt_t;
+
+typedef struct luninfo_list {
+ int lunnum;
+ uint_t node_state;
+ uint_t lun_flag;
+ char *path;
+ struct luninfo_list *next;
+} luninfo_list_t;
+
+typedef struct {
+ char *xport_phys;
+ char *dyncomp;
+ uint_t flags;
+ luninfo_list_t *lunlist; /* Singly linked list */
+} apid_t;
+
+/* Report luns names */
+#define FP_SCMD_REPORT_LUN 0xA0
+#define DEFAULT_NUM_LUN 1024
+#define REPORT_LUN_HDR_SIZE 8
+#define SAM_LUN_SIZE 8
+
+#ifdef _BIG_ENDIAN
+#define htonll(x) (x)
+#define ntohll(x) (x)
+#else
+#define htonll(x) ((((unsigned long long)htonl(x)) << 32) + htonl(x >> 32))
+#define ntohll(x) ((((unsigned long long)ntohl(x)) << 32) + ntohl(x >> 32))
+#endif
+
+typedef struct report_lun_resp {
+ uint32_t num_lun;
+ uint32_t reserved;
+ longlong_t lun_string[DEFAULT_NUM_LUN];
+} report_lun_resp_t;
+
+/*
+ * Hardware options acceptable for fp plugin.
+ * list related options are handled by getsupopts() and set to
+ * index of array.
+ */
+#define OPT_DEVINFO_FORCE 0
+#define OPT_SHOW_SCSI_LUN 1
+#define OPT_FCP_DEV 2
+#define OPT_DISABLE_RCM 0
+#define OPT_FORCE_UPDATE_REP 1
+#define OPT_NO_UPDATE_REP 2
+#define OPT_REMOVE_UNUSABLE_SCSI_LUN 3
+#define OPT_REMOVE_UNUSABLE_FCP_DEV 4
+
+/* walk tree flag */
+#define FLAG_PATH_INFO_WALK 0x00000001
+
+/* apid_t flags */
+#define FLAG_DISABLE_RCM 0x00000001
+#define FLAG_FORCE_UPDATE_REP 0x00000010
+#define FLAG_NO_UPDATE_REP 0x00000100
+#define FLAG_DYN_AP_CONFIGURED 0x00001000
+#define FLAG_DEVINFO_FORCE 0x00010000
+#define FLAG_FCP_DEV 0x00100000
+#define FLAG_REMOVE_UNUSABLE_FCP_DEV 0x01000000
+
+/* apid_t lun flags */
+#define FLAG_SKIP_RCMOFFLINE 0x00000001
+#define FLAG_SKIP_RCMREMOVE 0x00000010
+#define FLAG_SKIP_ONLINEOTHERS 0x00000100
+
+/* define for peripheral qualifier mask */
+#define FP_PERI_QUAL_MASK 0xE0
+
+/* Message ids */
+typedef enum {
+
+/* ERRORS */
+ERR_UNKNOWN = -1,
+ERR_OP_FAILED,
+ERR_CMD_INVAL,
+ERR_NOT_BUSAPID,
+ERR_APID_INVAL,
+ERR_NOT_BUSOP,
+ERR_NOT_DEVOP,
+ERR_UNAVAILABLE,
+ERR_CTRLR_CRIT,
+ERR_BUS_GETSTATE,
+ERR_BUS_NOTCONNECTED,
+ERR_BUS_CONNECTED,
+ERR_BUS_QUIESCE,
+ERR_BUS_UNQUIESCE,
+ERR_BUS_CONFIGURE,
+ERR_BUS_UNCONFIGURE,
+ERR_DEV_CONFIGURE,
+ERR_DEV_UNCONFIGURE,
+ERR_FCA_CONFIGURE,
+ERR_FCA_UNCONFIGURE,
+ERR_DEV_REPLACE,
+ERR_DEV_INSERT,
+ERR_DEV_GETSTATE,
+ERR_RESET,
+ERR_LIST,
+ERR_FC,
+ERR_FC_GET_DEVLIST,
+ERR_FC_GET_FIRST_DEV,
+ERR_FC_GET_NEXT_DEV,
+ERRARG_FC_DEV_MAP_INIT,
+ERRARG_FC_PROP_LOOKUP_BYTES,
+ERRARG_FC_INQUIRY,
+ERRARG_FC_REP_LUNS,
+ERRARG_FC_TOPOLOGY,
+ERRARG_PATH_TOO_LONG,
+ERRARG_INVALID_PATH,
+ERRARG_OPENDIR,
+ERRARG_VHCI_GET_PATHLIST,
+ERRARG_XPORT_NOT_IN_PHCI_LIST,
+ERR_SIG_STATE,
+ERR_MAYBE_BUSY,
+ERR_BUS_DEV_MISMATCH,
+ERR_GET_DEVLIST,
+ERR_MEM_ALLOC,
+ERR_DEVCTL_OFFLINE,
+ERR_UPD_REP,
+ERR_CONF_OK_UPD_REP,
+ERR_UNCONF_OK_UPD_REP,
+ERR_PARTIAL_SUCCESS,
+ERR_HBA_LOAD_LIBRARY,
+ERR_MATCHING_HBA_PORT,
+ERR_NO_ADAPTER_FOUND,
+
+/* Errors with arguments */
+ERRARG_OPT_INVAL,
+ERRARG_HWCMD_INVAL,
+ERRARG_DEVINFO,
+ERRARG_NOT_IN_DEVLIST,
+ERRARG_NOT_IN_DEVINFO,
+ERRARG_DI_GET_PROP,
+ERRARG_DC_DDEF_ALLOC,
+ERRARG_DC_BYTE_ARRAY,
+ERRARG_DC_BUS_ACQUIRE,
+ERRARG_BUS_DEV_CREATE,
+ERRARG_BUS_DEV_CREATE_UNKNOWN,
+ERRARG_DEV_ACQUIRE,
+ERRARG_DEV_REMOVE,
+
+/* RCM Errors */
+ERR_RCM_HANDLE,
+ERRARG_RCM_SUSPEND,
+ERRARG_RCM_RESUME,
+ERRARG_RCM_OFFLINE,
+ERRARG_RCM_ONLINE,
+ERRARG_RCM_REMOVE,
+ERRARG_RCM_INFO,
+
+/* Commands */
+CMD_INSERT_DEV,
+CMD_REMOVE_DEV,
+CMD_REPLACE_DEV,
+CMD_RESET_DEV,
+CMD_RESET_BUS,
+CMD_RESET_ALL,
+
+/* help messages */
+MSG_HELP_HDR,
+MSG_HELP_USAGE,
+
+/* Hotplug messages */
+MSG_INSDEV,
+MSG_RMDEV,
+MSG_REPLDEV,
+
+/* Hotplugging confirmation prompts */
+CONF_QUIESCE_1,
+CONF_QUIESCE_2,
+CONF_UNQUIESCE,
+
+/* Misc. */
+WARN_DISCONNECT
+} msgid_t;
+
+typedef struct {
+ msgid_t str_id;
+ fpcfga_cmd_t cmd;
+ fpcfga_ret_t (*fcn)(fpcfga_cmd_t, apid_t *, prompt_t *, char **);
+} hw_cmd_t;
+
+typedef struct {
+ msgid_t msgid;
+ int nargs; /* Number of arguments following msgid */
+ int intl; /* Flag: if 1, internationalize */
+ const char *msgstr;
+} msgcvt_t;
+
+
+#define SLASH "/"
+#define CFGA_DEV_DIR "/dev/cfg"
+#define DEV_DIR "/dev"
+#define DEVICES_DIR "/devices"
+#define DEV_DSK "/dev/dsk"
+#define DEV_RDSK "/dev/rdsk"
+#define DEV_RMT "/dev/rmt"
+#define DSK_DIR "dsk"
+#define RDSK_DIR "rdsk"
+#define RMT_DIR "rmt"
+
+
+#define DYN_SEP "::"
+#define LUN_COMP_SEP ","
+#define MINOR_SEP ":"
+
+#define S_FREE(x) (((x) != NULL) ? (free(x), (x) = NULL) : (void *)0)
+#define S_STR(x) (((x) == NULL) ? "" : (x))
+
+
+#define IS_STUB_NODE(s) (di_instance(s) == -1 && \
+ di_nodeid(s) == (DI_PROM_NODEID))
+
+#define GET_MSG_STR(i) (str_tbl[msg_idx(i)].msgstr)
+
+#define GET_DYN(a) (((a) != NULL) ? strstr((a), DYN_SEP) : (void *)0)
+#define GET_LUN_DYN(a) (((a) != NULL) ? strstr((a), LUN_COMP_SEP) : (void *)0)
+
+/*
+ * The following macro removes the separator from the dynamic component.
+ */
+#define DYN_TO_DYNCOMP(a) ((a) + strlen(DYN_SEP))
+#define LUN_DYN_TO_LUNCOMP(a) ((a) + strlen(LUN_COMP_SEP))
+
+/*
+ * Property names
+ */
+#define PORT_WWN_PROP "port-wwn"
+#define LUN_GUID_PROP "client-guid"
+#define LUN_PROP "lun"
+
+#define WWN_S_LEN 17 /* NULL terminated string */
+#define WWN_SIZE 8
+/* Constants used for repository updates */
+#define ADD_ENTRY 0
+#define REMOVE_ENTRY 1
+
+#define FAB_REPOSITORY_DIR "/etc/cfg/fp"
+#define FAB_REPOSITORY "/etc/cfg/fp/fabric_WWN_map"
+#define TMP_FAB_REPOSITORY "/etc/cfg/fp/fabric_WWN_map.tmp"
+#define OLD_FAB_REPOSITORY "/etc/cfg/fp/fabric_WWN_map.old"
+
+/* MPXIO VHCI root dir */
+#define SCSI_VHCI_ROOT "/devices/scsi_vhci/"
+#define SCSI_VHCI_DRVR "scsi_vhci"
+#define HBA_MAX_RETRIES 10
+
+/* Function prototypes */
+
+fpcfga_ret_t get_report_lun_data(const char *xport_phys,
+ const char *dyncomp, int *num_luns, report_lun_resp_t **resp_buf,
+ struct scsi_extended_sense *sense, int *l_errnop);
+/* Functions in cfga_cs.c */
+fpcfga_ret_t
+dev_change_state(cfga_cmd_t, apid_t *, la_wwn_t *, cfga_flags_t, char **,
+ HBA_HANDLE handle, HBA_PORTATTRIBUTES portAttrs);
+fpcfga_ret_t
+fca_change_state(cfga_cmd_t, apid_t *, cfga_flags_t, char **);
+
+/* Functions in cfga_rep.c */
+int update_fabric_wwn_list(int, const char *, char **);
+
+fpcfga_ret_t dev_insert(fpcfga_cmd_t cmd, apid_t *apidp, prompt_t *argsp,
+ char **errstring);
+fpcfga_ret_t dev_replace(fpcfga_cmd_t cmd, apid_t *apidp, prompt_t *argsp,
+ char **errstring);
+fpcfga_ret_t dev_remove(fpcfga_cmd_t cmd, apid_t *apidp, prompt_t *argsp,
+ char **errstring);
+fpcfga_ret_t reset_common(fpcfga_cmd_t cmd, apid_t *apidp, prompt_t *argsp,
+ char **errstring);
+
+
+/* List related routines */
+fpcfga_ret_t do_list(apid_t *apidp, fpcfga_cmd_t cmd,
+ ldata_list_t **ldatalistp, int *nelem, char **errstring);
+fpcfga_ret_t do_list_FCP_dev(const char *ap_id, uint_t flags, fpcfga_cmd_t cmd,
+ ldata_list_t **llpp, int *nelemp, char **errstring);
+fpcfga_ret_t list_ext_postprocess(ldata_list_t **ldatalistp, int nelem,
+ cfga_list_data_t **ap_id_list, int *nlistp, char **errstring);
+int stat_path_info_node(di_node_t root, void *arg, int *l_errnop);
+
+/* Conversion routines */
+fpcfga_ret_t make_xport_logid(const char *xport_phys, char **xport_logpp,
+ int *l_errnop);
+fpcfga_ret_t dyn_apid_to_path(const char *xport_phys, const char *dyncomp,
+ struct luninfo_list **lunlistpp, int *l_errnop);
+void cvt_lawwn_to_dyncomp(const la_wwn_t *pwwn, char **dyncomp, int *l_errnop);
+int cvt_dyncomp_to_lawwn(const char *dyncomp, la_wwn_t *port_wwn);
+fpcfga_ret_t make_dyncomp_from_dinode(const di_node_t node, char **dyncompp,
+ int *l_errnop);
+fpcfga_ret_t make_portwwn_luncomp_from_dinode(const di_node_t node,
+ char **dyncompp, int **luncompp, int *l_errnop);
+fpcfga_ret_t make_portwwn_luncomp_from_pinode(const di_path_t pinode,
+ char **dyncompp, int **luncompp, int *l_errnop);
+fpcfga_ret_t construct_nodepath_from_dinode(const di_node_t node,
+ char **node_pathp, int *l_errnop);
+u_longlong_t wwnConversion(uchar_t *wwn);
+
+
+/* Functions in cfga_rcm.c */
+fpcfga_ret_t fp_rcm_offline(char *, char **, cfga_flags_t);
+fpcfga_ret_t fp_rcm_online(char *, char **, cfga_flags_t);
+fpcfga_ret_t fp_rcm_remove(char *, char **, cfga_flags_t);
+fpcfga_ret_t fp_rcm_suspend(char *, char *, char **, cfga_flags_t);
+fpcfga_ret_t fp_rcm_resume(char *, char *, char **, cfga_flags_t);
+fpcfga_ret_t fp_rcm_info(char *, char **, char **);
+
+/* Utility routines */
+fpcfga_ret_t physpath_to_devlink(const char *basedir, char *xport_phys,
+ char **xport_logpp, int *l_errnop, int match_minor);
+fpcfga_ret_t recurse_dev(const char *basedir, void *arg,
+ fpcfga_recur_t (*fcn)(const char *lpath, void *arg));
+fpcfga_ret_t apidt_create(const char *ap_id, apid_t *apidp,
+ char **errstring);
+void apidt_free(apid_t *apidp);
+cfga_err_t err_cvt(fpcfga_ret_t err);
+void list_free(ldata_list_t **llpp);
+int known_state(di_node_t node);
+
+fpcfga_ret_t devctl_cmd(const char *ap_id, fpcfga_cmd_t cmd,
+ uint_t *statep, int *l_errnop);
+fpcfga_ret_t invoke_cmd(const char *func, apid_t *apidt, prompt_t *prp,
+ char **errstring);
+
+void cfga_err(char **errstring, int use_errno, ...);
+void cfga_msg(struct cfga_msg *msgp, ...);
+char *cfga_str(int append_newline, ...);
+int msg_idx(msgid_t msgid);
+fpcfga_ret_t walk_tree(const char *physpath, void *arg, uint_t init_flags,
+ walkarg_t *up, fpcfga_cmd_t cmd, int *l_errnop);
+int hba_dev_cmp(const char *hba, const char *dev);
+int dev_cmp(const char *dev1, const char *dev2, int match_minor);
+char *pathdup(const char *path, int *l_errnop);
+int getPortAttrsByWWN(HBA_HANDLE handle, HBA_WWN wwn,
+ HBA_PORTATTRIBUTES *attrs);
+int getDiscPortAttrs(HBA_HANDLE handle, int portIndex,
+ int discIndex, HBA_PORTATTRIBUTES *attrs);
+int getAdapterPortAttrs(HBA_HANDLE handle, int portIndex,
+ HBA_PORTATTRIBUTES *attrs);
+int getAdapterAttrs(HBA_HANDLE handle, HBA_ADAPTERATTRIBUTES *attrs);
+fpcfga_ret_t findMatchingAdapterPort(char *portPath,
+ HBA_HANDLE *matchingHandle, int *matchingPortIndex,
+ HBA_PORTATTRIBUTES *matchingPortAttrs, char **errstring);
+
+extern msgcvt_t str_tbl[];
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CFGA_FP_H */
diff --git a/usr/src/lib/cfgadm_plugins/fp/common/cfga_list.c b/usr/src/lib/cfgadm_plugins/fp/common/cfga_list.c
new file mode 100644
index 0000000000..8bde695934
--- /dev/null
+++ b/usr/src/lib/cfgadm_plugins/fp/common/cfga_list.c
@@ -0,0 +1,3742 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#include "cfga_fp.h"
+#include <sys/fibre-channel/impl/fc_error.h>
+
+/* Structure for walking the tree */
+typedef struct {
+ apid_t *apidp;
+ char *xport_logp;
+ ldata_list_t *listp;
+ fpcfga_cmd_t cmd;
+ cfga_stat_t chld_config;
+ cfga_type_t xport_type;
+ cfga_stat_t xport_rstate;
+ fpcfga_ret_t ret;
+ int l_errno;
+} fpcfga_list_t;
+
+typedef struct {
+ uint_t itype;
+ const char *ntype;
+ const char *name;
+} fpcfga_devtype_t;
+
+#define ERR_INQ_DTYPE 0xff
+
+/* The TYPE field is parseable and should not contain spaces */
+#define FP_FC_PORT_TYPE "fc"
+#define FP_FC_PORT_ERROR "fc-error"
+#define FP_FC_FABRIC_PORT_TYPE "fc-fabric"
+#define FP_FC_PUBLIC_PORT_TYPE "fc-public"
+#define FP_FC_PRIVATE_PORT_TYPE "fc-private"
+#define FP_FC_PT_TO_PT_PORT_TYPE "fc-pt_to_pt"
+
+/* Indicates no plag passing */
+#define NO_FLAG 0
+
+/* defines for retry algorithm */
+#define OPEN_RETRY_COUNT 5
+#define OPEN_RETRY_INTERVAL 10000 /* 1/100 of a sec. */
+#define IOCTL_RETRY_COUNT 5
+#define IOCTL_RETRY_INTERVAL 5000000 /* 5 sec */
+
+/* define for fcp scsi passthru wait */
+#define FCP_SCSI_CMD_TIMEOUT 10
+
+/* define for fcp pseudo node */
+#define FCP_PATH "/devices/pseudo/fcp@0:fcp"
+
+/* Function prototypes */
+static fpcfga_ret_t postprocess_list_data(const ldata_list_t *listp,
+ fpcfga_cmd_t cmd, cfga_stat_t chld_config, int *np, uint_t flags);
+static int stat_fc_dev(di_node_t node, void *arg);
+static int stat_FCP_dev(di_node_t node, void *arg);
+static fpcfga_ret_t do_stat_fca_xport(fpcfga_list_t *lap, int limited_stat,
+ HBA_PORTATTRIBUTES portAttrs);
+static int get_xport_state(di_node_t node, void *arg);
+
+static fpcfga_ret_t do_stat_fc_dev(const di_node_t node, const char *nodepath,
+ fpcfga_list_t *lap, int limited_stat);
+static fpcfga_ret_t do_stat_FCP_dev(const di_node_t node, const char *nodepath,
+ fpcfga_list_t *lap, int limited_stat);
+static cfga_stat_t xport_devinfo_to_recep_state(uint_t xport_di_state);
+static cfga_stat_t dev_devinfo_to_occupant_state(uint_t dev_di_state);
+static void get_hw_info(di_node_t node, cfga_list_data_t *clp);
+static const char *get_device_type(di_node_t);
+static fpcfga_ret_t init_ldata_for_accessible_dev(const char *dyncomp,
+ uchar_t inq_type, fpcfga_list_t *lap);
+static fpcfga_ret_t init_ldata_for_accessible_FCP_dev(const char *port_wwn,
+ int num_luns, struct report_lun_resp *resp_buf,
+ fpcfga_list_t *larg, int *l_errnop);
+static fpcfga_ret_t is_dyn_ap_on_ldata_list(const char *port_wwn,
+ const ldata_list_t *listp, ldata_list_t **matchldpp, int *l_errno);
+static fpcfga_ret_t is_FCP_dev_ap_on_ldata_list(const char *port_wwn,
+ const int lun_num, ldata_list_t *ldatap, ldata_list_t **matchldpp);
+
+static fpcfga_ret_t init_ldata_for_mpath_dev(di_path_t path, char *port_wwn,
+ int *l_errnop, fpcfga_list_t *lap);
+static fpcfga_ret_t insert_ldata_to_ldatalist(const char *port_wwn,
+ int *lun_nump, ldata_list_t *listp, ldata_list_t **ldatapp);
+static fpcfga_ret_t insert_fc_dev_ldata(const char *port_wwn,
+ ldata_list_t *listp, ldata_list_t **ldatapp);
+static fpcfga_ret_t insert_FCP_dev_ldata(const char *port_wwn, int lun_num,
+ ldata_list_t *listp, ldata_list_t **ldatapp);
+static int stat_path_info_fc_dev(di_node_t root, fpcfga_list_t *lap,
+ int *l_errnop);
+static int stat_path_info_FCP_dev(di_node_t root, fpcfga_list_t *lap,
+ int *l_errnop);
+static fpcfga_ret_t get_accessible_FCP_dev_ldata(const char *dyncomp,
+ fpcfga_list_t *lap, int *l_errnop);
+static fpcfga_ret_t get_standard_inq_data(const char *xport_phys,
+ const char *dyncomp, uchar_t *lun_num, struct scsi_inquiry **inq_buf,
+ int *l_errnop);
+static void init_fcp_scsi_cmd(struct fcp_scsi_cmd *fscsi, uchar_t *lun_num,
+ la_wwn_t *pwwn, void *scmdbuf, size_t scmdbuf_len, void *respbuf,
+ size_t respbuf_len, void *sensebuf, size_t sensebuf_len);
+static fpcfga_ret_t issue_fcp_scsi_cmd(const char *xport_phys,
+ struct fcp_scsi_cmd *fscsi, int *l_errnop);
+static uchar_t get_inq_dtype(char *xport_phys, char *dyncomp, HBA_HANDLE handle,
+ HBA_PORTATTRIBUTES *portAttrs, HBA_PORTATTRIBUTES *discPortAttrs);
+
+static fpcfga_devtype_t device_list[] = {
+ { DTYPE_DIRECT, DDI_NT_BLOCK_CHAN, "disk"},
+ { DTYPE_DIRECT, DDI_NT_BLOCK, "disk"},
+ { DTYPE_DIRECT, DDI_NT_BLOCK_WWN, "disk"},
+ { DTYPE_DIRECT, DDI_NT_BLOCK_FABRIC, "disk"},
+ { DTYPE_SEQUENTIAL, DDI_NT_TAPE, "tape"},
+ { DTYPE_PRINTER, NULL, "printer"},
+ { DTYPE_PROCESSOR, NULL, "processor"},
+ { DTYPE_WORM, NULL, "WORM"},
+ { DTYPE_RODIRECT, DDI_NT_CD_CHAN, "CD-ROM"},
+ { DTYPE_RODIRECT, DDI_NT_CD, "CD-ROM"},
+ { DTYPE_SCANNER, NULL, "scanner"},
+ { DTYPE_OPTICAL, NULL, "optical"},
+ { DTYPE_CHANGER, NULL, "med-changer"},
+ { DTYPE_COMM, NULL, "comm-device"},
+ { DTYPE_ARRAY_CTRL, NULL, "array-ctrl"},
+ { DTYPE_ESI, NULL, "ESI"},
+ /*
+ * This has to be the LAST entry for DTYPE_UNKNOWN_INDEX.
+ * Add entries before this.
+ */
+ { DTYPE_UNKNOWN, NULL, "unknown"}
+};
+
+#define N_DEVICE_TYPES (sizeof (device_list) / sizeof (device_list[0]))
+
+#define DTYPE_UNKNOWN_INDEX (N_DEVICE_TYPES - 1)
+
+/*
+ * Main routine for list operation.
+ * It calls various routines to consturct ldata list and
+ * postprocess the list data.
+ *
+ * Overall algorithm:
+ * Get the device list on input hba port and construct ldata list for
+ * accesible devices.
+ * Stat hba port and devices through walking the device tree.
+ * Verify the validity of the list data.
+ */
+fpcfga_ret_t
+do_list(
+ apid_t *apidp,
+ fpcfga_cmd_t cmd,
+ ldata_list_t **llpp,
+ int *nelemp,
+ char **errstring)
+{
+ int n = -1, l_errno = 0, limited_stat;
+ walkarg_t walkarg;
+ fpcfga_list_t larg = {NULL};
+ fpcfga_ret_t ret;
+ la_wwn_t pwwn;
+ char *dyncomp = NULL;
+ HBA_HANDLE handle;
+ HBA_PORTATTRIBUTES portAttrs;
+ HBA_PORTATTRIBUTES discPortAttrs;
+ HBA_STATUS status;
+ int portIndex, discIndex;
+ int retry;
+ uchar_t inq_dtype;
+
+ if (*llpp != NULL || *nelemp != 0) {
+ return (FPCFGA_ERR);
+ }
+
+ /* Create the hba logid (also base component of logical ap_id) */
+ ret = make_xport_logid(apidp->xport_phys, &larg.xport_logp, &l_errno);
+ if (ret != FPCFGA_OK) {
+ cfga_err(errstring, l_errno, ERR_LIST, 0);
+ return (FPCFGA_ERR);
+ }
+
+ assert(larg.xport_logp != NULL);
+
+ larg.cmd = cmd;
+ larg.apidp = apidp;
+ larg.xport_rstate = CFGA_STAT_NONE;
+
+ if ((ret = findMatchingAdapterPort(larg.apidp->xport_phys, &handle,
+ &portIndex, &portAttrs, errstring)) != FPCFGA_OK) {
+ S_FREE(larg.xport_logp);
+ return (ret);
+ }
+
+ /*
+ * If stating a specific device, we will do limited stat on fca port.
+ * otherwise full stat on fca part is required.
+ * If stating a specific device we don't know if it exists or is
+ * configured yet. larg.ret is set to apid noexist for do_stat_dev.
+ * otherwise larg.ret is set to ok initially.
+ */
+ if (larg.cmd == FPCFGA_STAT_FC_DEV) {
+ limited_stat = 1; /* for do_stat_fca_xport */
+ larg.ret = FPCFGA_APID_NOEXIST; /* for stat_fc_dev */
+ } else {
+ limited_stat = 0; /* for do_stat_fca_xport */
+ larg.ret = FPCFGA_OK; /* for stat_fc_dev */
+ }
+
+ /* For all list commands, the fca port needs to be stat'ed */
+ if ((ret = do_stat_fca_xport(&larg, limited_stat,
+ portAttrs)) != FPCFGA_OK) {
+ cfga_err(errstring, larg.l_errno, ERR_LIST, 0);
+ list_free(&larg.listp);
+ S_FREE(larg.xport_logp);
+ HBA_CloseAdapter(handle);
+ HBA_FreeLibrary();
+ return (ret);
+ }
+
+#ifdef DEBUG
+ if (limited_stat) {
+ assert(larg.listp == NULL);
+ } else {
+ assert(larg.listp != NULL);
+ }
+#endif
+ /*
+ * If stat'ing a FCA port or ALL, we have the bus stat data at
+ * this point.
+ * Assume that the bus has no configured children.
+ */
+ larg.chld_config = CFGA_STAT_UNCONFIGURED;
+
+ switch (larg.cmd) {
+ case FPCFGA_STAT_FC_DEV:
+ /* la_wwn_t has uchar_t raw_wwn[8] thus no need to free. */
+ if (cvt_dyncomp_to_lawwn(apidp->dyncomp, &pwwn) != 0) {
+ cfga_err(errstring, 0, ERR_LIST, 0);
+ list_free(&larg.listp);
+ S_FREE(larg.xport_logp);
+ HBA_CloseAdapter(handle);
+ HBA_FreeLibrary();
+ return (FPCFGA_LIB_ERR);
+ }
+ /*
+ * if the dyncomp exists on disco ports construct list_data
+ * otherwise return FPCFGA_APID_NOEXIST.
+ */
+ retry = 0;
+ do {
+ status = getPortAttrsByWWN(handle,
+ *((HBA_WWN *)(&pwwn)), &discPortAttrs);
+ if (status == HBA_STATUS_ERROR_STALE_DATA) {
+ /* get Port Attributes again after refresh. */
+ HBA_RefreshInformation(handle);
+ } else {
+ break; /* either okay or some other error */
+ }
+ } while (retry++ < HBA_MAX_RETRIES);
+
+ if (status == HBA_STATUS_OK) {
+ /*
+ * if dyncomp found in disco ports
+ * construct ldata_list and return.
+ * otherwise continue to stat on dev tree with
+ * larg.ret set to access_ok which informs stat_fc_dev
+ * the existence of device on disco ports.
+ *
+ * if path is null that guatantees the node is not
+ * configured. if node is detached the path
+ * is incomplete and not usable for further
+ * operations like uscsi_inq so take care of it here.
+ */
+ inq_dtype = get_inq_dtype(apidp->xport_phys,
+ apidp->dyncomp, handle, &portAttrs, &discPortAttrs);
+
+ if (init_ldata_for_accessible_dev(apidp->dyncomp,
+ inq_dtype, &larg) != FPCFGA_OK) {
+ cfga_err(errstring, larg.l_errno,
+ ERR_LIST, 0);
+ list_free(&larg.listp);
+ S_FREE(larg.xport_logp);
+ HBA_CloseAdapter(handle);
+ HBA_FreeLibrary();
+ return (FPCFGA_LIB_ERR);
+ }
+ if (apidp->lunlist == NULL) {
+ n = 0;
+ if (postprocess_list_data(
+ larg.listp, cmd,
+ larg.chld_config, &n, NO_FLAG) !=
+ FPCFGA_OK) {
+ cfga_err(errstring,
+ larg.l_errno, ERR_LIST, 0);
+ list_free(&larg.listp);
+ S_FREE(larg.xport_logp);
+ HBA_CloseAdapter(handle);
+ HBA_FreeLibrary();
+ return (FPCFGA_LIB_ERR);
+ }
+ *nelemp = n;
+ *llpp = larg.listp;
+ S_FREE(larg.xport_logp);
+ HBA_CloseAdapter(handle);
+ HBA_FreeLibrary();
+ return (FPCFGA_OK);
+ }
+ larg.ret = FPCFGA_ACCESS_OK;
+ } else if (status == HBA_STATUS_ERROR_ILLEGAL_WWN) {
+ /*
+ * path indicates if the node exists in dev tree.
+ * if not found in dev tree return apid no exist.
+ * otherwise continue to stat with larg.ret set to
+ * apid_noexist.
+ */
+ if (apidp->lunlist == NULL) {
+ list_free(&larg.listp);
+ S_FREE(larg.xport_logp);
+ HBA_CloseAdapter(handle);
+ HBA_FreeLibrary();
+ return (FPCFGA_APID_NOEXIST);
+ }
+ } else { /* any error */
+ /*
+ * path indicates if the node exists in dev tree.
+ * if not found in dev tree return lib error.
+ * otherwise continue to stat with larg.ret set to
+ * apid_noexist.
+ */
+ if (apidp->lunlist == NULL) {
+ cfga_err(errstring, 0, ERR_FC_GET_DEVLIST, 0);
+ list_free(&larg.listp);
+ S_FREE(larg.xport_logp);
+ HBA_CloseAdapter(handle);
+ HBA_FreeLibrary();
+ return (FPCFGA_LIB_ERR);
+ }
+ }
+ break;
+ case FPCFGA_STAT_ALL:
+ /*
+ * for each dev in disco ports, create a ldata_list element.
+ * if if no disco ports found, continue to stat on devinfo tree
+ * to see if any node exist on the fca port.
+ */
+ for (discIndex = 0;
+ discIndex < portAttrs.NumberofDiscoveredPorts;
+ discIndex++) {
+ if (getDiscPortAttrs(handle, portIndex,
+ discIndex, &discPortAttrs)) {
+ /* Move on to the next target */
+ continue;
+ }
+ memcpy(&pwwn, &discPortAttrs.PortWWN, sizeof (la_wwn_t));
+ cvt_lawwn_to_dyncomp(&pwwn, &dyncomp, &l_errno);
+ if (dyncomp == NULL) {
+ cfga_err(errstring, l_errno, ERR_LIST, 0);
+ list_free(&larg.listp);
+ S_FREE(larg.xport_logp);
+ HBA_CloseAdapter(handle);
+ HBA_FreeLibrary();
+ return (FPCFGA_LIB_ERR);
+ }
+ inq_dtype = get_inq_dtype(apidp->xport_phys, dyncomp,
+ handle, &portAttrs, &discPortAttrs);
+
+ if ((ret = init_ldata_for_accessible_dev(
+ dyncomp, inq_dtype, &larg)) != FPCFGA_OK) {
+ S_FREE(dyncomp);
+ cfga_err(errstring, larg.l_errno, ERR_LIST, 0);
+ list_free(&larg.listp);
+ S_FREE(larg.xport_logp);
+ HBA_CloseAdapter(handle);
+ HBA_FreeLibrary();
+ return (FPCFGA_LIB_ERR);
+ }
+ S_FREE(dyncomp);
+ }
+ break;
+ default:
+ break;
+ }
+
+ /* we need to stat at least 1 device for all commands */
+ if (apidp->flags == FLAG_DEVINFO_FORCE) {
+ walkarg.flags = FLAG_DEVINFO_FORCE;
+ } else {
+ walkarg.flags = 0;
+ }
+
+ walkarg.flags |= FLAG_PATH_INFO_WALK;
+ walkarg.walkmode.node_args.flags = DI_WALK_CLDFIRST;
+ walkarg.walkmode.node_args.fcn = stat_fc_dev;
+
+ /*
+ * Subtree is ALWAYS rooted at the HBA (not at the device) as
+ * otherwise deadlock may occur if bus is disconnected.
+ *
+ * DINFOPROP was sufficient on apidp->xport_phys prior to the support
+ * on scsi_vhci child node. In order to get the link between
+ * scsi_vhci node and path info node the snap shot of the
+ * the whole device tree is required with DINFOCPYALL | DINFOPATH flag.
+ */
+ ret = walk_tree(apidp->xport_phys, &larg, DINFOCPYALL | DINFOPATH,
+ &walkarg, FPCFGA_WALK_NODE, &larg.l_errno);
+
+ /*
+ * ret from walk_tree is either FPCFGA_OK or FPCFGA_ERR.
+ * larg.ret is used to detect other errors. Make sure larg.ret
+ * is set to a correct error.
+ */
+ if (ret != FPCFGA_OK || (ret = larg.ret) != FPCFGA_OK) {
+ if (ret != FPCFGA_APID_NOEXIST) {
+ cfga_err(errstring, larg.l_errno, ERR_LIST, 0);
+ }
+ /* if larg.ret = FPCFGA_APID_NOEXIST; */
+ goto out;
+ }
+
+ assert(larg.listp != NULL);
+
+ n = 0;
+ ret = postprocess_list_data(larg.listp, cmd, larg.chld_config, &n,
+ NO_FLAG);
+ if (ret != FPCFGA_OK) {
+ cfga_err(errstring, 0, ERR_LIST, 0);
+ ret = FPCFGA_LIB_ERR;
+ goto out;
+ }
+
+ *nelemp = n;
+ *llpp = larg.listp;
+ ret = FPCFGA_OK;
+ /* FALLTHROUGH */
+out:
+ if (ret != FPCFGA_OK) list_free(&larg.listp);
+ S_FREE(larg.xport_logp);
+ HBA_CloseAdapter(handle);
+ HBA_FreeLibrary();
+ return (ret);
+}
+
+/*
+ * Main routine for list operation when show_FCP_dev option is given.
+ * It calls various routines to consturct ldata list and
+ * postprocess the list data.
+ *
+ * The difference between do_list() and do_list_FCP_dev() is to
+ * process FCP SCSI LUN data list via uscsi report lun operation and
+ * stat lun level instead of port WWN based target level.
+ * The rest of logic is same.
+ *
+ * Overall algorithm:
+ * Get the device list on input hba port and construct ldata list for
+ * accesible devices.
+ * For each configured device, USCSI report lun is issued and ldata list
+ * with FCP device level(LUN) information is created.
+ * Stat hba port and LUN devices through walking the device tree.
+ * Verify the validity of the list data.
+ */
+fpcfga_ret_t
+do_list_FCP_dev(
+ const char *ap_id,
+ uint_t flags,
+ fpcfga_cmd_t cmd,
+ ldata_list_t **llpp,
+ int *nelemp,
+ char **errstring)
+{
+ int n = -1, l_errno = 0, limited_stat, len;
+ walkarg_t walkarg;
+ fpcfga_list_t larg = {NULL};
+ fpcfga_ret_t ret;
+ la_wwn_t pwwn;
+ char *xport_phys = NULL, *dyn = NULL, *dyncomp = NULL,
+ *lun_dyn = NULL;
+ apid_t apid_con = {NULL};
+ HBA_HANDLE handle;
+ HBA_PORTATTRIBUTES portAttrs;
+ HBA_PORTATTRIBUTES discPortAttrs;
+ HBA_STATUS status;
+ int portIndex, discIndex;
+ int retry;
+ uint64_t lun = 0;
+ struct scsi_inquiry inq;
+ struct scsi_extended_sense sense;
+ HBA_UINT8 scsiStatus;
+ uint32_t inquirySize = sizeof (inq),
+ senseSize = sizeof (sense);
+
+ if (*llpp != NULL || *nelemp != 0) {
+ return (FPCFGA_ERR);
+ }
+
+ if ((xport_phys = pathdup(ap_id, &l_errno)) == NULL) {
+ cfga_err(errstring, l_errno, ERR_OP_FAILED, 0);
+ return (FPCFGA_LIB_ERR);
+ }
+
+ /* Extract the base(hba) and dynamic(device) component if any */
+ if ((dyn = GET_DYN(xport_phys)) != NULL) {
+ len = strlen(DYN_TO_DYNCOMP(dyn)) + 1;
+ dyncomp = calloc(1, len);
+ if (dyncomp == NULL) {
+ cfga_err(errstring, errno, ERR_OP_FAILED, 0);
+ S_FREE(xport_phys);
+ return (FPCFGA_LIB_ERR);
+ }
+
+ (void) strcpy(dyncomp, DYN_TO_DYNCOMP(dyn));
+ /* Remove the dynamic component from the base. */
+ *dyn = '\0';
+ /* if lun dyncomp exists delete it */
+ if ((lun_dyn = GET_LUN_DYN(dyncomp)) != NULL) {
+ *lun_dyn = '\0';
+ }
+ }
+
+ apid_con.xport_phys = xport_phys;
+ apid_con.dyncomp = dyncomp;
+ apid_con.flags = flags;
+
+ larg.apidp = &apid_con;
+
+ /* Create the hba logid (also base component of logical ap_id) */
+ ret = make_xport_logid(larg.apidp->xport_phys, &larg.xport_logp,
+ &l_errno);
+ if (ret != FPCFGA_OK) {
+ cfga_err(errstring, l_errno, ERR_LIST, 0);
+ S_FREE(larg.apidp->xport_phys);
+ S_FREE(larg.apidp->dyncomp);
+ return (FPCFGA_ERR);
+ }
+
+ assert(larg.xport_logp != NULL);
+
+ larg.cmd = cmd;
+ larg.xport_rstate = CFGA_STAT_NONE;
+
+ if ((ret = findMatchingAdapterPort(larg.apidp->xport_phys, &handle,
+ &portIndex, &portAttrs, errstring)) != FPCFGA_OK) {
+ S_FREE(larg.xport_logp);
+ S_FREE(larg.apidp->dyncomp);
+ return (ret);
+ }
+
+ /*
+ * If stating a specific device, we will do limited stat on fca port.
+ * otherwise full stat on fca part is required.
+ * If stating a specific device we don't know if it exists or is
+ * configured yet. larg.ret is set to apid noexist for do_stat_dev.
+ * otherwise larg.ret is set to ok initially.
+ */
+ if (larg.cmd == FPCFGA_STAT_FC_DEV) {
+ limited_stat = 1; /* for do_stat_fca_xport */
+ larg.ret = FPCFGA_APID_NOEXIST; /* for stat_fc_dev */
+ } else {
+ limited_stat = 0; /* for do_stat_fca_xport */
+ larg.ret = FPCFGA_OK; /* for stat_fc_dev */
+ }
+
+ /* For all list commands, the fca port needs to be stat'ed */
+ if ((ret = do_stat_fca_xport(&larg, limited_stat,
+ portAttrs)) != FPCFGA_OK) {
+ cfga_err(errstring, larg.l_errno, ERR_LIST, 0);
+ list_free(&larg.listp);
+ S_FREE(larg.xport_logp);
+ S_FREE(larg.apidp->xport_phys);
+ S_FREE(larg.apidp->dyncomp);
+ HBA_CloseAdapter(handle);
+ HBA_FreeLibrary();
+ return (ret);
+ }
+
+ /*
+ * If stat'ing a FCA port or ALL, we have the bus stat data at
+ * this point.
+ * Assume that the bus has no configured children.
+ */
+ larg.chld_config = CFGA_STAT_UNCONFIGURED;
+
+ switch (larg.cmd) {
+ case FPCFGA_STAT_FC_DEV:
+ /* la_wwn_t has uchar_t raw_wwn[8] thus no need to free. */
+ if (cvt_dyncomp_to_lawwn(larg.apidp->dyncomp, &pwwn) != 0) {
+ cfga_err(errstring, 0, ERR_LIST, 0);
+ list_free(&larg.listp);
+ S_FREE(larg.xport_logp);
+ S_FREE(larg.apidp->xport_phys);
+ S_FREE(larg.apidp->dyncomp);
+ HBA_CloseAdapter(handle);
+ HBA_FreeLibrary();
+ return (FPCFGA_LIB_ERR);
+ }
+ /*
+ * if the dyncomp exists on disco ports construct list_data
+ * otherwise return FPCFGA_APID_NOEXIST.
+ */
+ retry = 0;
+ do {
+ status = getPortAttrsByWWN(handle,
+ *((HBA_WWN *)(&pwwn)), &discPortAttrs);
+ if (status == HBA_STATUS_ERROR_STALE_DATA) {
+ /* get Port Attributes again after refresh. */
+ HBA_RefreshInformation(handle);
+ } else {
+ break; /* either okay or some other error */
+ }
+ } while (retry++ < HBA_MAX_RETRIES);
+
+ if (status == HBA_STATUS_OK) {
+ /*
+ * if dyncomp exists only in dev list
+ * construct ldata_list and return.
+ * otherwise continue to stat on dev tree with
+ * larg.ret set to access_ok which informs stat_fc_dev
+ * the existence of device on dev_list.
+ *
+ * if path is null that guatantees the node is not
+ * configured. if node is detached the path
+ * is incomplete and not usable for further
+ * operations like uscsi_inq so take care of it here.
+ */
+ status = HBA_ScsiInquiryV2(handle, portAttrs.PortWWN,
+ discPortAttrs.PortWWN, lun, 0, 0,
+ &inq, &inquirySize, &scsiStatus,
+ &sense, &senseSize);
+ if (status == HBA_STATUS_OK) {
+ inq.inq_dtype = inq.inq_dtype & DTYPE_MASK;
+ } else if (status == HBA_STATUS_ERROR_NOT_A_TARGET) {
+ inq.inq_dtype = DTYPE_UNKNOWN;
+ } else {
+ inq.inq_dtype = ERR_INQ_DTYPE;
+ }
+
+ if (init_ldata_for_accessible_dev(larg.apidp->dyncomp,
+ inq.inq_dtype, &larg) != FPCFGA_OK) {
+ cfga_err(errstring, larg.l_errno,
+ ERR_LIST, 0);
+ list_free(&larg.listp);
+ S_FREE(larg.xport_logp);
+ S_FREE(larg.apidp->xport_phys);
+ S_FREE(larg.apidp->dyncomp);
+ HBA_CloseAdapter(handle);
+ HBA_FreeLibrary();
+ return (FPCFGA_LIB_ERR);
+ }
+ if ((ret = get_accessible_FCP_dev_ldata(
+ larg.apidp->dyncomp, &larg, &l_errno))
+ != FPCFGA_OK) {
+ cfga_err(errstring, larg.l_errno, ERR_LIST, 0);
+ list_free(&larg.listp);
+ S_FREE(larg.xport_logp);
+ S_FREE(larg.apidp->xport_phys);
+ S_FREE(larg.apidp->dyncomp);
+ HBA_CloseAdapter(handle);
+ HBA_FreeLibrary();
+ return (FPCFGA_LIB_ERR);
+ } else {
+ /* continue to stat dev with access okay. */
+ larg.ret = FPCFGA_ACCESS_OK;
+ }
+ } else if (status == HBA_STATUS_ERROR_ILLEGAL_WWN) {
+ /*
+ * path indicates if the node exists in dev tree.
+ * if not found in dev tree return apid no exist.
+ * otherwise continue to stat with larg.ret set to
+ * apid_noexist.
+ */
+ if (larg.apidp->lunlist == NULL) {
+ list_free(&larg.listp);
+ S_FREE(larg.xport_logp);
+ HBA_CloseAdapter(handle);
+ HBA_FreeLibrary();
+ return (FPCFGA_APID_NOEXIST);
+ }
+ } else { /* not found or any error */
+ /*
+ * continue to stat dev with larg.ret set to
+ * apid_noexist.
+ */
+ larg.ret = FPCFGA_APID_NOEXIST;
+ }
+ break;
+ case FPCFGA_STAT_ALL:
+ /*
+ * for each dev in disco ports, create a ldata_list element.
+ * if if no disco ports found, continue to stat on devinfo tree
+ * to see if any node exist on the fca port.
+ */
+ for (discIndex = 0;
+ discIndex < portAttrs.NumberofDiscoveredPorts;
+ discIndex++) {
+ if (getDiscPortAttrs(handle, portIndex,
+ discIndex, &discPortAttrs)) {
+ /* Move on to the next target */
+ continue;
+ }
+ memcpy(&pwwn, &discPortAttrs.PortWWN, sizeof (la_wwn_t));
+ cvt_lawwn_to_dyncomp(&pwwn, &dyncomp, &l_errno);
+ if (dyncomp == NULL) {
+ cfga_err(errstring, l_errno, ERR_LIST, 0);
+ list_free(&larg.listp);
+ S_FREE(larg.xport_logp);
+ S_FREE(larg.apidp->xport_phys);
+ S_FREE(larg.apidp->dyncomp);
+ HBA_CloseAdapter(handle);
+ HBA_FreeLibrary();
+ return (FPCFGA_LIB_ERR);
+ }
+ status = HBA_ScsiInquiryV2(handle, portAttrs.PortWWN,
+ discPortAttrs.PortWWN, lun, 0, 0,
+ &inq, &inquirySize, &scsiStatus,
+ &sense, &senseSize);
+ if (status == HBA_STATUS_OK) {
+ inq.inq_dtype = inq.inq_dtype & DTYPE_MASK;
+ } else if (status == HBA_STATUS_ERROR_NOT_A_TARGET) {
+ inq.inq_dtype = DTYPE_UNKNOWN;
+ } else {
+ inq.inq_dtype = ERR_INQ_DTYPE;
+ }
+ if ((ret = init_ldata_for_accessible_dev(
+ dyncomp, inq.inq_dtype, &larg)) != FPCFGA_OK) {
+ S_FREE(dyncomp);
+ cfga_err(errstring, larg.l_errno, ERR_LIST, 0);
+ list_free(&larg.listp);
+ S_FREE(larg.xport_logp);
+ S_FREE(larg.apidp->xport_phys);
+ S_FREE(larg.apidp->dyncomp);
+ HBA_CloseAdapter(handle);
+ HBA_FreeLibrary();
+ return (FPCFGA_LIB_ERR);
+ }
+ if ((ret = get_accessible_FCP_dev_ldata(
+ dyncomp, &larg, &l_errno)) != FPCFGA_OK) {
+ S_FREE(dyncomp);
+ cfga_err(errstring, larg.l_errno, ERR_LIST, 0);
+ list_free(&larg.listp);
+ S_FREE(larg.xport_logp);
+ S_FREE(larg.apidp->xport_phys);
+ S_FREE(larg.apidp->dyncomp);
+ HBA_CloseAdapter(handle);
+ HBA_FreeLibrary();
+ return (ret);
+ }
+ S_FREE(dyncomp);
+ }
+ break;
+ /* default: continue */
+ }
+
+ /* we need to stat at least 1 device for all commands */
+ if ((larg.apidp->flags & FLAG_DEVINFO_FORCE) == FLAG_DEVINFO_FORCE) {
+ walkarg.flags = FLAG_DEVINFO_FORCE;
+ } else {
+ walkarg.flags = 0;
+ }
+
+ walkarg.flags |= FLAG_PATH_INFO_WALK;
+ walkarg.walkmode.node_args.flags = DI_WALK_CLDFIRST;
+ walkarg.walkmode.node_args.fcn = stat_FCP_dev;
+
+ /*
+ * Subtree is ALWAYS rooted at the HBA (not at the device) as
+ * otherwise deadlock may occur if bus is disconnected.
+ *
+ * DINFOPROP was sufficient on apidp->xport_phys prior to the support
+ * on scsi_vhci child node. In order to get the link between
+ * scsi_vhci node and path info node the snap shot of the
+ * the whole device tree is required with DINFOCPYALL | DINFOPATH flag.
+ */
+ ret = walk_tree(larg.apidp->xport_phys, &larg, DINFOCPYALL | DINFOPATH,
+ &walkarg, FPCFGA_WALK_NODE, &larg.l_errno);
+
+ /*
+ * ret from walk_tree is either FPCFGA_OK or FPCFGA_ERR.
+ * larg.ret is used to detect other errors. Make sure larg.ret
+ * is set to a correct error.
+ */
+ if (ret != FPCFGA_OK || (ret = larg.ret) != FPCFGA_OK) {
+ if (ret != FPCFGA_APID_NOEXIST) {
+ cfga_err(errstring, larg.l_errno, ERR_LIST, 0);
+ }
+ /* if larg.ret = FPCFGA_APID_NOEXIST return. */
+ list_free(&larg.listp);
+ S_FREE(larg.xport_logp);
+ S_FREE(larg.apidp->xport_phys);
+ S_FREE(larg.apidp->dyncomp);
+ HBA_CloseAdapter(handle);
+ HBA_FreeLibrary();
+ return (ret);
+ }
+
+ assert(larg.listp != NULL);
+
+ n = 0;
+ ret = postprocess_list_data(larg.listp, cmd, larg.chld_config, &n,
+ flags);
+ if (ret != FPCFGA_OK) {
+ cfga_err(errstring, 0, ERR_LIST, 0);
+ list_free(&larg.listp);
+ S_FREE(larg.xport_logp);
+ S_FREE(larg.apidp->xport_phys);
+ S_FREE(larg.apidp->dyncomp);
+ HBA_CloseAdapter(handle);
+ HBA_FreeLibrary();
+ return (FPCFGA_LIB_ERR);
+ }
+
+ *nelemp = n;
+ *llpp = larg.listp;
+ ret = FPCFGA_OK;
+ S_FREE(larg.xport_logp);
+ S_FREE(larg.apidp->xport_phys);
+ S_FREE(larg.apidp->dyncomp);
+ HBA_CloseAdapter(handle);
+ HBA_FreeLibrary();
+ return (FPCFGA_OK);
+}
+
+/*
+ * This routine returns initialize struct fcp_ioctl.
+ */
+static void
+init_fcp_scsi_cmd(
+ struct fcp_scsi_cmd *fscsi,
+ uchar_t *lun_num,
+ la_wwn_t *pwwn,
+ void *scmdbuf,
+ size_t scmdbuf_len,
+ void *respbuf,
+ size_t respbuf_len,
+ void *sensebuf,
+ size_t sensebuf_len)
+{
+ memset(fscsi, 0, sizeof (struct fcp_scsi_cmd));
+ memset(scmdbuf, 0, scmdbuf_len);
+ memcpy(fscsi->scsi_fc_pwwn.raw_wwn, pwwn, sizeof (u_longlong_t));
+ fscsi->scsi_fc_rspcode = 0;
+ fscsi->scsi_flags = FCP_SCSI_READ;
+ fscsi->scsi_timeout = FCP_SCSI_CMD_TIMEOUT; /* second */
+ fscsi->scsi_cdbbufaddr = (caddr_t)scmdbuf;
+ fscsi->scsi_cdblen = scmdbuf_len;
+ fscsi->scsi_bufaddr = (caddr_t)respbuf;
+ fscsi->scsi_buflen = respbuf_len;
+ fscsi->scsi_bufresid = 0;
+ fscsi->scsi_bufstatus = 0;
+ fscsi->scsi_rqbufaddr = (caddr_t)sensebuf;
+ fscsi->scsi_rqlen = sensebuf_len;
+ fscsi->scsi_rqresid = 0;
+ memcpy(&fscsi->scsi_lun, lun_num, sizeof (fscsi->scsi_lun));
+}
+
+/*
+ * This routine returns issues FCP_TGT_SEND_SCSI
+ */
+static fpcfga_ret_t
+issue_fcp_scsi_cmd(
+ const char *xport_phys,
+ struct fcp_scsi_cmd *fscsi,
+ int *l_errnop)
+{
+ struct stat stbuf;
+ int fcp_fd, retry, rv;
+
+ if (stat(xport_phys, &stbuf) < 0) {
+ *l_errnop = errno;
+ return (FPCFGA_LIB_ERR);
+ }
+
+ fscsi->scsi_fc_port_num = (uint32_t)minor(stbuf.st_rdev);
+ fcp_fd = open(FCP_PATH, O_RDONLY | O_NDELAY);
+ retry = 0;
+ while (fcp_fd < 0 && retry++ < OPEN_RETRY_COUNT && (
+ errno == EBUSY || errno == EAGAIN)) {
+ (void) usleep(OPEN_RETRY_INTERVAL);
+ fcp_fd = open(FCP_PATH, O_RDONLY|O_NDELAY);
+ }
+ if (fcp_fd < 0) {
+ *l_errnop = errno;
+ return (FPCFGA_LIB_ERR);
+ }
+
+ rv = ioctl(fcp_fd, FCP_TGT_SEND_SCSI, fscsi);
+ retry = 0;
+ while ((rv != 0 && retry++ < IOCTL_RETRY_COUNT &&
+ (errno == EBUSY || errno == EAGAIN)) ||
+ (retry++ < IOCTL_RETRY_COUNT &&
+ ((uchar_t)fscsi->scsi_bufstatus & STATUS_MASK)
+ == STATUS_BUSY)) {
+ (void) usleep(IOCTL_RETRY_INTERVAL);
+ rv = ioctl(fcp_fd, FCP_TGT_SEND_SCSI, fscsi);
+ }
+ close(fcp_fd);
+
+ if (fscsi->scsi_fc_status == FC_DEVICE_NOT_TGT) {
+ return (FPCFGA_FCP_SEND_SCSI_DEV_NOT_TGT);
+ } else if (rv != 0 || fscsi->scsi_bufstatus != 0) {
+ *l_errnop = errno;
+ return (FPCFGA_FCP_TGT_SEND_SCSI_FAILED);
+ }
+ return (FPCFGA_OK);
+}
+
+/*
+ * This routine returns standard inq data for
+ * a target represented by dyncomp.
+ *
+ * Calls FCP passthru ioctl FCP_TGT_SEND_SCSI to get inquiry data.
+ *
+ * Caller should free the *inq_buf.
+ */
+static fpcfga_ret_t
+get_standard_inq_data(
+ const char *xport_phys,
+ const char *dyncomp,
+ uchar_t *lun_num,
+ struct scsi_inquiry **inq_buf,
+ int *l_errnop)
+{
+ struct fcp_scsi_cmd fscsi;
+ struct scsi_extended_sense sensebuf;
+ union scsi_cdb scsi_inq_req;
+ la_wwn_t pwwn;
+ int alloc_len;
+ fpcfga_ret_t ret;
+
+
+ alloc_len = sizeof (struct scsi_inquiry);
+ if ((*inq_buf = (struct scsi_inquiry *)calloc(1, alloc_len)) == NULL) {
+ *l_errnop = errno;
+ return (FPCFGA_LIB_ERR);
+ }
+
+ if (cvt_dyncomp_to_lawwn(dyncomp, &pwwn) != 0) {
+ return (FPCFGA_LIB_ERR);
+ }
+
+ init_fcp_scsi_cmd(&fscsi, lun_num, &pwwn, &scsi_inq_req,
+ sizeof (scsi_inq_req), *inq_buf, alloc_len, &sensebuf,
+ sizeof (struct scsi_extended_sense));
+ scsi_inq_req.scc_cmd = SCMD_INQUIRY;
+ scsi_inq_req.g0_count0 = sizeof (struct scsi_inquiry);
+
+ if ((ret = issue_fcp_scsi_cmd(xport_phys, &fscsi, l_errnop))
+ != FPCFGA_OK) {
+ S_FREE(*inq_buf);
+ return (ret);
+ }
+
+ return (FPCFGA_OK);
+}
+
+/*
+ * This routine returns report lun data and number of luns found
+ * on a target represented by dyncomp.
+ *
+ * Calls FCP passthru ioctl FCP_TGT_SEND_SCSI to get report lun data.
+ *
+ * Caller should free the *resp_buf when FPCFGA_OK is returned.
+ */
+fpcfga_ret_t
+get_report_lun_data(
+ const char *xport_phys,
+ const char *dyncomp,
+ int *num_luns,
+ report_lun_resp_t **resp_buf,
+ struct scsi_extended_sense *sensebuf,
+ int *l_errnop)
+{
+ struct fcp_scsi_cmd fscsi;
+ union scsi_cdb scsi_rl_req;
+ la_wwn_t pwwn;
+ int alloc_len;
+ fpcfga_ret_t ret;
+ uchar_t lun_data[SAM_LUN_SIZE];
+
+ alloc_len = sizeof (struct report_lun_resp);
+ if ((*resp_buf = (report_lun_resp_t *)calloc(1, alloc_len)) == NULL) {
+ *l_errnop = errno;
+ return (FPCFGA_LIB_ERR);
+ }
+
+ if (cvt_dyncomp_to_lawwn(dyncomp, &pwwn) != 0) {
+ S_FREE(*resp_buf);
+ return (FPCFGA_LIB_ERR);
+ }
+
+ /* sending to LUN 0 so initializing lun_data buffer to be 0 */
+ memset(lun_data, 0, sizeof (lun_data));
+ init_fcp_scsi_cmd(&fscsi, lun_data, &pwwn, &scsi_rl_req,
+ sizeof (scsi_rl_req), *resp_buf, alloc_len, sensebuf,
+ sizeof (struct scsi_extended_sense));
+ scsi_rl_req.scc_cmd = FP_SCMD_REPORT_LUN;
+ FORMG5COUNT(&scsi_rl_req, alloc_len);
+
+ if ((ret = issue_fcp_scsi_cmd(xport_phys, &fscsi, l_errnop))
+ != FPCFGA_OK) {
+ S_FREE(*resp_buf);
+ return (ret);
+ }
+
+ if (ntohl((*resp_buf)->num_lun) >
+ (sizeof (struct report_lun_resp) - REPORT_LUN_HDR_SIZE)) {
+ alloc_len = (*resp_buf)->num_lun + REPORT_LUN_HDR_SIZE;
+ S_FREE(*resp_buf);
+ if ((*resp_buf = (report_lun_resp_t *)calloc(1, alloc_len))
+ == NULL) {
+ *l_errnop = errno;
+ return (FPCFGA_LIB_ERR);
+ }
+ (void) memset((char *)*resp_buf, 0, alloc_len);
+ FORMG5COUNT(&scsi_rl_req, alloc_len);
+
+ fscsi.scsi_bufaddr = (caddr_t)*resp_buf;
+ fscsi.scsi_buflen = alloc_len;
+
+ if ((ret = issue_fcp_scsi_cmd(xport_phys, &fscsi, l_errnop))
+ != FPCFGA_OK) {
+ S_FREE(*resp_buf);
+ return (ret);
+ }
+ }
+
+ /* num_lun represent number of luns * 8. */
+ *num_luns = ntohl((*resp_buf)->num_lun) >> 3;
+
+ return (FPCFGA_OK);
+}
+
+/*
+ * Routine for consturct ldata list for each FCP SCSI LUN device
+ * for a discovered target device.
+ * It calls get_report_lun_data to get report lun data and
+ * construct ldata list per each lun.
+ *
+ * It is called only when show_FCP_dev option is given.
+ *
+ * Overall algorithm:
+ * Get the report lun data thru FCP passthru ioctl.
+ * Call init_ldata_for_accessible_FCP_dev to process the report LUN data.
+ * For each LUN found standard inquiry is issued to get device type.
+ */
+static fpcfga_ret_t
+get_accessible_FCP_dev_ldata(
+ const char *dyncomp,
+ fpcfga_list_t *lap,
+ int *l_errnop)
+{
+ report_lun_resp_t *resp_buf;
+ struct scsi_extended_sense sense;
+ int num_luns;
+ fpcfga_ret_t ret;
+
+ memset(&sense, 0, sizeof (sense));
+ if ((ret = get_report_lun_data(lap->apidp->xport_phys, dyncomp,
+ &num_luns, &resp_buf, &sense, l_errnop)) != FPCFGA_OK) {
+ /*
+ * when report lun data fails then return FPCFGA_OK thus
+ * keep the ldata for the target which is acquired previously.
+ * For remote hba node this will be normal.
+ * For a target error may already be detected through
+ * FCP_TGT_INQ.
+ */
+ if ((ret == FPCFGA_FCP_TGT_SEND_SCSI_FAILED) ||
+ (ret == FPCFGA_FCP_SEND_SCSI_DEV_NOT_TGT)) {
+ ret = FPCFGA_OK;
+ }
+ return (ret);
+ }
+
+ if (num_luns > 0) {
+ ret = init_ldata_for_accessible_FCP_dev(
+ dyncomp, num_luns, resp_buf, lap, l_errnop);
+ } else {
+ /*
+ * proceed with to stat if no lun found.
+ * This will make the target apid will be kept.
+ */
+ ret = FPCFGA_OK;
+ }
+
+ S_FREE(resp_buf);
+ return (ret);
+}
+
+/*
+ * Routine for checking validity of ldata list based on input argumemnt.
+ * Set the occupant state of hba port if the list is valid.
+ */
+static fpcfga_ret_t
+postprocess_list_data(
+ const ldata_list_t *listp,
+ fpcfga_cmd_t cmd,
+ cfga_stat_t chld_config,
+ int *np,
+ uint_t flags)
+{
+ ldata_list_t *tmplp = NULL;
+ cfga_list_data_t *xport_ldatap = NULL;
+ int i;
+
+
+ *np = 0;
+
+ if (listp == NULL) {
+ return (FPCFGA_ERR);
+ }
+
+ tmplp = (ldata_list_t *)listp;
+ for (i = 0; tmplp != NULL; tmplp = tmplp->next) {
+ i++;
+ if (GET_DYN(tmplp->ldata.ap_phys_id) == NULL) {
+ /* A bus stat data */
+ assert(GET_DYN(tmplp->ldata.ap_log_id) == NULL);
+ xport_ldatap = &tmplp->ldata;
+#ifdef DEBUG
+ } else {
+ assert(GET_DYN(tmplp->ldata.ap_log_id) != NULL);
+#endif
+ }
+ }
+
+ switch (cmd) {
+ case FPCFGA_STAT_FC_DEV:
+ if ((flags & FLAG_FCP_DEV) == FLAG_FCP_DEV) {
+ if (i < 1 || xport_ldatap != NULL) {
+ return (FPCFGA_LIB_ERR);
+ }
+ } else {
+ if (i != 1 || xport_ldatap != NULL) {
+ return (FPCFGA_LIB_ERR);
+ }
+ }
+ break;
+ case FPCFGA_STAT_FCA_PORT:
+ if (i != 1 || xport_ldatap == NULL) {
+ return (FPCFGA_LIB_ERR);
+ }
+ break;
+ case FPCFGA_STAT_ALL:
+ if (i < 1 || xport_ldatap == NULL) {
+ return (FPCFGA_LIB_ERR);
+ }
+ break;
+ default:
+ return (FPCFGA_LIB_ERR);
+ }
+
+ *np = i;
+
+ /* Fill in the occupant (child) state. */
+ if (xport_ldatap != NULL) {
+ xport_ldatap->ap_o_state = chld_config;
+ }
+ return (FPCFGA_OK);
+}
+
+/*
+ * Routine for checking each target device found in device tree.
+ * When the matching port WWN dev is found from the accessble ldata list
+ * the target device is updated with configured ostate.
+ *
+ * Overall algorithm:
+ * Parse the device tree to find configured devices which matches with
+ * list argument. If cmd is stat on a specific target device it
+ * matches port WWN and continues to further processing. If cmd is
+ * stat on hba port all the device target under the hba are processed.
+ */
+static int
+stat_fc_dev(di_node_t node, void *arg)
+{
+ fpcfga_list_t *lap = NULL;
+ char *devfsp = NULL, *nodepath = NULL;
+ size_t len = 0;
+ int limited_stat = 0, match_minor, rv;
+ fpcfga_ret_t ret;
+ di_prop_t prop = DI_PROP_NIL;
+ uchar_t *port_wwn_data;
+ char port_wwn[WWN_SIZE*2+1];
+ int count;
+
+ lap = (fpcfga_list_t *)arg;
+
+ /*
+ * Skip partial nodes
+ *
+ * This checking is from the scsi plug-in and will be deleted for
+ * fp plug-in. The node will be processed for fp even if it is
+ * in driver detached state. From fp perspective the node is configured
+ * as long as the node is not in offline or down state.
+ * scsi plug-in considers the known state when it is offlined
+ * regradless of driver detached state or when it is not in driver
+ * detached state like normal state.
+ * If the node is only in driver detached state it is considered as
+ * unknown state.
+ *
+ * if (!known_state(node) && (lap->cmd != FPCFGA_STAT_FC_DEV)) {
+ * return (DI_WALK_CONTINUE);
+ *
+ */
+
+ devfsp = di_devfs_path(node);
+ if (devfsp == NULL) {
+ rv = DI_WALK_CONTINUE;
+ goto out;
+ }
+
+ len = strlen(DEVICES_DIR) + strlen(devfsp) + 1;
+
+ nodepath = calloc(1, len);
+ if (nodepath == NULL) {
+ lap->l_errno = errno;
+ lap->ret = FPCFGA_LIB_ERR;
+ rv = DI_WALK_TERMINATE;
+ goto out;
+ }
+
+ (void) snprintf(nodepath, len, "%s%s", DEVICES_DIR, devfsp);
+
+ /* Skip node if it is HBA */
+ match_minor = 0;
+ if (!dev_cmp(lap->apidp->xport_phys, nodepath, match_minor)) {
+ rv = DI_WALK_CONTINUE;
+ goto out;
+ }
+
+ /* If stat'ing a specific device, is this node that device */
+ if (lap->cmd == FPCFGA_STAT_FC_DEV) {
+ /* checks port wwn property to find a match */
+ while ((prop = di_prop_next(node, prop))
+ != DI_PROP_NIL) {
+ if ((strcmp(PORT_WWN_PROP,
+ di_prop_name(prop)) == 0) &&
+ (di_prop_type(prop) ==
+ DI_PROP_TYPE_BYTE)) {
+ break;
+ }
+ }
+
+ if (prop != DI_PROP_NIL) {
+ count = di_prop_bytes(prop, &port_wwn_data);
+ if (count != WWN_SIZE) {
+ lap->ret = FPCFGA_LIB_ERR;
+ rv = DI_WALK_TERMINATE;
+ goto out;
+ }
+ (void) sprintf(port_wwn, "%016llx",
+ (wwnConversion(port_wwn_data)));
+ /*
+ * port wwn doesn't match contine to walk
+ * if match call do_stat_fc_dev.
+ */
+ if (strncmp(port_wwn, lap->apidp->dyncomp,
+ WWN_SIZE*2)) {
+ rv = DI_WALK_CONTINUE;
+ goto out;
+ }
+ } else {
+ rv = DI_WALK_CONTINUE;
+ goto out;
+ }
+ }
+
+ /*
+ * If stat'ing a xport only, we look at device nodes only to get
+ * xport configuration status. So a limited stat will suffice.
+ */
+ if (lap->cmd == FPCFGA_STAT_FCA_PORT) {
+ limited_stat = 1;
+ } else {
+ limited_stat = 0;
+ }
+
+ /*
+ * Ignore errors if stat'ing a bus or listing all
+ */
+ ret = do_stat_fc_dev(node, nodepath, lap, limited_stat);
+ if (ret != FPCFGA_OK) {
+ if (lap->cmd == FPCFGA_STAT_FC_DEV) {
+ lap->ret = ret;
+ rv = DI_WALK_TERMINATE;
+ } else {
+ rv = DI_WALK_CONTINUE;
+ }
+ goto out;
+ }
+
+ /* Are we done ? */
+ rv = DI_WALK_CONTINUE;
+ if (lap->cmd == FPCFGA_STAT_FCA_PORT &&
+ lap->chld_config == CFGA_STAT_CONFIGURED) {
+ rv = DI_WALK_TERMINATE;
+ } else if (lap->cmd == FPCFGA_STAT_FC_DEV) {
+ /*
+ * If stat'ing a specific device, we are done at this point.
+ */
+ rv = DI_WALK_TERMINATE;
+ }
+
+ /*FALLTHRU*/
+out:
+ S_FREE(nodepath);
+ if (devfsp != NULL) di_devfs_path_free(devfsp);
+ return (rv);
+}
+
+/*
+ * Routine for checking each FCP SCSI LUN device found in device tree.
+ * When the matching port WWN and LUN are found from the accessble ldata list
+ * the FCP SCSI LUN is updated with configured ostate.
+ *
+ * Overall algorithm:
+ * Parse the device tree to find configured devices which matches with
+ * list argument. If cmd is stat on a specific target device it
+ * matches port WWN and continues to further processing. If cmd is
+ * stat on hba port all the FCP SCSI LUN under the hba are processed.
+ */
+static int
+stat_FCP_dev(di_node_t node, void *arg)
+{
+ fpcfga_list_t *lap = NULL;
+ char *devfsp = NULL, *nodepath = NULL;
+ size_t len = 0;
+ int limited_stat = 0, match_minor, rv, di_ret;
+ fpcfga_ret_t ret;
+ uchar_t *port_wwn_data;
+ char port_wwn[WWN_SIZE*2+1];
+
+ lap = (fpcfga_list_t *)arg;
+
+ devfsp = di_devfs_path(node);
+ if (devfsp == NULL) {
+ rv = DI_WALK_CONTINUE;
+ goto out;
+ }
+
+ len = strlen(DEVICES_DIR) + strlen(devfsp) + 1;
+
+ nodepath = calloc(1, len);
+ if (nodepath == NULL) {
+ lap->l_errno = errno;
+ lap->ret = FPCFGA_LIB_ERR;
+ rv = DI_WALK_TERMINATE;
+ goto out;
+ }
+
+ (void) snprintf(nodepath, len, "%s%s", DEVICES_DIR, devfsp);
+
+ /* Skip node if it is HBA */
+ match_minor = 0;
+ if (!dev_cmp(lap->apidp->xport_phys, nodepath, match_minor)) {
+ rv = DI_WALK_CONTINUE;
+ goto out;
+ }
+
+ /* If stat'ing a specific device, is this node that device */
+ if (lap->cmd == FPCFGA_STAT_FC_DEV) {
+ /* checks port wwn property to find a match */
+ di_ret = di_prop_lookup_bytes(DDI_DEV_T_ANY, node,
+ PORT_WWN_PROP, &port_wwn_data);
+ if (di_ret == -1) {
+ rv = DI_WALK_CONTINUE;
+ goto out;
+ } else {
+ (void) sprintf(port_wwn, "%016llx",
+ (wwnConversion(port_wwn_data)));
+ /*
+ * port wwn doesn't match contine to walk
+ * if match call do_stat_FCP_dev.
+ */
+ if (strncmp(port_wwn, lap->apidp->dyncomp,
+ WWN_SIZE*2)) {
+ rv = DI_WALK_CONTINUE;
+ goto out;
+ }
+ }
+ }
+
+ /*
+ * If stat'ing a xport only, we look at device nodes only to get
+ * xport configuration status. So a limited stat will suffice.
+ */
+ if (lap->cmd == FPCFGA_STAT_FCA_PORT) {
+ limited_stat = 1;
+ } else {
+ limited_stat = 0;
+ }
+
+ /*
+ * Ignore errors if stat'ing a bus or listing all
+ */
+ ret = do_stat_FCP_dev(node, nodepath, lap, limited_stat);
+ if (ret != FPCFGA_OK) {
+ rv = DI_WALK_CONTINUE;
+ goto out;
+ }
+
+ /* Are we done ? */
+ rv = DI_WALK_CONTINUE;
+ if (lap->cmd == FPCFGA_STAT_FCA_PORT &&
+ lap->chld_config == CFGA_STAT_CONFIGURED) {
+ rv = DI_WALK_TERMINATE;
+ }
+
+ /*FALLTHRU*/
+out:
+ S_FREE(nodepath);
+ if (devfsp != NULL) di_devfs_path_free(devfsp);
+ return (rv);
+}
+
+static fpcfga_ret_t
+do_stat_fca_xport(fpcfga_list_t *lap, int limited_stat,
+ HBA_PORTATTRIBUTES portAttrs)
+{
+ cfga_list_data_t *clp = NULL;
+ ldata_list_t *listp = NULL;
+ int l_errno = 0;
+ uint_t devinfo_state = 0;
+ walkarg_t walkarg;
+ fpcfga_ret_t ret;
+ cfga_cond_t cond = CFGA_COND_UNKNOWN;
+
+ assert(lap->xport_logp != NULL);
+
+ /* Get xport state */
+ if (lap->apidp->flags == FLAG_DEVINFO_FORCE) {
+ walkarg.flags = FLAG_DEVINFO_FORCE;
+ } else {
+ walkarg.flags = 0;
+ }
+ walkarg.walkmode.node_args.flags = 0;
+ walkarg.walkmode.node_args.fcn = get_xport_state;
+
+ ret = walk_tree(lap->apidp->xport_phys, &devinfo_state,
+ DINFOCPYALL | DINFOPATH, &walkarg, FPCFGA_WALK_NODE, &l_errno);
+ if (ret == FPCFGA_OK) {
+ lap->xport_rstate = xport_devinfo_to_recep_state(devinfo_state);
+ } else {
+ lap->xport_rstate = CFGA_STAT_NONE;
+ }
+
+ /*
+ * Get topology works okay even if the fp port is connected
+ * to a switch and no devices connected to the switch.
+ * In this case the list will only shows fp port info without
+ * any device listed.
+ */
+ switch (portAttrs.PortType) {
+ case HBA_PORTTYPE_NLPORT:
+ (void) snprintf(lap->xport_type,
+ sizeof (lap->xport_type), "%s",
+ FP_FC_PUBLIC_PORT_TYPE);
+ break;
+ case HBA_PORTTYPE_NPORT:
+ (void) snprintf(lap->xport_type,
+ sizeof (lap->xport_type), "%s",
+ FP_FC_FABRIC_PORT_TYPE);
+ break;
+ case HBA_PORTTYPE_LPORT:
+ (void) snprintf(lap->xport_type,
+ sizeof (lap->xport_type), "%s",
+ FP_FC_PRIVATE_PORT_TYPE);
+ break;
+ case HBA_PORTTYPE_PTP:
+ (void) snprintf(lap->xport_type,
+ sizeof (lap->xport_type), "%s",
+ FP_FC_PT_TO_PT_PORT_TYPE);
+ break;
+ /*
+ * HBA_PORTTYPE_UNKNOWN means nothing is connected
+ */
+ case HBA_PORTTYPE_UNKNOWN:
+ (void) snprintf(lap->xport_type,
+ sizeof (lap->xport_type), "%s",
+ FP_FC_PORT_TYPE);
+ break;
+ /* NOT_PRESENT, OTHER, FPORT, FLPORT */
+ default:
+ (void) snprintf(lap->xport_type,
+ sizeof (lap->xport_type), "%s",
+ FP_FC_PORT_TYPE);
+ cond = CFGA_COND_FAILED;
+ break;
+ }
+
+ if (limited_stat) {
+ /* We only want to know bus(receptacle) connect status */
+ return (FPCFGA_OK);
+ }
+
+ listp = calloc(1, sizeof (ldata_list_t));
+ if (listp == NULL) {
+ lap->l_errno = errno;
+ return (FPCFGA_LIB_ERR);
+ }
+
+ clp = &listp->ldata;
+
+ (void) snprintf(clp->ap_log_id, sizeof (clp->ap_log_id), "%s",
+ lap->xport_logp);
+ (void) snprintf(clp->ap_phys_id, sizeof (clp->ap_phys_id), "%s",
+ lap->apidp->xport_phys);
+
+ clp->ap_class[0] = '\0'; /* Filled by libcfgadm */
+ clp->ap_r_state = lap->xport_rstate;
+ clp->ap_o_state = lap->chld_config;
+ clp->ap_cond = cond;
+ clp->ap_busy = 0;
+ clp->ap_status_time = (time_t)-1;
+ clp->ap_info[0] = '\0';
+ (void) strncpy(clp->ap_type, lap->xport_type, sizeof (clp->ap_type));
+
+ /* Link it in. lap->listp is NULL originally. */
+ listp->next = lap->listp;
+ /* lap->listp now gets cfga_list_data for the fca port. */
+ lap->listp = listp;
+
+ return (FPCFGA_OK);
+}
+
+
+static int
+get_xport_state(di_node_t node, void *arg)
+{
+ uint_t *di_statep = (uint_t *)arg;
+
+ *di_statep = di_state(node);
+
+ return (DI_WALK_TERMINATE);
+}
+
+/*
+ * Routine for updating ldata list based on the state of device node.
+ * When no matching accessible ldata is found a new ldata is created
+ * with proper state information.
+ *
+ * Overall algorithm:
+ * If the device node is online and the matching ldata is found
+ * the target device is updated with configued and unknown condition.
+ * If the device node is offline or down and the matching ldata is found
+ * the target device is updated with configued and unusable condition.
+ * If the device node is online but the matching ldata is not found
+ * the target device is created with configued and failing condition.
+ * If the device node is offline or down and the matching ldata is not found
+ * the target device is created with configued and unusable condition.
+ */
+static fpcfga_ret_t
+do_stat_fc_dev(
+ const di_node_t node,
+ const char *nodepath,
+ fpcfga_list_t *lap,
+ int limited_stat)
+{
+ uint_t dctl_state = 0, devinfo_state = 0;
+ char *dyncomp = NULL;
+ cfga_list_data_t *clp = NULL;
+ cfga_busy_t busy;
+ ldata_list_t *listp = NULL;
+ ldata_list_t *matchldp = NULL;
+ int l_errno = 0;
+ cfga_stat_t ostate;
+ cfga_cond_t cond;
+ fpcfga_ret_t ret;
+
+ assert(lap->apidp->xport_phys != NULL);
+ assert(lap->xport_logp != NULL);
+
+ cond = CFGA_COND_UNKNOWN;
+
+ devinfo_state = di_state(node);
+ ostate = dev_devinfo_to_occupant_state(devinfo_state);
+
+ /*
+ * NOTE: The framework cannot currently detect layered driver
+ * opens, so the busy indicator is not very reliable. Also,
+ * non-root users will not be able to determine busy
+ * status (libdevice needs root permissions).
+ * This should probably be fixed by adding a DI_BUSY to the di_state()
+ * routine in libdevinfo.
+ */
+ if (devctl_cmd(nodepath, FPCFGA_DEV_GETSTATE, &dctl_state,
+ &l_errno) == FPCFGA_OK) {
+ busy = ((dctl_state & DEVICE_BUSY) == DEVICE_BUSY) ? 1 : 0;
+ } else {
+ busy = 0;
+ }
+
+ /* We only want to know device config state */
+ if (limited_stat) {
+ if (((strcmp(lap->xport_type, FP_FC_FABRIC_PORT_TYPE) == 0) ||
+ strcmp(lap->xport_type, FP_FC_PUBLIC_PORT_TYPE) == 0)) {
+ lap->chld_config = CFGA_STAT_CONFIGURED;
+ } else {
+ if (ostate != CFGA_STAT_UNCONFIGURED) {
+ lap->chld_config = CFGA_STAT_CONFIGURED;
+ }
+ }
+ return (FPCFGA_OK);
+ }
+
+ /*
+ * If child device is configured, see if it is accessible also
+ * for FPCFGA_STAT_FC_DEV cmd.
+ */
+ if (lap->cmd == FPCFGA_STAT_FC_DEV) {
+ switch (ostate) {
+ case CFGA_STAT_CONFIGURED:
+ /*
+ * if configured and not accessble, the device is
+ * till be displayed with failing condition.
+ * return code should be FPCFGA_OK to display it.
+ */
+ case CFGA_STAT_NONE:
+ /*
+ * If not unconfigured and not attached
+ * the state is set to CFGA_STAT_NONE currently.
+ * This is okay for the detached node due to
+ * the driver being unloaded.
+ * May need to define another state to
+ * isolate the detached only state.
+ *
+ * handle the same way as configured.
+ */
+ if (lap->ret != FPCFGA_ACCESS_OK) {
+ cond = CFGA_COND_FAILING;
+ }
+ lap->chld_config = CFGA_STAT_CONFIGURED;
+ break;
+ case CFGA_STAT_UNCONFIGURED:
+ /*
+ * if unconfigured - offline or down,
+ * set to cond to unusable regardless of accessibility.
+ * This behavior needs to be examined further.
+ * When the device is not accessible the node
+ * may get offline or down. In that case failing
+ * cond may make more sense.
+ * In anycase the ostate will be set to configured
+ * configured.
+ */
+ cond = CFGA_COND_UNUSABLE;
+ /*
+ * For fabric port the fca port is considered as
+ * configured since user configured previously
+ * for any existing node. Otherwise when the
+ * device was accessible, the hba is considered as
+ * configured.
+ */
+ if (((strcmp(lap->xport_type,
+ FP_FC_PUBLIC_PORT_TYPE) == 0) ||
+ (strcmp(lap->xport_type,
+ FP_FC_FABRIC_PORT_TYPE) == 0)) ||
+ (lap->ret == FPCFGA_ACCESS_OK)) {
+ lap->chld_config = CFGA_STAT_CONFIGURED;
+ } else {
+ lap->ret = FPCFGA_APID_NOEXIST;
+ return (FPCFGA_OK);
+ }
+ break;
+ default:
+ break;
+ }
+
+ /* if device found in disco ports, ldata already created. */
+ if (lap->ret == FPCFGA_ACCESS_OK) {
+ /*
+ * if cond is not changed then don't update
+ * condition to keep the previous condition.
+ */
+ if (cond != CFGA_COND_UNKNOWN) {
+ lap->listp->ldata.ap_cond = cond;
+ }
+ lap->listp->ldata.ap_o_state = CFGA_STAT_CONFIGURED;
+ lap->listp->ldata.ap_busy = busy;
+ lap->ret = FPCFGA_OK;
+ return (FPCFGA_OK);
+ }
+ }
+
+ /*
+ * if cmd is stat all check ldata list
+ * to see if the node exist on the dev list. Otherwise create
+ * the list element.
+ */
+ if (lap->cmd == FPCFGA_STAT_ALL) {
+ if (lap->listp != NULL) {
+ if ((ret = make_dyncomp_from_dinode(node,
+ &dyncomp, &l_errno)) != FPCFGA_OK) {
+ return (ret);
+ }
+ ret = is_dyn_ap_on_ldata_list(dyncomp, lap->listp,
+ &matchldp, &l_errno);
+ switch (ret) {
+ case FPCFGA_ACCESS_OK:
+ /* node exists so set ostate to configured. */
+ lap->chld_config = CFGA_STAT_CONFIGURED;
+ matchldp->ldata.ap_o_state =
+ CFGA_STAT_CONFIGURED;
+ matchldp->ldata.ap_busy = busy;
+ clp = &matchldp->ldata;
+ switch (ostate) {
+ case CFGA_STAT_CONFIGURED:
+ /*
+ * If not unconfigured and not attached
+ * the state is set to CFGA_STAT_NONE currently.
+ * This is okay for the detached node due to
+ * the driver being unloaded.
+ * May need to define another state to
+ * isolate the detached only state.
+ */
+ case CFGA_STAT_NONE:
+ /* update ap_type and ap_info */
+ get_hw_info(node, clp);
+ break;
+ /*
+ * node is offline or down.
+ * set cond to unusable.
+ */
+ case CFGA_STAT_UNCONFIGURED:
+ /*
+ * if cond is not unknown
+ * we already set the cond from
+ * a different node with the same
+ * port WWN or initial probing
+ * was failed so don't update again.
+ */
+ if (matchldp->ldata.ap_cond ==
+ CFGA_COND_UNKNOWN) {
+ matchldp->ldata.ap_cond =
+ CFGA_COND_UNUSABLE;
+ }
+ break;
+ default:
+ break;
+ }
+ /* node found in ldata list so just return. */
+ lap->ret = FPCFGA_OK;
+ S_FREE(dyncomp);
+ return (FPCFGA_OK);
+ case FPCFGA_LIB_ERR:
+ lap->l_errno = l_errno;
+ S_FREE(dyncomp);
+ return (ret);
+ case FPCFGA_APID_NOACCESS:
+ switch (ostate) {
+ /* node is attached but not in dev list */
+ case CFGA_STAT_CONFIGURED:
+ case CFGA_STAT_NONE:
+ lap->chld_config = CFGA_STAT_CONFIGURED;
+ cond = CFGA_COND_FAILING;
+ break;
+ /*
+ * node is offline or down.
+ * set cond to unusable.
+ */
+ case CFGA_STAT_UNCONFIGURED:
+ /*
+ * For fabric port the fca port is
+ * considered as configured since user
+ * configured previously for any
+ * existing node.
+ */
+ cond = CFGA_COND_UNUSABLE;
+ if ((strcmp(lap->xport_type,
+ FP_FC_PUBLIC_PORT_TYPE) == 0) ||
+ (strcmp(lap->xport_type,
+ FP_FC_FABRIC_PORT_TYPE) == 0)) {
+ lap->chld_config =
+ CFGA_STAT_CONFIGURED;
+ } else {
+ lap->ret = FPCFGA_OK;
+ S_FREE(dyncomp);
+ return (FPCFGA_OK);
+ }
+ break;
+ default:
+ /*
+ * continue to create ldata_list struct for
+ * this node
+ */
+ break;
+ }
+ default:
+ break;
+ }
+ } else {
+ /*
+ * dev_list is null so there is no accessible dev.
+ * set the cond and continue to create ldata.
+ */
+ switch (ostate) {
+ case CFGA_STAT_CONFIGURED:
+ case CFGA_STAT_NONE:
+ cond = CFGA_COND_FAILING;
+ lap->chld_config = CFGA_STAT_CONFIGURED;
+ break;
+ /*
+ * node is offline or down.
+ * set cond to unusable.
+ */
+ case CFGA_STAT_UNCONFIGURED:
+ cond = CFGA_COND_UNUSABLE;
+ /*
+ * For fabric port the fca port is
+ * considered as configured since user
+ * configured previously for any
+ * existing node.
+ */
+ if ((strcmp(lap->xport_type,
+ FP_FC_PUBLIC_PORT_TYPE) == 0) ||
+ (strcmp(lap->xport_type,
+ FP_FC_FABRIC_PORT_TYPE) == 0)) {
+ lap->chld_config =
+ CFGA_STAT_CONFIGURED;
+ } else {
+ lap->ret = FPCFGA_OK;
+ S_FREE(dyncomp);
+ return (FPCFGA_OK);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ listp = calloc(1, sizeof (ldata_list_t));
+ if (listp == NULL) {
+ lap->l_errno = errno;
+ S_FREE(dyncomp);
+ return (FPCFGA_LIB_ERR);
+ }
+
+ clp = &listp->ldata;
+
+ /* Create the dynamic component. */
+ if (dyncomp == NULL) {
+ ret = make_dyncomp_from_dinode(node, &dyncomp, &l_errno);
+ if (ret != FPCFGA_OK) {
+ S_FREE(listp);
+ return (ret);
+ }
+ }
+
+ /* Create logical and physical ap_id */
+ (void) snprintf(clp->ap_log_id, sizeof (clp->ap_log_id), "%s%s%s",
+ lap->xport_logp, DYN_SEP, dyncomp);
+
+ (void) snprintf(clp->ap_phys_id, sizeof (clp->ap_phys_id), "%s%s%s",
+ lap->apidp->xport_phys, DYN_SEP, dyncomp);
+
+ S_FREE(dyncomp);
+
+ clp->ap_class[0] = '\0'; /* Filled in by libcfgadm */
+ clp->ap_r_state = lap->xport_rstate;
+ /* set to ostate to configured and set cond with info. */
+ clp->ap_o_state = CFGA_STAT_CONFIGURED;
+ clp->ap_cond = cond;
+ clp->ap_busy = busy;
+ clp->ap_status_time = (time_t)-1;
+
+ /* get ap_type and ap_info. */
+ get_hw_info(node, clp);
+
+ /* Link it in */
+ listp->next = lap->listp;
+ lap->listp = listp;
+
+ lap->ret = FPCFGA_OK;
+ return (FPCFGA_OK);
+}
+
+/*
+ * Wrapper routine for handling path info.
+ *
+ * When show_FCP_dev option is given stat_path_info_FCP_dev() is called.
+ * Otherwise stat_path_info_fc_dev() is called.
+ */
+int
+stat_path_info_node(
+ di_node_t root,
+ void *arg,
+ int *l_errnop)
+{
+ fpcfga_list_t *lap = NULL;
+
+ lap = (fpcfga_list_t *)arg;
+ if ((lap->apidp->flags & (FLAG_FCP_DEV)) == FLAG_FCP_DEV) {
+ return (stat_path_info_FCP_dev(root, lap, l_errnop));
+ } else {
+ return (stat_path_info_fc_dev(root, lap, l_errnop));
+ }
+}
+
+/*
+ * Routine for updating ldata list based on the state of path info node.
+ * When no matching accessible ldata is found a new ldata is created
+ * with proper state information.
+ *
+ * Overall algorithm:
+ * If the path info node is not offline and the matching ldata is found
+ * the target device is updated with configued and unknown condition.
+ * If the path info node is offline or failed and the matching ldata is found
+ * the target device is updated with configued and unusable condition.
+ * If the path info node is online but the matching ldata is not found
+ * the target device is created with configued and failing condition.
+ * If the path info is offline or failed and the matching ldata is not found
+ * the target device is created with configued and unusable condition.
+ */
+static int
+stat_path_info_fc_dev(
+ di_node_t root,
+ fpcfga_list_t *lap,
+ int *l_errnop)
+{
+ ldata_list_t *matchldp = NULL;
+ di_path_t path = DI_PATH_NIL;
+ uchar_t *port_wwn_data;
+ char port_wwn[WWN_SIZE*2+1];
+ int count;
+ fpcfga_ret_t ret;
+ di_path_state_t pstate;
+
+ if (root == DI_NODE_NIL) {
+ return (FPCFGA_LIB_ERR);
+ }
+
+ /*
+ * if stat on a specific dev and walk_node found it okay
+ * then just return ok.
+ */
+ if ((lap->cmd == FPCFGA_STAT_FC_DEV) && (lap->ret == FPCFGA_OK)) {
+ return (FPCFGA_OK);
+ }
+
+ /*
+ * if stat on a fca xport and chld_config is set
+ * then just return ok.
+ */
+ if ((lap->cmd == FPCFGA_STAT_FCA_PORT) &&
+ (lap->chld_config == CFGA_STAT_CONFIGURED)) {
+ return (FPCFGA_OK);
+ }
+
+ /*
+ * when there is no path_info node return FPCFGA_OK.
+ * That way the result from walk_node shall be maintained.
+ */
+ if ((path = di_path_next_client(root, path)) == DI_PATH_NIL) {
+ /*
+ * if the dev was in dev list but not found
+ * return OK to indicate is not configured.
+ */
+ if (lap->ret == FPCFGA_ACCESS_OK) {
+ lap->ret = FPCFGA_OK;
+ }
+ return (FPCFGA_OK);
+ }
+
+ /* if stat on fca port return. */
+ if (lap->cmd == FPCFGA_STAT_FCA_PORT) {
+ if (((strcmp(lap->xport_type, FP_FC_FABRIC_PORT_TYPE) == 0) ||
+ strcmp(lap->xport_type, FP_FC_PUBLIC_PORT_TYPE) == 0)) {
+ lap->chld_config = CFGA_STAT_CONFIGURED;
+ return (FPCFGA_OK);
+ } else {
+ if ((pstate = di_path_state(path)) !=
+ DI_PATH_STATE_OFFLINE) {
+ lap->chld_config = CFGA_STAT_CONFIGURED;
+ return (FPCFGA_OK);
+ }
+ }
+ }
+ /*
+ * now parse the path info node.
+ */
+ do {
+ count = di_path_prop_lookup_bytes(path, PORT_WWN_PROP,
+ &port_wwn_data);
+ if (count != WWN_SIZE) {
+ ret = FPCFGA_LIB_ERR;
+ break;
+ }
+
+ (void) sprintf(port_wwn, "%016llx",
+ (wwnConversion(port_wwn_data)));
+ switch (lap->cmd) {
+ case FPCFGA_STAT_FC_DEV:
+ /* if no match contine to the next path info node. */
+ if (strncmp(port_wwn, lap->apidp->dyncomp,
+ WWN_SIZE*2)) {
+ break;
+ }
+ /* if device in dev_list, ldata already created. */
+ if (lap->ret == FPCFGA_ACCESS_OK) {
+ lap->listp->ldata.ap_o_state =
+ CFGA_STAT_CONFIGURED;
+ if (((pstate = di_path_state(path)) ==
+ DI_PATH_STATE_OFFLINE) ||
+ (pstate == DI_PATH_STATE_FAULT)) {
+ lap->listp->ldata.ap_cond =
+ CFGA_COND_UNUSABLE;
+ }
+ lap->ret = FPCFGA_OK;
+ return (FPCFGA_OK);
+ } else {
+ if ((strcmp(lap->xport_type,
+ FP_FC_PUBLIC_PORT_TYPE) == 0) ||
+ (strcmp(lap->xport_type,
+ FP_FC_FABRIC_PORT_TYPE) == 0)) {
+ lap->chld_config = CFGA_STAT_CONFIGURED;
+ return (init_ldata_for_mpath_dev(
+ path, port_wwn, l_errnop, lap));
+ } else {
+ if ((di_path_state(path)) !=
+ DI_PATH_STATE_OFFLINE) {
+ return (init_ldata_for_mpath_dev(
+ path, port_wwn, l_errnop, lap));
+ } else {
+ lap->ret = FPCFGA_APID_NOEXIST;
+ return (FPCFGA_OK);
+ }
+ }
+ }
+ case FPCFGA_STAT_ALL:
+ /* check if there is list data. */
+ if (lap->listp != NULL) {
+ ret = is_dyn_ap_on_ldata_list(port_wwn,
+ lap->listp, &matchldp, l_errnop);
+ if (ret == FPCFGA_ACCESS_OK) {
+ lap->chld_config = CFGA_STAT_CONFIGURED;
+ matchldp->ldata.ap_o_state =
+ CFGA_STAT_CONFIGURED;
+ /*
+ * Update the condition as unusable
+ * if the pathinfo state is failed
+ * or offline.
+ */
+ if (((pstate = di_path_state(path)) ==
+ DI_PATH_STATE_OFFLINE) ||
+ (pstate ==
+ DI_PATH_STATE_FAULT)) {
+ matchldp->ldata.ap_cond =
+ CFGA_COND_UNUSABLE;
+ }
+ break;
+ } else if (ret == FPCFGA_LIB_ERR) {
+ lap->l_errno = *l_errnop;
+ return (ret);
+ }
+ }
+ /*
+ * now create ldata for this particular path info node.
+ * if port top is private loop and pathinfo is in
+ * in offline state don't include to ldata list.
+ */
+ if (((strcmp(lap->xport_type,
+ FP_FC_PUBLIC_PORT_TYPE) == 0) ||
+ (strcmp(lap->xport_type,
+ FP_FC_FABRIC_PORT_TYPE) == 0)) ||
+ (di_path_state(path) !=
+ DI_PATH_STATE_OFFLINE)) {
+ lap->chld_config = CFGA_STAT_CONFIGURED;
+ ret = init_ldata_for_mpath_dev(
+ path, port_wwn, l_errnop, lap);
+ if (ret != FPCFGA_OK) {
+ return (ret);
+ }
+ }
+ break;
+ case FPCFGA_STAT_FCA_PORT:
+ if (di_path_state(path) != DI_PATH_STATE_OFFLINE) {
+ lap->chld_config = CFGA_STAT_CONFIGURED;
+ return (FPCFGA_OK);
+ }
+ }
+ path = di_path_next_client(root, path);
+ } while (path != DI_PATH_NIL);
+
+ return (FPCFGA_OK);
+
+}
+
+/*
+ * Routine for updating ldata list based on the state of path info node.
+ * When no matching accessible ldata is found a new ldata is created
+ * with proper state information.
+ *
+ * The difference from stat_path_info_fc_dev() is
+ * to handle FCP SCSI LUN information. Otherwise overall algorithm is
+ * same.
+ *
+ * Overall algorithm:
+ * If the path info node is not offline and the matching ldata is found
+ * the target device is updated with configued and unknown condition.
+ * If the path info node is offline or failed and the matching ldata is found
+ * the target device is updated with configued and unusable condition.
+ * If the path info node is online but the matching ldata is not found
+ * the target device is created with configued and failing condition.
+ * If the path info is offline or failed and the matching ldata is not found
+ * the target device is created with configued and unusable condition.
+ */
+static int
+stat_path_info_FCP_dev(
+ di_node_t root,
+ fpcfga_list_t *lap,
+ int *l_errnop)
+{
+ ldata_list_t *matchldp = NULL, *listp = NULL;
+ cfga_list_data_t *clp;
+ di_path_t path = DI_PATH_NIL;
+ di_node_t client_node = DI_NODE_NIL;
+ char *port_wwn = NULL, *nodepath = NULL;
+ int *lun_nump;
+ fpcfga_ret_t ldata_ret;
+ di_path_state_t pstate;
+ cfga_busy_t busy;
+ uint_t dctl_state = 0;
+
+ if (root == DI_NODE_NIL) {
+ return (FPCFGA_LIB_ERR);
+ }
+
+ /*
+ * if stat on a fca xport and chld_config is set
+ * then just return ok.
+ */
+ if ((lap->cmd == FPCFGA_STAT_FCA_PORT) &&
+ (lap->chld_config == CFGA_STAT_CONFIGURED)) {
+ return (FPCFGA_OK);
+ }
+ /*
+ * when there is no path_info node return FPCFGA_OK.
+ * That way the result from walk_node shall be maintained.
+ */
+ if ((path = di_path_next_client(root, path)) == DI_PATH_NIL) {
+ /*
+ * if the dev was in dev list but not found
+ * return ok.
+ */
+ if (lap->ret == FPCFGA_ACCESS_OK) {
+ lap->ret = FPCFGA_OK;
+ }
+ return (FPCFGA_OK);
+ }
+ /*
+ * If stat on fca port and port topology is fabric return here.
+ * If not fabric return only when path state is not offfline.
+ * The other cases are handbled below.
+ */
+ if (lap->cmd == FPCFGA_STAT_FCA_PORT) {
+ if (((strcmp(lap->xport_type, FP_FC_FABRIC_PORT_TYPE) == 0) ||
+ strcmp(lap->xport_type, FP_FC_PUBLIC_PORT_TYPE) == 0)) {
+ lap->chld_config = CFGA_STAT_CONFIGURED;
+ return (FPCFGA_OK);
+ } else {
+ if ((pstate = di_path_state(path)) !=
+ DI_PATH_STATE_OFFLINE) {
+ lap->chld_config = CFGA_STAT_CONFIGURED;
+ return (FPCFGA_OK);
+ }
+ }
+ }
+ /*
+ * now parse the path info node.
+ */
+ do {
+ switch (lap->cmd) {
+ case FPCFGA_STAT_FC_DEV:
+ if ((make_portwwn_luncomp_from_pinode(path, &port_wwn,
+ &lun_nump, l_errnop)) != FPCFGA_OK) {
+ return (FPCFGA_LIB_ERR);
+ }
+
+ if ((ldata_ret = is_FCP_dev_ap_on_ldata_list(port_wwn,
+ *lun_nump, lap->listp, &matchldp))
+ == FPCFGA_LIB_ERR) {
+ S_FREE(port_wwn);
+ return (ldata_ret);
+ }
+
+ if (ldata_ret == FPCFGA_ACCESS_OK) {
+ lap->chld_config = CFGA_STAT_CONFIGURED;
+ matchldp->ldata.ap_o_state =
+ CFGA_STAT_CONFIGURED;
+ /*
+ * Update the condition as unusable
+ * if the pathinfo state is failed
+ * or offline.
+ */
+ if (((pstate = di_path_state(path)) ==
+ DI_PATH_STATE_OFFLINE) ||
+ (pstate == DI_PATH_STATE_FAULT)) {
+ matchldp->ldata.ap_cond =
+ CFGA_COND_UNUSABLE;
+ }
+ lap->ret = FPCFGA_OK;
+ break;
+ }
+
+ if (strncmp(port_wwn, lap->apidp->dyncomp, WWN_SIZE*2)
+ != 0) {
+ break;
+ }
+ /*
+ * now create ldata for this particular path info node.
+ * if port top is private loop and pathinfo is in
+ * in offline state don't include to ldata list.
+ */
+ if (((strcmp(lap->xport_type,
+ FP_FC_PUBLIC_PORT_TYPE) == 0) ||
+ (strcmp(lap->xport_type,
+ FP_FC_FABRIC_PORT_TYPE) == 0)) ||
+ (di_path_state(path) !=
+ DI_PATH_STATE_OFFLINE)) {
+ lap->chld_config = CFGA_STAT_CONFIGURED;
+ /* create ldata for this pi node. */
+ client_node = di_path_client_node(path);
+ if (client_node == DI_NODE_NIL) {
+ *l_errnop = errno;
+ S_FREE(port_wwn);
+ return (FPCFGA_LIB_ERR);
+ }
+ if ((construct_nodepath_from_dinode(
+ client_node, &nodepath, l_errnop))
+ != FPCFGA_OK) {
+ S_FREE(port_wwn);
+ return (FPCFGA_LIB_ERR);
+ }
+
+ listp = calloc(1, sizeof (ldata_list_t));
+ if (listp == NULL) {
+ S_FREE(port_wwn);
+ S_FREE(nodepath);
+ lap->l_errno = errno;
+ return (FPCFGA_LIB_ERR);
+ }
+
+ clp = &listp->ldata;
+
+ /* Create logical and physical ap_id */
+ (void) snprintf(clp->ap_log_id,
+ sizeof (clp->ap_log_id), "%s%s%s%s%d",
+ lap->xport_logp, DYN_SEP, port_wwn,
+ LUN_COMP_SEP, *lun_nump);
+ (void) snprintf(clp->ap_phys_id,
+ sizeof (clp->ap_phys_id), "%s%s%s%s%d",
+ lap->apidp->xport_phys, DYN_SEP, port_wwn,
+ LUN_COMP_SEP, *lun_nump);
+ /*
+ * We reached here since FCP dev is not found
+ * in ldata list but path info node exists.
+ *
+ * Update the condition as failing
+ * if the pathinfo state was normal.
+ * Update the condition as unusable
+ * if the pathinfo state is failed
+ * or offline.
+ */
+ clp->ap_class[0] = '\0'; /* Filled by libcfgadm */
+ clp->ap_o_state = CFGA_STAT_CONFIGURED;
+ if (((pstate = di_path_state(path))
+ == DI_PATH_STATE_OFFLINE) ||
+ (pstate == DI_PATH_STATE_FAULT)) {
+ clp->ap_cond = CFGA_COND_UNUSABLE;
+ } else {
+ clp->ap_cond = CFGA_COND_FAILING;
+ }
+ clp->ap_r_state = lap->xport_rstate;
+ clp->ap_info[0] = '\0';
+ /* update ap_type and ap_info */
+ get_hw_info(client_node, clp);
+ if (devctl_cmd(nodepath, FPCFGA_DEV_GETSTATE,
+ &dctl_state, l_errnop) == FPCFGA_OK) {
+ busy = ((dctl_state & DEVICE_BUSY)
+ == DEVICE_BUSY) ? 1 : 0;
+ } else {
+ busy = 0;
+ }
+ clp->ap_busy = busy;
+ clp->ap_status_time = (time_t)-1;
+
+ (void) insert_ldata_to_ldatalist(port_wwn,
+ lun_nump, listp, &(lap->listp));
+ }
+ break;
+ case FPCFGA_STAT_ALL:
+ if ((make_portwwn_luncomp_from_pinode(path, &port_wwn,
+ &lun_nump, l_errnop)) != FPCFGA_OK) {
+ return (FPCFGA_LIB_ERR);
+ }
+
+ if ((ldata_ret = is_FCP_dev_ap_on_ldata_list(port_wwn,
+ *lun_nump, lap->listp, &matchldp))
+ == FPCFGA_LIB_ERR) {
+ S_FREE(port_wwn);
+ return (ldata_ret);
+ }
+
+ if (ldata_ret == FPCFGA_ACCESS_OK) {
+ lap->chld_config = CFGA_STAT_CONFIGURED;
+ matchldp->ldata.ap_o_state =
+ CFGA_STAT_CONFIGURED;
+ /*
+ * Update the condition as unusable
+ * if the pathinfo state is failed
+ * or offline.
+ */
+ if (((pstate = di_path_state(path)) ==
+ DI_PATH_STATE_OFFLINE) ||
+ (pstate == DI_PATH_STATE_FAULT)) {
+ matchldp->ldata.ap_cond =
+ CFGA_COND_UNUSABLE;
+ }
+ break;
+ }
+ /*
+ * now create ldata for this particular path info node.
+ * if port top is private loop and pathinfo is in
+ * in offline state don't include to ldata list.
+ */
+ if (((strcmp(lap->xport_type,
+ FP_FC_PUBLIC_PORT_TYPE) == 0) ||
+ (strcmp(lap->xport_type,
+ FP_FC_FABRIC_PORT_TYPE) == 0)) ||
+ (di_path_state(path) !=
+ DI_PATH_STATE_OFFLINE)) {
+ lap->chld_config = CFGA_STAT_CONFIGURED;
+ /* create ldata for this pi node. */
+ client_node = di_path_client_node(path);
+ if (client_node == DI_NODE_NIL) {
+ *l_errnop = errno;
+ S_FREE(port_wwn);
+ return (FPCFGA_LIB_ERR);
+ }
+ if ((construct_nodepath_from_dinode(
+ client_node, &nodepath, l_errnop))
+ != FPCFGA_OK) {
+ S_FREE(port_wwn);
+ return (FPCFGA_LIB_ERR);
+ }
+
+ listp = calloc(1, sizeof (ldata_list_t));
+ if (listp == NULL) {
+ S_FREE(port_wwn);
+ S_FREE(nodepath);
+ lap->l_errno = errno;
+ return (FPCFGA_LIB_ERR);
+ }
+
+ clp = &listp->ldata;
+
+ /* Create logical and physical ap_id */
+ (void) snprintf(clp->ap_log_id,
+ sizeof (clp->ap_log_id), "%s%s%s%s%d",
+ lap->xport_logp, DYN_SEP, port_wwn,
+ LUN_COMP_SEP, *lun_nump);
+ (void) snprintf(clp->ap_phys_id,
+ sizeof (clp->ap_phys_id), "%s%s%s%s%d",
+ lap->apidp->xport_phys, DYN_SEP, port_wwn,
+ LUN_COMP_SEP, *lun_nump);
+ /*
+ * We reached here since FCP dev is not found
+ * in ldata list but path info node exists.
+ *
+ * Update the condition as failing
+ * if the pathinfo state was normal.
+ * Update the condition as unusable
+ * if the pathinfo state is failed
+ * or offline.
+ */
+ clp->ap_class[0] = '\0'; /* Filled by libcfgadm */
+ clp->ap_o_state = CFGA_STAT_CONFIGURED;
+ if (((pstate = di_path_state(path))
+ == DI_PATH_STATE_OFFLINE) ||
+ (pstate == DI_PATH_STATE_FAULT)) {
+ clp->ap_cond = CFGA_COND_UNUSABLE;
+ } else {
+ clp->ap_cond = CFGA_COND_FAILING;
+ }
+ clp->ap_r_state = lap->xport_rstate;
+ clp->ap_info[0] = '\0';
+ /* update ap_type and ap_info */
+ get_hw_info(client_node, clp);
+ if (devctl_cmd(nodepath, FPCFGA_DEV_GETSTATE,
+ &dctl_state, l_errnop) == FPCFGA_OK) {
+ busy = ((dctl_state & DEVICE_BUSY)
+ == DEVICE_BUSY) ? 1 : 0;
+ } else {
+ busy = 0;
+ }
+ clp->ap_busy = busy;
+ clp->ap_status_time = (time_t)-1;
+
+ (void) insert_ldata_to_ldatalist(port_wwn,
+ lun_nump, listp, &(lap->listp));
+ }
+ break;
+ case FPCFGA_STAT_FCA_PORT:
+ if (di_path_state(path) != DI_PATH_STATE_OFFLINE) {
+ lap->chld_config = CFGA_STAT_CONFIGURED;
+ lap->ret = FPCFGA_OK;
+ return (FPCFGA_OK);
+ }
+ }
+ path = di_path_next_client(root, path);
+ } while (path != DI_PATH_NIL);
+
+ lap->ret = FPCFGA_OK;
+ S_FREE(port_wwn);
+ S_FREE(nodepath);
+ return (FPCFGA_OK);
+
+}
+
+/*
+ * Routine for updating ldata list based on the state of device node.
+ * When no matching accessible ldata is found a new ldata is created
+ * with proper state information.
+ *
+ * The difference from do_stat_fc_dev() is
+ * to handle FCP SCSI LUN information. Otherwise overall algorithm is
+ * same.
+ *
+ * Overall algorithm:
+ * If the device node is online and the matching ldata is found
+ * the target device is updated with configued and unknown condition.
+ * If the device node is offline or down and the matching ldata is found
+ * the target device is updated with configued and unusable condition.
+ * If the device node is online but the matching ldata is not found
+ * the target device is created with configued and failing condition.
+ * If the device node is offline or down and the matching ldata is not found
+ * the target device is created with configued and unusable condition.
+ */
+static fpcfga_ret_t
+do_stat_FCP_dev(
+ const di_node_t node,
+ const char *nodepath,
+ fpcfga_list_t *lap,
+ int limited_stat)
+{
+ uint_t dctl_state = 0, devinfo_state = 0;
+ char *port_wwn = NULL;
+ cfga_list_data_t *clp = NULL;
+ cfga_busy_t busy;
+ ldata_list_t *listp = NULL;
+ ldata_list_t *matchldp = NULL;
+ int l_errno = 0, *lun_nump;
+ cfga_stat_t ostate;
+ cfga_cond_t cond;
+ fpcfga_ret_t ldata_ret;
+
+ assert(lap->apidp->xport_phys != NULL);
+ assert(lap->xport_logp != NULL);
+
+ cond = CFGA_COND_UNKNOWN;
+
+ devinfo_state = di_state(node);
+ ostate = dev_devinfo_to_occupant_state(devinfo_state);
+
+ /*
+ * NOTE: The devctl framework cannot currently detect layered driver
+ * opens, so the busy indicator is not very reliable. Also,
+ * non-root users will not be able to determine busy
+ * status (libdevice needs root permissions).
+ * This should probably be fixed by adding a DI_BUSY to the di_state()
+ * routine in libdevinfo.
+ */
+ if (devctl_cmd(nodepath, FPCFGA_DEV_GETSTATE, &dctl_state,
+ &l_errno) == FPCFGA_OK) {
+ busy = ((dctl_state & DEVICE_BUSY) == DEVICE_BUSY) ? 1 : 0;
+ } else {
+ busy = 0;
+ }
+
+ /* We only want to know device config state */
+ if (limited_stat) {
+ if (((strcmp(lap->xport_type, FP_FC_FABRIC_PORT_TYPE) == 0) ||
+ strcmp(lap->xport_type, FP_FC_PUBLIC_PORT_TYPE) == 0)) {
+ lap->chld_config = CFGA_STAT_CONFIGURED;
+ } else {
+ if (ostate != CFGA_STAT_UNCONFIGURED) {
+ lap->chld_config = CFGA_STAT_CONFIGURED;
+ }
+ }
+ return (FPCFGA_OK);
+ }
+
+ /*
+ * If child device is configured, see if it is accessible also
+ * for FPCFGA_STAT_FC_DEV cmd.
+ */
+ if ((make_portwwn_luncomp_from_dinode(node, &port_wwn, &lun_nump,
+ &l_errno)) != FPCFGA_OK) {
+ lap->l_errno = l_errno;
+ return (FPCFGA_LIB_ERR);
+ }
+
+ if ((ldata_ret = is_FCP_dev_ap_on_ldata_list(port_wwn, *lun_nump,
+ lap->listp, &matchldp)) == FPCFGA_LIB_ERR) {
+ lap->l_errno = l_errno;
+ S_FREE(port_wwn);
+ return (ldata_ret);
+ }
+
+ if (lap->cmd == FPCFGA_STAT_FC_DEV) {
+ switch (ostate) {
+ case CFGA_STAT_CONFIGURED:
+ /*
+ * if configured and not accessble, the device is
+ * till be displayed with failing condition.
+ * return code should be FPCFGA_OK to display it.
+ */
+ case CFGA_STAT_NONE:
+ /*
+ * If not unconfigured and not attached
+ * the state is set to CFGA_STAT_NONE currently.
+ * This is okay for the detached node due to
+ * the driver being unloaded.
+ * May need to define another state to
+ * isolate the detached only state.
+ *
+ * handle the same way as configured.
+ */
+ if (ldata_ret != FPCFGA_ACCESS_OK) {
+ cond = CFGA_COND_FAILING;
+ }
+ lap->chld_config = CFGA_STAT_CONFIGURED;
+ break;
+ case CFGA_STAT_UNCONFIGURED:
+ /*
+ * if unconfigured - offline or down,
+ * set to cond to unusable regardless of accessibility.
+ * This behavior needs to be examined further.
+ * When the device is not accessible the node
+ * may get offline or down. In that case failing
+ * cond may make more sense.
+ * In anycase the ostate will be set to configured
+ * configured.
+ */
+ cond = CFGA_COND_UNUSABLE;
+ /*
+ * For fabric port the fca port is considered as
+ * configured since user configured previously
+ * for any existing node. Otherwise when the
+ * device was accessible, the hba is considered as
+ * configured.
+ */
+ if (((strcmp(lap->xport_type,
+ FP_FC_PUBLIC_PORT_TYPE) == 0) ||
+ (strcmp(lap->xport_type,
+ FP_FC_FABRIC_PORT_TYPE) == 0)) ||
+ (lap->ret == FPCFGA_ACCESS_OK)) {
+ lap->chld_config = CFGA_STAT_CONFIGURED;
+ } else {
+ /*
+ * if lap->ret is okay there is at least
+ * one matching ldata exist. Need to keep
+ * okay ret to display the matching ones.
+ */
+ if (lap->ret != FPCFGA_OK) {
+ lap->ret = FPCFGA_APID_NOEXIST;
+ }
+ S_FREE(port_wwn);
+ return (FPCFGA_OK);
+ }
+ break;
+ default:
+ break;
+ }
+
+ /* if device found in dev_list, ldata already created. */
+ if (ldata_ret == FPCFGA_ACCESS_OK) {
+ /*
+ * if cond is not changed then don't update
+ * condition to keep any condtion
+ * from initial discovery. If the initial
+ * cond was failed the same condition will be kept.
+ */
+ if (cond != CFGA_COND_UNKNOWN) {
+ matchldp->ldata.ap_cond = cond;
+ }
+ matchldp->ldata.ap_o_state = CFGA_STAT_CONFIGURED;
+ matchldp->ldata.ap_busy = busy;
+ /* update ap_info via inquiry */
+ clp = &matchldp->ldata;
+ /* update ap_type and ap_info */
+ get_hw_info(node, clp);
+ lap->ret = FPCFGA_OK;
+ S_FREE(port_wwn);
+ return (FPCFGA_OK);
+ }
+ }
+
+ /*
+ * if cmd is stat all check ldata list
+ * to see if the node exist on the dev list. Otherwise create
+ * the list element.
+ */
+ if (lap->cmd == FPCFGA_STAT_ALL) {
+ switch (ldata_ret) {
+ case FPCFGA_ACCESS_OK:
+ /* node exists so set ostate to configured. */
+ lap->chld_config = CFGA_STAT_CONFIGURED;
+ matchldp->ldata.ap_o_state =
+ CFGA_STAT_CONFIGURED;
+ matchldp->ldata.ap_busy = busy;
+ clp = &matchldp->ldata;
+ switch (ostate) {
+ case CFGA_STAT_CONFIGURED:
+ /*
+ * If not unconfigured and not attached
+ * the state is set to CFGA_STAT_NONE currently.
+ * This is okay for the detached node due to
+ * the driver being unloaded.
+ * May need to define another state to
+ * isolate the detached only state.
+ */
+ case CFGA_STAT_NONE:
+ /* update ap_type and ap_info */
+ get_hw_info(node, clp);
+ break;
+ /*
+ * node is offline or down.
+ * set cond to unusable.
+ */
+ case CFGA_STAT_UNCONFIGURED:
+ /*
+ * if cond is not unknown
+ * initial probing was failed
+ * so don't update again.
+ */
+ if (matchldp->ldata.ap_cond ==
+ CFGA_COND_UNKNOWN) {
+ matchldp->ldata.ap_cond =
+ CFGA_COND_UNUSABLE;
+ }
+ break;
+ default:
+ break;
+ }
+ /* node found in ldata list so just return. */
+ lap->ret = FPCFGA_OK;
+ S_FREE(port_wwn);
+ return (FPCFGA_OK);
+ case FPCFGA_APID_NOACCESS:
+ switch (ostate) {
+ /* node is attached but not in dev list */
+ case CFGA_STAT_CONFIGURED:
+ case CFGA_STAT_NONE:
+ lap->chld_config = CFGA_STAT_CONFIGURED;
+ cond = CFGA_COND_FAILING;
+ break;
+ /*
+ * node is offline or down.
+ * set cond to unusable.
+ */
+ case CFGA_STAT_UNCONFIGURED:
+ /*
+ * For fabric port the fca port is
+ * considered as configured since user
+ * configured previously for any
+ * existing node.
+ */
+ cond = CFGA_COND_UNUSABLE;
+ if ((strcmp(lap->xport_type,
+ FP_FC_PUBLIC_PORT_TYPE) == 0) ||
+ (strcmp(lap->xport_type,
+ FP_FC_FABRIC_PORT_TYPE) == 0)) {
+ lap->chld_config =
+ CFGA_STAT_CONFIGURED;
+ } else {
+ lap->ret = FPCFGA_OK;
+ S_FREE(port_wwn);
+ return (FPCFGA_OK);
+ }
+ break;
+ default:
+ /*
+ * continue to create ldata_list struct for
+ * this node
+ */
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ listp = calloc(1, sizeof (ldata_list_t));
+ if (listp == NULL) {
+ lap->l_errno = errno;
+ S_FREE(port_wwn);
+ return (FPCFGA_LIB_ERR);
+ }
+
+ clp = &listp->ldata;
+
+ /* Create logical and physical ap_id */
+ (void) snprintf(clp->ap_log_id, sizeof (clp->ap_log_id),
+ "%s%s%s%s%d", lap->xport_logp, DYN_SEP, port_wwn,
+ LUN_COMP_SEP, *lun_nump);
+ (void) snprintf(clp->ap_phys_id, sizeof (clp->ap_phys_id),
+ "%s%s%s%s%d", lap->apidp->xport_phys, DYN_SEP, port_wwn,
+ LUN_COMP_SEP, *lun_nump);
+ clp->ap_class[0] = '\0'; /* Filled in by libcfgadm */
+ clp->ap_r_state = lap->xport_rstate;
+ clp->ap_o_state = CFGA_STAT_CONFIGURED;
+ clp->ap_cond = cond;
+ clp->ap_busy = busy;
+ clp->ap_status_time = (time_t)-1;
+ clp->ap_info[0] = '\0';
+
+ get_hw_info(node, clp);
+
+ (void) insert_ldata_to_ldatalist(port_wwn, lun_nump, listp,
+ &(lap->listp));
+
+ lap->ret = FPCFGA_OK;
+ S_FREE(port_wwn);
+ return (FPCFGA_OK);
+}
+
+/*
+ * Searches the ldata_list to find if the the input port_wwn exist.
+ *
+ * Input: port_wwn, ldata_list.
+ * Return value: FPCFGA_APID_NOACCESS if not found on ldata_list.
+ * FPCFGA_ACCESS_OK if found on ldata_list.
+ */
+static fpcfga_ret_t
+is_dyn_ap_on_ldata_list(const char *port_wwn, const ldata_list_t *listp,
+ ldata_list_t **matchldpp, int *l_errnop)
+{
+ char *dyn = NULL, *dyncomp = NULL;
+ int len;
+ ldata_list_t *tmplp;
+ fpcfga_ret_t ret;
+
+
+ ret = FPCFGA_APID_NOACCESS;
+
+ tmplp = (ldata_list_t *)listp;
+ while (tmplp != NULL) {
+ if ((dyn = GET_DYN(tmplp->ldata.ap_phys_id)) != NULL) {
+ len = strlen(DYN_TO_DYNCOMP(dyn)) + 1;
+ dyncomp = calloc(1, len);
+ if (dyncomp == NULL) {
+ *l_errnop = errno;
+ ret = FPCFGA_LIB_ERR;
+ break;
+ }
+ (void) strcpy(dyncomp, DYN_TO_DYNCOMP(dyn));
+ if (!(strncmp(port_wwn, dyncomp, WWN_SIZE*2))) {
+ *matchldpp = tmplp;
+ S_FREE(dyncomp);
+ ret = FPCFGA_ACCESS_OK;
+ break;
+ }
+ S_FREE(dyncomp);
+ }
+ tmplp = tmplp->next;
+ }
+
+ return (ret);
+}
+
+/*
+ * Searches the ldata_list to find if the the input port_wwn and lun exist.
+ *
+ * Input: port_wwn, ldata_list.
+ * Return value: FPCFGA_APID_NOACCESS if not found on ldata_list.
+ * FPCFGA_ACCESS_OK if found on ldata_list.
+ */
+static fpcfga_ret_t
+is_FCP_dev_ap_on_ldata_list(const char *port_wwn, const int lun_num,
+ ldata_list_t *ldatap,
+ ldata_list_t **matchldpp)
+{
+ ldata_list_t *curlp = NULL;
+ char *dyn = NULL, *dyncomp = NULL;
+ char *lun_dyn = NULL, *lunp = NULL;
+ int ldata_lun;
+ fpcfga_ret_t ret;
+
+ /*
+ * if there is no list data just return the FCP dev list.
+ * Normally this should not occur since list data should
+ * be created through discoveredPort list.
+ */
+ ret = FPCFGA_APID_NOACCESS;
+ if (ldatap == NULL) {
+ return (ret);
+ }
+
+ dyn = GET_DYN(ldatap->ldata.ap_phys_id);
+ if (dyn != NULL) dyncomp = DYN_TO_DYNCOMP(dyn);
+ if ((dyncomp != NULL) &&
+ (strncmp(dyncomp, port_wwn, WWN_SIZE*2) == 0)) {
+ lun_dyn = GET_LUN_DYN(dyncomp);
+ if (lun_dyn != NULL) {
+ lunp = LUN_DYN_TO_LUNCOMP(lun_dyn);
+ if ((ldata_lun = atoi(lunp)) == lun_num) {
+ *matchldpp = ldatap;
+ return (FPCFGA_ACCESS_OK);
+ } else if (ldata_lun > lun_num) {
+ return (ret);
+ }
+ /* else continue */
+ } else {
+ /* we have match without lun comp. */
+ *matchldpp = ldatap;
+ return (FPCFGA_ACCESS_OK);
+ }
+ }
+
+ curlp = ldatap->next;
+
+ dyn = dyncomp = NULL;
+ lun_dyn = lunp = NULL;
+ while (curlp != NULL) {
+ dyn = GET_DYN(curlp->ldata.ap_phys_id);
+ if (dyn != NULL) dyncomp = DYN_TO_DYNCOMP(dyn);
+ if ((dyncomp != NULL) &&
+ (strncmp(dyncomp, port_wwn, WWN_SIZE*2) == 0)) {
+ lun_dyn = GET_LUN_DYN(dyncomp);
+ if (lun_dyn != NULL) {
+ lunp = LUN_DYN_TO_LUNCOMP(lun_dyn);
+ if ((ldata_lun = atoi(lunp)) == lun_num) {
+ *matchldpp = curlp;
+ return (FPCFGA_ACCESS_OK);
+ } else if (ldata_lun > lun_num) {
+ return (ret);
+ }
+ /* else continue */
+ } else {
+ /* we have match without lun comp. */
+ *matchldpp = curlp;
+ return (FPCFGA_ACCESS_OK);
+ }
+ }
+ dyn = dyncomp = NULL;
+ lun_dyn = lunp = NULL;
+ curlp = curlp->next;
+ }
+
+ return (ret);
+
+}
+
+/*
+ * This routine is called when a pathinfo without matching pwwn in dev_list
+ * is found.
+ */
+static fpcfga_ret_t
+init_ldata_for_mpath_dev(di_path_t path, char *pwwn, int *l_errnop,
+ fpcfga_list_t *lap)
+{
+ ldata_list_t *listp = NULL;
+ cfga_list_data_t *clp = NULL;
+ size_t devlen;
+ char *devpath;
+ di_node_t client_node = DI_NODE_NIL;
+ uint_t dctl_state = 0;
+ cfga_busy_t busy;
+ char *client_path;
+ di_path_state_t pstate;
+
+ /* get the client node path */
+ if (path == DI_PATH_NIL) {
+ return (FPCFGA_LIB_ERR);
+ }
+ client_node = di_path_client_node(path);
+ if (client_node == DI_NODE_NIL) {
+ return (FPCFGA_LIB_ERR);
+ }
+ if ((client_path = di_devfs_path(client_node)) == NULL) {
+ return (FPCFGA_LIB_ERR);
+ }
+ devlen = strlen(DEVICES_DIR) + strlen(client_path) + 1;
+ devpath = calloc(1, devlen);
+ if (devpath == NULL) {
+ di_devfs_path_free(client_path);
+ *l_errnop = errno;
+ return (FPCFGA_LIB_ERR);
+ }
+ (void) snprintf(devpath, devlen, "%s%s", DEVICES_DIR, client_path);
+
+ /* now need to create ldata for this dev */
+ listp = calloc(1, sizeof (ldata_list_t));
+ if (listp == NULL) {
+ di_devfs_path_free(client_path);
+ S_FREE(devpath);
+ *l_errnop = errno;
+ return (FPCFGA_LIB_ERR);
+ }
+
+ clp = &listp->ldata;
+
+ /* Create logical and physical ap_id */
+ (void) snprintf(clp->ap_log_id, sizeof (clp->ap_log_id), "%s%s%s",
+ lap->xport_logp, DYN_SEP, pwwn);
+ (void) snprintf(clp->ap_phys_id, sizeof (clp->ap_phys_id), "%s%s%s",
+ lap->apidp->xport_phys, DYN_SEP, pwwn);
+
+ /* Filled in by libcfgadm */
+ clp->ap_class[0] = '\0'; /* Filled by libcfgadm */
+ clp->ap_r_state = lap->xport_rstate;
+ /* set to ostate to configured. */
+ clp->ap_o_state = CFGA_STAT_CONFIGURED;
+ /*
+ * This routine is called when a port WWN is not found in dev list
+ * but path info node exists.
+ *
+ * Update the condition as failing if the pathinfo state was normal.
+ * Update the condition as unusable if the pathinfo state is failed
+ * or offline.
+ */
+ if (((pstate = di_path_state(path)) == DI_PATH_STATE_OFFLINE) ||
+ (pstate == DI_PATH_STATE_FAULT)) {
+ clp->ap_cond = CFGA_COND_UNUSABLE;
+ } else {
+ clp->ap_cond = CFGA_COND_FAILING;
+ }
+ clp->ap_status_time = (time_t)-1;
+ /* update ap_type and ap_info */
+ get_hw_info(client_node, clp);
+
+ if (devctl_cmd(devpath, FPCFGA_DEV_GETSTATE,
+ &dctl_state, l_errnop) == FPCFGA_OK) {
+ busy = ((dctl_state & DEVICE_BUSY) == DEVICE_BUSY) ? 1 : 0;
+ } else {
+ busy = 0;
+ }
+ clp->ap_busy = busy;
+ /* Link it in */
+ listp->next = lap->listp;
+ lap->listp = listp;
+
+ di_devfs_path_free(client_path);
+ S_FREE(devpath);
+
+ /* now return with ok status with ldata. */
+ lap->ret = FPCFGA_OK;
+ return (FPCFGA_OK);
+}
+
+/*
+ * Initialize the cfga_list_data struct for an accessible device
+ * from g_get_dev_list().
+ *
+ * Input: fca port ldata.
+ * Output: device cfga_list_data.
+ *
+ */
+static fpcfga_ret_t
+init_ldata_for_accessible_dev(const char *dyncomp, uchar_t inq_type,
+ fpcfga_list_t *lap)
+{
+ ldata_list_t *listp = NULL;
+ cfga_list_data_t *clp = NULL;
+ int i;
+
+ listp = calloc(1, sizeof (ldata_list_t));
+ if (listp == NULL) {
+ lap->l_errno = errno;
+ return (FPCFGA_LIB_ERR);
+ }
+
+ clp = &listp->ldata;
+
+ assert(dyncomp != NULL);
+
+ /* Create logical and physical ap_id */
+ (void) snprintf(clp->ap_log_id, sizeof (clp->ap_log_id), "%s%s%s",
+ lap->xport_logp, DYN_SEP, dyncomp);
+
+ (void) snprintf(clp->ap_phys_id, sizeof (clp->ap_phys_id), "%s%s%s",
+ lap->apidp->xport_phys, DYN_SEP, dyncomp);
+
+ clp->ap_class[0] = '\0'; /* Filled in by libcfgadm */
+ clp->ap_r_state = lap->xport_rstate;
+ clp->ap_o_state = CFGA_STAT_UNCONFIGURED;
+ clp->ap_cond = CFGA_COND_UNKNOWN;
+ clp->ap_busy = 0;
+ clp->ap_status_time = (time_t)-1;
+ clp->ap_info[0] = '\0';
+ for (i = 0; i < N_DEVICE_TYPES; i++) {
+ if (inq_type == device_list[i].itype) {
+ (void) snprintf(clp->ap_type, sizeof (clp->ap_type),
+ "%s", (char *)device_list[i].name);
+ break;
+ }
+ }
+ if (i == N_DEVICE_TYPES) {
+ if (inq_type == ERR_INQ_DTYPE) {
+ clp->ap_cond = CFGA_COND_FAILED;
+ snprintf(clp->ap_type, sizeof (clp->ap_type), "%s",
+ (char *)GET_MSG_STR(ERR_UNAVAILABLE));
+ } else {
+ (void) snprintf(clp->ap_type, sizeof (clp->ap_type),
+ "%s", "unknown");
+ }
+ }
+
+ /* Link it in */
+ (void) insert_ldata_to_ldatalist(dyncomp, NULL, listp, &(lap->listp));
+
+ return (FPCFGA_OK);
+}
+
+/*
+ * Initialize the cfga_list_data struct for an accessible FCP SCSI LUN device
+ * from the report lun data.
+ *
+ * Input: fca port ldata. report lun info
+ * Output: device cfga_list_data.
+ *
+ */
+static fpcfga_ret_t
+init_ldata_for_accessible_FCP_dev(
+ const char *port_wwn,
+ int num_luns,
+ struct report_lun_resp *resp_buf,
+ fpcfga_list_t *lap,
+ int *l_errnop)
+{
+ ldata_list_t *listp = NULL, *listp_start = NULL, *listp_end = NULL,
+ *prevlp = NULL, *curlp = NULL, *matchp_start = NULL,
+ *matchp_end = NULL;
+ cfga_list_data_t *clp = NULL;
+ char *dyn = NULL, *dyncomp = NULL;
+ uchar_t *lun_string;
+ uint16_t lun_num;
+ int i, j, str_ret;
+ fpcfga_ret_t ret;
+ char dtype[CFGA_TYPE_LEN];
+ struct scsi_inquiry *inq_buf;
+ uchar_t peri_qual;
+ cfga_cond_t cond = CFGA_COND_UNKNOWN;
+ uchar_t lun_num_raw[SAM_LUN_SIZE];
+
+ /* when number of lun is 0 it is not an error. so just return ok. */
+ if (num_luns == 0) {
+ return (FPCFGA_OK);
+ }
+
+ for (i = 0; i < num_luns; i++) {
+ lun_string = (uchar_t *)&(resp_buf->lun_string[i]);
+ memcpy(lun_num_raw, lun_string, sizeof (lun_num_raw));
+ if ((ret = get_standard_inq_data(lap->apidp->xport_phys, port_wwn,
+ lun_num_raw, &inq_buf, l_errnop))
+ != FPCFGA_OK) {
+ if (ret == FPCFGA_FCP_TGT_SEND_SCSI_FAILED) {
+ (void) strlcpy(dtype,
+ (char *)GET_MSG_STR(ERR_UNAVAILABLE), CFGA_TYPE_LEN);
+ cond = CFGA_COND_FAILED;
+ } else {
+ S_FREE(inq_buf);
+ return (FPCFGA_LIB_ERR);
+ }
+ } else {
+ peri_qual = inq_buf->inq_dtype & FP_PERI_QUAL_MASK;
+ /*
+ * peripheral qualifier is not 0 so the device node should not
+ * included in the ldata list. There should not be a device
+ * node for the lun either.
+ */
+ if (peri_qual != DPQ_POSSIBLE) {
+ S_FREE(inq_buf);
+ continue;
+ }
+ *dtype = NULL;
+ for (j = 0; j < N_DEVICE_TYPES; j++) {
+ if ((inq_buf->inq_dtype & DTYPE_MASK)
+ == device_list[j].itype) {
+ (void) strlcpy(dtype, (char *)device_list[j].name,
+ CFGA_TYPE_LEN);
+ break;
+ }
+ }
+ if (*dtype == NULL) {
+ (void) strlcpy(dtype,
+ (char *)device_list[DTYPE_UNKNOWN_INDEX].name,
+ CFGA_TYPE_LEN);
+ }
+ }
+ /*
+ * Followed FCP driver for getting lun number from report
+ * lun data.
+ * According to SAM-2 there are multiple address method for
+ * FCP SCIS LUN. Logincal unit addressing, peripheral device
+ * addressing, flat space addressing, and extended logical
+ * unit addressing.
+ *
+ * as of 11/2001 FCP supports logical unit addressing and
+ * peripheral device addressing even thoough 3 defined.
+ * SSFCP_LUN_ADDRESSING 0x80
+ * SSFCP_PD_ADDRESSING 0x00
+ * SSFCP_VOLUME_ADDRESSING 0x40
+ *
+ * the menthod below is used by FCP when (lun_string[0] & 0xC0)
+ * is either SSFCP_LUN_ADDRESSING or SSFCP_PD_ADDRESSING mode.
+ */
+ lun_num = ((lun_string[0] & 0x3F) << 8) | lun_string[1];
+ listp = calloc(1, sizeof (ldata_list_t));
+ if (listp == NULL) {
+ *l_errnop = errno;
+ list_free(&listp_start);
+ return (FPCFGA_LIB_ERR);
+ }
+
+ clp = &listp->ldata;
+ /* Create logical and physical ap_id */
+ (void) snprintf(clp->ap_log_id, sizeof (clp->ap_log_id),
+ "%s%s%s%s%d", lap->xport_logp, DYN_SEP, port_wwn,
+ LUN_COMP_SEP, lun_num);
+ (void) snprintf(clp->ap_phys_id, sizeof (clp->ap_phys_id),
+ "%s%s%s%s%d", lap->apidp->xport_phys, DYN_SEP, port_wwn,
+ LUN_COMP_SEP, lun_num);
+ (void) strncpy(clp->ap_type, dtype, strlen(dtype));
+ clp->ap_class[0] = '\0'; /* Filled in by libcfgadm */
+ clp->ap_r_state = lap->xport_rstate;
+ clp->ap_o_state = CFGA_STAT_UNCONFIGURED;
+ clp->ap_cond = cond;
+ clp->ap_busy = 0;
+ clp->ap_status_time = (time_t)-1;
+ clp->ap_info[0] = '\0';
+ if (listp_start == NULL) {
+ listp_start = listp;
+ } else {
+ if ((ret = insert_FCP_dev_ldata(
+ port_wwn, lun_num, listp,
+ &listp_start)) != FPCFGA_OK) {
+ list_free(&listp_start);
+ return (ret);
+ }
+ }
+ listp = NULL;
+ S_FREE(inq_buf);
+ }
+
+ /*
+ * list data can be null when device peripheral qualifier is not 0
+ * for any luns. Return ok to continue.
+ */
+ if (listp_start == NULL) {
+ return (FPCFGA_OK);
+ }
+ /*
+ * get the end of list for later uses.
+ */
+ curlp = listp_start->next;
+ prevlp = listp_start;
+ while (curlp) {
+ prevlp = curlp;
+ curlp = curlp->next;
+ }
+ listp_end = prevlp;
+
+ /*
+ * if there is no list data just return the FCP dev list.
+ * Normally this should not occur since list data should
+ * be created through g_get_dev_list().
+ */
+ if (lap->listp == NULL) {
+ lap->listp = listp_start;
+ for (listp = listp_start; listp != NULL; listp = listp->next) {
+ listp->ldata.ap_cond = CFGA_COND_FAILING;
+ }
+ return (FPCFGA_OK);
+ }
+
+ dyn = GET_DYN(lap->listp->ldata.ap_phys_id);
+ if ((dyn != NULL) && ((dyncomp = DYN_TO_DYNCOMP(dyn)) != NULL)) {
+ if ((str_ret = strncmp(dyncomp, port_wwn, WWN_SIZE*2)) == 0) {
+ matchp_start = matchp_end = lap->listp;
+ while (matchp_end->next != NULL) {
+ dyn = GET_DYN(
+ matchp_end->next->ldata.ap_phys_id);
+ if ((dyn != NULL) &&
+ ((dyncomp = DYN_TO_DYNCOMP(dyn)) != NULL)) {
+ if ((str_ret = strncmp(dyncomp,
+ port_wwn, WWN_SIZE*2)) == 0) {
+ matchp_end = matchp_end->next;
+ } else {
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+ /* fillup inqdtype */
+ for (listp = listp_start; listp != NULL;
+ listp = listp->next) {
+ listp->ldata.ap_cond =
+ lap->listp->ldata.ap_cond;
+ }
+ /* link the new elem of lap->listp. */
+ listp_end->next = matchp_end->next;
+ /* free the one matching wwn. */
+ matchp_end->next = NULL;
+ list_free(&matchp_start);
+ /* link lap->listp to listp_start. */
+ lap->listp = listp_start;
+ return (FPCFGA_OK);
+ } else if (str_ret > 0) {
+ for (listp = listp_start; listp != NULL;
+ listp = listp->next) {
+ listp->ldata.ap_cond = CFGA_COND_FAILING;
+ }
+ listp_end->next = lap->listp->next;
+ lap->listp = listp_start;
+ return (FPCFGA_OK);
+ }
+ }
+
+ prevlp = lap->listp;
+ curlp = lap->listp->next;
+
+ dyn = dyncomp = NULL;
+ while (curlp != NULL) {
+ dyn = GET_DYN(curlp->ldata.ap_phys_id);
+ if ((dyn != NULL) &&
+ ((dyncomp = DYN_TO_DYNCOMP(dyn)) != NULL)) {
+ if ((str_ret = strncmp(dyncomp, port_wwn,
+ WWN_SIZE*2)) == 0) {
+ matchp_start = matchp_end = curlp;
+ while (matchp_end->next != NULL) {
+ dyn = GET_DYN(
+ matchp_end->next->ldata.ap_phys_id);
+ if ((dyn != NULL) &&
+ ((dyncomp = DYN_TO_DYNCOMP(dyn))
+ != NULL)) {
+ if ((str_ret = strncmp(dyncomp,
+ port_wwn, WWN_SIZE*2))
+ == 0) {
+ matchp_end =
+ matchp_end->next;
+ } else {
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+ for (listp = listp_start; listp != NULL;
+ listp = listp->next) {
+ listp->ldata.ap_cond = curlp->ldata.ap_cond;
+ }
+ /* link the next elem to listp_end. */
+ listp_end->next = matchp_end->next;
+ /* link prevlp to listp_start to drop curlp. */
+ prevlp->next = listp_start;
+ /* free matching pwwn elem. */
+ matchp_end->next = NULL;
+ list_free(&matchp_start);
+ return (FPCFGA_OK);
+ } else if (str_ret > 0) {
+ for (listp = listp_start; listp != NULL;
+ listp = listp->next) {
+ /*
+ * Dev not found from accessible
+ * fc dev list but the node should
+ * exist. Set to failing cond now
+ * and check the node state later.
+ */
+ listp->ldata.ap_cond = CFGA_COND_FAILING;
+ }
+ /* keep the cur elem by linking to list_end. */
+ listp_end->next = curlp;
+ prevlp->next = listp_start;
+ return (FPCFGA_OK);
+ }
+ }
+ dyn = dyncomp = NULL;
+ prevlp = curlp;
+ curlp = curlp->next;
+ }
+
+ prevlp->next = listp_start;
+ for (listp = listp_start; listp != NULL; listp = listp->next) {
+ listp->ldata.ap_cond = CFGA_COND_FAILING;
+ }
+
+ return (FPCFGA_OK);
+
+}
+
+/* fill in device type, vid, pid from properties */
+static void
+get_hw_info(di_node_t node, cfga_list_data_t *clp)
+{
+ char *cp = NULL;
+ char *inq_vid, *inq_pid;
+ int i;
+
+ /*
+ * if the type is not previously assigned with valid SCSI device type
+ * check devinfo to find the type.
+ * once device is configured it should have a valid device type.
+ * device node is configured but no valid device type is found
+ * the type will be set to unavailable.
+ */
+ if (clp->ap_type != NULL) {
+ /*
+ * if the type is not one of defined SCSI device type
+ * check devinfo to find the type.
+ *
+ * Note: unknown type is not a valid device type.
+ * It is added in to the device list table to provide
+ * constant string of "unknown".
+ */
+ for (i = 0; i < (N_DEVICE_TYPES -1); i++) {
+ if (strncmp((char *)clp->ap_type, (char *)device_list[i].name,
+ sizeof (clp->ap_type)) == 0) {
+ break;
+ }
+ }
+ if (i == (N_DEVICE_TYPES - 1)) {
+ cp = (char *)get_device_type(node);
+ if (cp == NULL) {
+ cp = (char *)GET_MSG_STR(ERR_UNAVAILABLE);
+ }
+ (void) snprintf(clp->ap_type, sizeof (clp->ap_type), "%s",
+ S_STR(cp));
+ }
+ } else {
+ cp = (char *)get_device_type(node);
+ if (cp == NULL) {
+ cp = (char *)GET_MSG_STR(ERR_UNAVAILABLE);
+ }
+ (void) snprintf(clp->ap_type, sizeof (clp->ap_type), "%s",
+ S_STR(cp));
+ }
+
+ /*
+ * Fill in vendor and product ID.
+ */
+ if ((di_prop_lookup_strings(DDI_DEV_T_ANY, node,
+ "inquiry-product-id", &inq_pid) == 1) &&
+ (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
+ "inquiry-vendor-id", &inq_vid) == 1)) {
+ (void) snprintf(clp->ap_info, sizeof (clp->ap_info),
+ "%s %s", inq_vid, inq_pid);
+ }
+}
+
+/*
+ * Get dtype from "inquiry-device-type" property. If not present,
+ * derive it from minor node type
+ */
+static const char *
+get_device_type(di_node_t node)
+{
+ char *name = NULL;
+ int *inq_dtype;
+ int i;
+
+ if (node == DI_NODE_NIL) {
+ return (NULL);
+ }
+
+ /* first, derive type based on inquiry property */
+ if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "inquiry-device-type",
+ &inq_dtype) != -1) {
+ int itype = (*inq_dtype) & DTYPE_MASK;
+
+ for (i = 0; i < N_DEVICE_TYPES; i++) {
+ if (itype == device_list[i].itype) {
+ name = (char *)device_list[i].name;
+ break;
+ }
+ }
+ /*
+ * when found to be unknown type, set name to null to check
+ * device minor node type.
+ */
+ if (i == (N_DEVICE_TYPES - 1)) {
+ name = NULL;
+ }
+ }
+
+ /* if property fails, use minor nodetype */
+ if (name == NULL) {
+ char *nodetype;
+ di_minor_t minor = di_minor_next(node, DI_MINOR_NIL);
+
+ if ((minor != DI_MINOR_NIL) &&
+ ((nodetype = di_minor_nodetype(minor)) != NULL)) {
+ for (i = 0; i < N_DEVICE_TYPES; i++) {
+ if (device_list[i].ntype &&
+ (strcmp(nodetype, device_list[i].ntype)
+ == 0)) {
+ name = (char *)device_list[i].name;
+ break;
+ }
+ }
+ }
+ }
+
+ return (name);
+}
+
+/* Transform list data to stat data */
+fpcfga_ret_t
+list_ext_postprocess(
+ ldata_list_t **llpp,
+ int nelem,
+ cfga_list_data_t **ap_id_list,
+ int *nlistp,
+ char **errstring)
+{
+ cfga_list_data_t *ldatap = NULL;
+ ldata_list_t *tmplp = NULL;
+ int i = -1;
+
+ *ap_id_list = NULL;
+ *nlistp = 0;
+
+ if (*llpp == NULL || nelem < 0) {
+ return (FPCFGA_LIB_ERR);
+ }
+
+ if (nelem == 0) {
+ return (FPCFGA_APID_NOEXIST);
+ }
+
+ ldatap = calloc(nelem, sizeof (cfga_list_data_t));
+ if (ldatap == NULL) {
+ cfga_err(errstring, errno, ERR_LIST, 0);
+ return (FPCFGA_LIB_ERR);
+ }
+
+ /* Extract the list_data structures from the linked list */
+ tmplp = *llpp;
+ for (i = 0; i < nelem && tmplp != NULL; i++) {
+ ldatap[i] = tmplp->ldata;
+ tmplp = tmplp->next;
+ }
+
+ if (i < nelem || tmplp != NULL) {
+ S_FREE(ldatap);
+ return (FPCFGA_LIB_ERR);
+ }
+
+ *nlistp = nelem;
+ *ap_id_list = ldatap;
+
+ return (FPCFGA_OK);
+}
+
+/*
+ * Convert bus state to receptacle state
+ */
+static cfga_stat_t
+xport_devinfo_to_recep_state(uint_t xport_di_state)
+{
+ cfga_stat_t rs;
+
+ switch (xport_di_state) {
+ case DI_BUS_QUIESCED:
+ case DI_BUS_DOWN:
+ rs = CFGA_STAT_DISCONNECTED;
+ break;
+ /*
+ * NOTE: An explicit flag for active should probably be added to
+ * libdevinfo.
+ */
+ default:
+ rs = CFGA_STAT_CONNECTED;
+ break;
+ }
+
+ return (rs);
+}
+
+/*
+ * Convert device state to occupant state
+ * if driver is attached the node is configured.
+ * if offline or down the node is unconfigured.
+ * if only driver detached it is none state which is treated the same
+ * way as configured state.
+ */
+static cfga_stat_t
+dev_devinfo_to_occupant_state(uint_t dev_di_state)
+{
+ /* Driver attached ? */
+ if ((dev_di_state & DI_DRIVER_DETACHED) != DI_DRIVER_DETACHED) {
+ return (CFGA_STAT_CONFIGURED);
+ }
+
+ if ((dev_di_state & DI_DEVICE_OFFLINE) == DI_DEVICE_OFFLINE ||
+ (dev_di_state & DI_DEVICE_DOWN) == DI_DEVICE_DOWN) {
+ return (CFGA_STAT_UNCONFIGURED);
+ } else {
+ return (CFGA_STAT_NONE);
+ }
+}
+
+/*
+ * Wrapper routine for inserting ldata to make an sorted ldata list.
+ *
+ * When show_FCP_dev option is given insert_FCP_dev_ldata() is called.
+ * Otherwise insert_fc_dev_ldata() is called.
+ */
+static fpcfga_ret_t
+insert_ldata_to_ldatalist(
+ const char *port_wwn,
+ int *lun_nump,
+ ldata_list_t *listp,
+ ldata_list_t **ldatapp)
+{
+
+ if (lun_nump == NULL) {
+ return (insert_fc_dev_ldata(port_wwn, listp, ldatapp));
+ } else {
+ return
+ (insert_FCP_dev_ldata(port_wwn, *lun_nump, listp, ldatapp));
+ }
+}
+
+/*
+ * Insert an input ldata to ldata list to make sorted ldata list.
+ */
+static fpcfga_ret_t
+insert_fc_dev_ldata(
+ const char *port_wwn,
+ ldata_list_t *listp,
+ ldata_list_t **ldatapp)
+{
+ ldata_list_t *prevlp = NULL, *curlp = NULL;
+ char *dyn = NULL, *dyncomp = NULL;
+
+ if (*ldatapp == NULL) {
+ *ldatapp = listp;
+ return (FPCFGA_OK);
+ }
+
+ dyn = GET_DYN((*ldatapp)->ldata.ap_phys_id);
+ if (dyn != NULL) dyncomp = DYN_TO_DYNCOMP(dyn);
+ if ((dyncomp != NULL) &&
+ (strncmp(dyncomp, port_wwn, WWN_SIZE*2) >= 0)) {
+ listp->next = *ldatapp;
+ *ldatapp = listp;
+ return (FPCFGA_OK);
+ }
+ /* else continue */
+
+ prevlp = *ldatapp;
+ curlp = (*ldatapp)->next;
+
+ dyn = dyncomp = NULL;
+ while (curlp != NULL) {
+ dyn = GET_DYN(curlp->ldata.ap_phys_id);
+ if (dyn != NULL) dyncomp = DYN_TO_DYNCOMP(dyn);
+ if ((dyncomp != NULL) &&
+ (strncmp(dyncomp, port_wwn, WWN_SIZE*2) >= 0)) {
+ listp->next = prevlp->next;
+ prevlp->next = listp;
+ return (FPCFGA_OK);
+ }
+ dyn = dyncomp = NULL;
+ prevlp = curlp;
+ curlp = curlp->next;
+ }
+
+ /* add the ldata to the end of the list. */
+ prevlp->next = listp;
+ return (FPCFGA_OK);
+}
+
+/*
+ * Insert an input ldata to ldata list to make sorted ldata list.
+ */
+static fpcfga_ret_t
+insert_FCP_dev_ldata(
+ const char *port_wwn,
+ int lun_num,
+ ldata_list_t *listp,
+ ldata_list_t **ldatapp)
+{
+ ldata_list_t *prevlp = NULL, *curlp = NULL;
+ char *dyn = NULL, *dyncomp = NULL;
+ char *lun_dyn = NULL, *lunp = NULL;
+
+ if (*ldatapp == NULL) {
+ *ldatapp = listp;
+ return (FPCFGA_OK);
+ }
+
+ dyn = GET_DYN((*ldatapp)->ldata.ap_phys_id);
+ if (dyn != NULL) dyncomp = DYN_TO_DYNCOMP(dyn);
+ if ((dyncomp != NULL) &&
+ (strncmp(dyncomp, port_wwn, WWN_SIZE*2) == 0)) {
+ lun_dyn = GET_LUN_DYN(dyncomp);
+ if (lun_dyn != NULL) {
+ lunp = LUN_DYN_TO_LUNCOMP(lun_dyn);
+ if ((atoi(lunp)) >= lun_num) {
+ listp->next = *ldatapp;
+ *ldatapp = listp;
+ return (FPCFGA_OK);
+ }
+ }
+ } else if ((dyncomp != NULL) &&
+ (strncmp(dyncomp, port_wwn, WWN_SIZE*2) > 0)) {
+ listp->next = *ldatapp;
+ *ldatapp = listp;
+ return (FPCFGA_OK);
+ }
+
+ prevlp = *ldatapp;
+ curlp = (*ldatapp)->next;
+
+ dyn = dyncomp = NULL;
+ lun_dyn = lunp = NULL;
+ while (curlp != NULL) {
+ dyn = GET_DYN(curlp->ldata.ap_phys_id);
+ if (dyn != NULL) dyncomp = DYN_TO_DYNCOMP(dyn);
+
+ if ((dyncomp != NULL) &&
+ (strncmp(dyncomp, port_wwn, WWN_SIZE*2) == 0)) {
+ lun_dyn = GET_LUN_DYN(dyncomp);
+ if (lun_dyn != NULL) {
+ lunp = LUN_DYN_TO_LUNCOMP(lun_dyn);
+ if ((atoi(lunp)) >= lun_num) {
+ listp->next = prevlp->next;
+ prevlp->next = listp;
+ return (FPCFGA_OK);
+ }
+ }
+ /* else continue */
+ } else if ((dyncomp != NULL) &&
+ (strncmp(dyncomp, port_wwn, WWN_SIZE*2) > 0)) {
+ listp->next = prevlp->next;
+ prevlp->next = listp;
+ return (FPCFGA_OK);
+ }
+ /* else continue */
+
+ dyn = dyncomp = NULL;
+ lun_dyn = lunp = NULL;
+ prevlp = curlp;
+ curlp = curlp->next;
+ }
+
+ /* add the ldata to the end of the list. */
+ prevlp->next = listp;
+ return (FPCFGA_OK);
+}
+
+/*
+ * This function will return the dtype for the given device
+ * It will first issue a report lun to lun 0 and then it will issue a SCSI
+ * Inquiry to the first lun returned by report luns.
+ *
+ * If everything is successful, the dtype will be returned with the peri
+ * qualifier masked out.
+ *
+ * If either the report lun or the scsi inquiry fails, we will first check
+ * the return status. If the return status is SCSI_DEVICE_NOT_TGT, then
+ * we will assume this is a remote HBA and return an UNKNOWN DTYPE
+ * for all other failures, we will return a dtype of ERR_INQ_DTYPE
+ */
+static uchar_t
+get_inq_dtype(char *xport_phys, char *dyncomp, HBA_HANDLE handle,
+ HBA_PORTATTRIBUTES *portAttrs, HBA_PORTATTRIBUTES *discPortAttrs) {
+ HBA_STATUS status;
+ report_lun_resp_t *resp_buf;
+ int num_luns = 0, ret, l_errno;
+ uchar_t *lun_string;
+ uint64_t lun = 0;
+ struct scsi_inquiry inq;
+ struct scsi_extended_sense sense;
+ HBA_UINT8 scsiStatus;
+ uint32_t inquirySize = sizeof (inq);
+ uint32_t senseSize = sizeof (sense);
+
+ memset(&inq, 0, sizeof (inq));
+ memset(&sense, 0, sizeof (sense));
+ if ((ret = get_report_lun_data(xport_phys, dyncomp,
+ &num_luns, &resp_buf, &sense, &l_errno))
+ != FPCFGA_OK) {
+ /*
+ * Checking the sense key data as well as the additional
+ * sense key. The SES Node is not required to repond
+ * to Report LUN. In the case of Minnow, the SES node
+ * returns with KEY_ILLEGAL_REQUEST and the additional
+ * sense key of 0x20. In this case we will blindly
+ * send the SCSI Inquiry call to lun 0
+ *
+ * if we get any other error we will set the inq_type
+ * appropriately
+ */
+ if ((sense.es_key == KEY_ILLEGAL_REQUEST) &&
+ (sense.es_add_code == 0x20)) {
+ lun = 0;
+ } else {
+ if (ret == FPCFGA_FCP_SEND_SCSI_DEV_NOT_TGT) {
+ inq.inq_dtype = DTYPE_UNKNOWN;
+ } else {
+ inq.inq_dtype = ERR_INQ_DTYPE;
+ }
+ return (inq.inq_dtype);
+ }
+ } else {
+ /* send the inquiry to the first lun */
+ lun_string = (uchar_t *)&(resp_buf->lun_string[0]);
+ memcpy(&lun, lun_string, sizeof (lun));
+ S_FREE(resp_buf);
+ }
+
+ memset(&sense, 0, sizeof (sense));
+ status = HBA_ScsiInquiryV2(handle,
+ portAttrs->PortWWN, discPortAttrs->PortWWN, lun, 0, 0,
+ &inq, &inquirySize, &scsiStatus, &sense, &senseSize);
+ if (status == HBA_STATUS_OK) {
+ inq.inq_dtype = inq.inq_dtype & DTYPE_MASK;
+ } else if (status == HBA_STATUS_ERROR_NOT_A_TARGET) {
+ inq.inq_dtype = DTYPE_UNKNOWN;
+ } else {
+ inq.inq_dtype = ERR_INQ_DTYPE;
+ }
+ return (inq.inq_dtype);
+}
diff --git a/usr/src/lib/cfgadm_plugins/fp/common/cfga_rcm.c b/usr/src/lib/cfgadm_plugins/fp/common/cfga_rcm.c
new file mode 100644
index 0000000000..5fbf41c96b
--- /dev/null
+++ b/usr/src/lib/cfgadm_plugins/fp/common/cfga_rcm.c
@@ -0,0 +1,613 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#include "cfga_fp.h"
+
+static fpcfga_ret_t fp_rcm_init(char *, cfga_flags_t, char **, uint_t *,
+ char **rsrc_fixed);
+static int fp_rcm_process_node(di_node_t, void *);
+static fpcfga_ret_t fp_rcm_info_table(rcm_info_t *, char **);
+static char *chop_minor(char *);
+
+#define MAX_FORMAT 80
+#define DEVICES "/devices"
+
+typedef struct {
+ char *bus_path;
+ char *filter;
+ char **errstring;
+ fpcfga_ret_t ret;
+ cfga_flags_t flags;
+ fpcfga_ret_t (*func)(char *, char *, char **, cfga_flags_t);
+} walkargs_t;
+
+static fpcfga_ret_t fp_rcm_info_table(rcm_info_t *, char **);
+static int fp_rcm_process_node(di_node_t, void *);
+static fpcfga_ret_t fp_rcm_init(char *, cfga_flags_t, char **, uint_t *,
+ char **);
+static char *chop_minor(char *);
+
+static rcm_handle_t *rcm_handle = NULL;
+static mutex_t rcm_handle_lock;
+
+/*
+ * fp_rcm_offline()
+ *
+ * Offline FP resource consumers.
+ */
+fpcfga_ret_t
+fp_rcm_offline(char *rsrc, char **errstring, cfga_flags_t flags)
+{
+ int rret;
+ uint_t rflags = 0;
+ char *rsrc_fixed;
+ rcm_info_t *rinfo = NULL;
+ fpcfga_ret_t ret = FPCFGA_OK;
+
+ if ((ret = fp_rcm_init(rsrc, flags, errstring, &rflags, &rsrc_fixed))
+ != FPCFGA_OK)
+ return (ret);
+
+ if ((rret = rcm_request_offline(rcm_handle, rsrc_fixed, rflags, &rinfo))
+ != RCM_SUCCESS) {
+ cfga_err(errstring, 0, ERRARG_RCM_OFFLINE, rsrc_fixed, 0);
+ if (rinfo) {
+ (void) fp_rcm_info_table(rinfo, errstring);
+ rcm_free_info(rinfo);
+ }
+ if (rret == RCM_FAILURE)
+ (void) fp_rcm_online(rsrc, errstring, flags);
+ ret = FPCFGA_BUSY;
+ }
+
+ S_FREE(rsrc_fixed);
+
+ return (ret);
+}
+
+/*
+ * fp_rcm_online()
+ *
+ * Online FP resource consumers that were previously offlined.
+ */
+fpcfga_ret_t
+fp_rcm_online(char *rsrc, char **errstring, cfga_flags_t flags)
+{
+ char *rsrc_fixed;
+ rcm_info_t *rinfo = NULL;
+ fpcfga_ret_t ret = FPCFGA_OK;
+
+ if ((ret = fp_rcm_init(rsrc, flags, errstring, NULL, &rsrc_fixed))
+ != FPCFGA_OK)
+ return (ret);
+
+ if (rcm_notify_online(rcm_handle, rsrc_fixed, 0, &rinfo)
+ != RCM_SUCCESS && rinfo != NULL) {
+ cfga_err(errstring, 0, ERRARG_RCM_ONLINE, rsrc_fixed, 0);
+ (void) fp_rcm_info_table(rinfo, errstring);
+ rcm_free_info(rinfo);
+ ret = FPCFGA_ERR;
+ }
+
+ S_FREE(rsrc_fixed);
+
+ return (ret);
+}
+
+/*
+ * fp_rcm_remove()
+ *
+ * Remove FP resource consumers after their kernel removal.
+ */
+fpcfga_ret_t
+fp_rcm_remove(char *rsrc, char **errstring, cfga_flags_t flags)
+{
+ char *rsrc_fixed;
+ rcm_info_t *rinfo = NULL;
+ fpcfga_ret_t ret = FPCFGA_OK;
+
+ if ((ret = fp_rcm_init(rsrc, flags, errstring, NULL, &rsrc_fixed))
+ != FPCFGA_OK)
+ return (ret);
+
+ if (rcm_notify_remove(rcm_handle, rsrc_fixed, 0, &rinfo)
+ != RCM_SUCCESS) {
+ cfga_err(errstring, 0, ERRARG_RCM_REMOVE, rsrc_fixed, 0);
+ if (rinfo) {
+ (void) fp_rcm_info_table(rinfo, errstring);
+ rcm_free_info(rinfo);
+ }
+ ret = FPCFGA_ERR;
+ }
+
+ S_FREE(rsrc_fixed);
+
+ return (ret);
+}
+
+/*
+ * fp_rcm_suspend()
+ *
+ * Suspend FP resource consumers before a bus quiesce.
+ */
+fpcfga_ret_t
+fp_rcm_suspend(char *rsrc, char *filter, char **errstring, cfga_flags_t flags)
+{
+ int rret;
+ uint_t rflags = 0;
+ char *rsrc_fixed;
+ char *filter_fixed;
+ char *rsrc_devpath;
+ rcm_info_t *rinfo = NULL;
+ di_node_t node;
+ fpcfga_ret_t ret = FPCFGA_OK;
+ walkargs_t walkargs;
+ timespec_t zerotime = { 0, 0 };
+
+ if ((ret = fp_rcm_init(rsrc, flags, errstring, &rflags, &rsrc_fixed))
+ != FPCFGA_OK)
+ return (ret);
+
+ /* If a filter is provided, ensure that it makes sense */
+ if (filter != NULL && strstr(filter, rsrc) != filter) {
+ S_FREE(rsrc_fixed);
+ cfga_err(errstring, 0, ERR_APID_INVAL, 0);
+ return (FPCFGA_ERR);
+ }
+
+ /*
+ * If no filter is specified: attempt a suspension on the resource,
+ * directly.
+ */
+ if (filter == NULL) {
+ if ((rret = rcm_request_suspend(rcm_handle, rsrc_fixed, rflags,
+ &zerotime, &rinfo)) != RCM_SUCCESS) {
+ cfga_err(errstring, 0, ERRARG_RCM_SUSPEND, rsrc_fixed,
+ 0);
+ if (rinfo) {
+ (void) fp_rcm_info_table(rinfo, errstring);
+ rcm_free_info(rinfo);
+ }
+ if (rret == RCM_FAILURE)
+ (void) fp_rcm_resume(rsrc, filter, errstring,
+ (flags & (~CFGA_FLAG_FORCE)));
+ ret = FPCFGA_BUSY;
+ }
+ S_FREE(rsrc_fixed);
+ return (ret);
+ }
+
+ /*
+ * If a filter is specified: open the resource with libdevinfo, walk
+ * through its nodes, and attempt a suspension of each node that
+ * mismatches the filter.
+ */
+
+ /* Chop off the filter's minor name */
+ if ((filter_fixed = chop_minor(filter)) == NULL)
+ return (FPCFGA_ERR);
+
+ /* get a libdevinfo snapshot of the resource's subtree */
+ rsrc_devpath = rsrc_fixed;
+ if (strstr(rsrc_fixed, DEVICES) != NULL)
+ rsrc_devpath += strlen(DEVICES);
+ node = di_init(rsrc_devpath, DINFOSUBTREE | DINFOMINOR);
+ if (node == DI_NODE_NIL) {
+ cfga_err(errstring, 0, ERRARG_DEVINFO, rsrc_fixed, 0);
+ ret = FPCFGA_ERR;
+ }
+
+ /* apply the filter, and suspend all resources not filtered out */
+ if (ret == FPCFGA_OK) {
+
+ walkargs.bus_path = rsrc_fixed;
+ walkargs.filter = filter_fixed;
+ walkargs.errstring = errstring;
+ walkargs.ret = FPCFGA_OK;
+ walkargs.flags = rflags;
+ walkargs.func = fp_rcm_suspend;
+
+ if (di_walk_node(node, 0, &walkargs, fp_rcm_process_node) < 0)
+ cfga_err(errstring, 0, ERRARG_DEVINFO, rsrc_fixed, 0);
+
+ ret = walkargs.ret;
+ }
+
+ if (node != DI_NODE_NIL)
+ di_fini(node);
+
+ S_FREE(rsrc_fixed);
+ S_FREE(filter_fixed);
+
+ if (ret != FPCFGA_OK)
+ (void) fp_rcm_resume(rsrc, filter, errstring,
+ (flags & (~CFGA_FLAG_FORCE)));
+
+ return (ret);
+}
+
+/*
+ * fp_rcm_resume()
+ *
+ * Resume FP resource consumers after a bus has been unquiesced.
+ */
+fpcfga_ret_t
+fp_rcm_resume(char *rsrc, char *filter, char **errstring, cfga_flags_t flags)
+{
+ uint_t rflags = 0;
+ char *rsrc_fixed;
+ char *filter_fixed;
+ char *rsrc_devpath;
+ rcm_info_t *rinfo = NULL;
+ di_node_t node;
+ fpcfga_ret_t ret = FPCFGA_OK;
+ walkargs_t walkargs;
+
+ if ((ret = fp_rcm_init(rsrc, flags, errstring, &rflags, &rsrc_fixed))
+ != FPCFGA_OK)
+ return (ret);
+
+ /* If a filter is provided, ensure that it makes sense */
+ if (filter != NULL && strstr(filter, rsrc) != filter) {
+ S_FREE(rsrc_fixed);
+ cfga_err(errstring, 0, ERR_APID_INVAL, 0);
+ return (FPCFGA_ERR);
+ }
+
+ /*
+ * If no filter is specified: resume the resource directly.
+ */
+ if (filter == NULL) {
+ if (rcm_notify_resume(rcm_handle, rsrc_fixed, rflags, &rinfo)
+ != RCM_SUCCESS && rinfo != NULL) {
+ cfga_err(errstring, 0, ERRARG_RCM_RESUME, rsrc_fixed,
+ 0);
+ (void) fp_rcm_info_table(rinfo, errstring);
+ rcm_free_info(rinfo);
+ ret = FPCFGA_BUSY;
+ }
+ S_FREE(rsrc_fixed);
+ return (ret);
+ }
+
+ /*
+ * If a filter is specified: open the resource with libdevinfo, walk
+ * through its nodes, and resume each of its nodes that mismatches
+ * the filter.
+ */
+
+ /* Chop off the filter's minor name */
+ if ((filter_fixed = chop_minor(filter)) == NULL)
+ return (FPCFGA_ERR);
+
+ /* get a libdevinfo snapshot of the resource's subtree */
+ rsrc_devpath = rsrc_fixed;
+ if (strstr(rsrc_fixed, DEVICES) != NULL)
+ rsrc_devpath += strlen(DEVICES);
+ node = di_init(rsrc_devpath, DINFOSUBTREE | DINFOMINOR);
+ if (node == DI_NODE_NIL) {
+ cfga_err(errstring, 0, ERRARG_DEVINFO, rsrc_fixed, 0);
+ ret = FPCFGA_ERR;
+ }
+
+ /* apply the filter, and resume all resources not filtered out */
+ if (ret == FPCFGA_OK) {
+
+ walkargs.bus_path = rsrc_fixed;
+ walkargs.filter = filter_fixed;
+ walkargs.errstring = errstring;
+ walkargs.ret = FPCFGA_OK;
+ walkargs.flags = rflags;
+ walkargs.func = fp_rcm_resume;
+
+ if (di_walk_node(node, 0, &walkargs, fp_rcm_process_node) < 0)
+ cfga_err(errstring, 0, ERRARG_DEVINFO, rsrc_fixed, 0);
+
+ ret = walkargs.ret;
+ }
+
+ if (node != DI_NODE_NIL)
+ di_fini(node);
+
+ S_FREE(rsrc_fixed);
+ S_FREE(filter_fixed);
+
+ return (ret);
+}
+
+/*
+ * fp_rcm_info
+ *
+ * Queries RCM information for resources, and formats it into a table.
+ * The table is appended to the info argument. If the info argument is a
+ * null pointer, then a new string is malloc'ed. If the info argument is
+ * not a null pointer, then it is realloc'ed to the required size.
+ */
+fpcfga_ret_t
+fp_rcm_info(char *rsrc, char **errstring, char **info)
+{
+ char *rsrc_fixed;
+ rcm_info_t *rinfo = NULL;
+ fpcfga_ret_t ret = FPCFGA_OK;
+
+ if ((ret = fp_rcm_init(rsrc, 0, errstring, NULL, &rsrc_fixed))
+ != FPCFGA_OK)
+ return (ret);
+
+ if (info == NULL) {
+ S_FREE(rsrc_fixed);
+ return (FPCFGA_ERR);
+ }
+
+ if (rcm_get_info(rcm_handle, rsrc_fixed, 0, &rinfo)
+ != RCM_SUCCESS) {
+ cfga_err(errstring, 0, ERRARG_RCM_INFO, rsrc_fixed, 0);
+ ret = FPCFGA_ERR;
+ } else if (rinfo == NULL)
+ ret = FPCFGA_OK;
+
+ if (rinfo) {
+ if ((ret = fp_rcm_info_table(rinfo, info)) != FPCFGA_OK)
+ cfga_err(errstring, 0, ERRARG_RCM_INFO, rsrc_fixed, 0);
+ rcm_free_info(rinfo);
+ }
+
+ S_FREE(rsrc_fixed);
+
+ return (ret);
+}
+
+/*
+ * fp_rcm_init()
+ *
+ * Contains common initialization code for entering a fp_rcm_xx()
+ * routine.
+ */
+static fpcfga_ret_t
+fp_rcm_init(char *rsrc, cfga_flags_t flags, char **errstring, uint_t *rflags,
+ char **rsrc_fixed)
+{
+ /* Validate the rsrc argument */
+ if (rsrc == NULL) {
+ cfga_err(errstring, 0, ERR_APID_INVAL, 0);
+ return (FPCFGA_ERR);
+ }
+
+ /* Translate the cfgadm flags to RCM flags */
+ if (rflags && (flags & CFGA_FLAG_FORCE))
+ *rflags |= RCM_FORCE;
+
+ /* Get a handle for the RCM operations */
+ (void) mutex_lock(&rcm_handle_lock);
+ if (rcm_handle == NULL) {
+ if (rcm_alloc_handle(NULL, RCM_NOPID, NULL, &rcm_handle) !=
+ RCM_SUCCESS) {
+ cfga_err(errstring, 0, ERR_RCM_HANDLE, 0);
+ (void) mutex_unlock(&rcm_handle_lock);
+ return (FPCFGA_LIB_ERR);
+ }
+ }
+ (void) mutex_unlock(&rcm_handle_lock);
+
+ /* Chop off the rsrc's minor, if it has one */
+ if ((*rsrc_fixed = chop_minor(rsrc)) == NULL)
+ return (FPCFGA_ERR);
+
+ return (FPCFGA_OK);
+}
+
+/*
+ * fp_rcm_process_node
+ *
+ * Helper routine for fp_rcm_{suspend,resume}. This is a di_walk_node()
+ * callback that will apply a filter to every node it sees, and either suspend
+ * or resume it if it doesn't match the filter.
+ */
+static int
+fp_rcm_process_node(di_node_t node, void *argp)
+{
+ char *devfs_path;
+ walkargs_t *walkargs;
+ fpcfga_ret_t ret = FPCFGA_OK;
+ char disk_path[MAXPATHLEN];
+
+ /* Guard against bad arguments */
+ if ((walkargs = (walkargs_t *)argp) == NULL)
+ return (DI_WALK_TERMINATE);
+ if (walkargs->filter == NULL || walkargs->errstring == NULL) {
+ walkargs->ret = FPCFGA_ERR;
+ return (DI_WALK_TERMINATE);
+ }
+
+ /* If the node has no minors, then skip it */
+ if (di_minor_next(node, DI_MINOR_NIL) == DI_MINOR_NIL)
+ return (DI_WALK_CONTINUE);
+
+ /* Construct the devices path */
+ if ((devfs_path = di_devfs_path(node)) == NULL)
+ return (DI_WALK_CONTINUE);
+ (void) snprintf(disk_path, MAXPATHLEN, "%s%s", DEVICES, devfs_path);
+ di_devfs_path_free(devfs_path);
+
+ /*
+ * If the node does not correspond to the targeted FP bus or the
+ * disk being filtered out, then use the appropriate suspend/resume
+ * function.
+ */
+ if (strcmp(disk_path, walkargs->bus_path) != 0 &&
+ strcmp(disk_path, walkargs->filter) != 0)
+ ret = (*walkargs->func)(disk_path, NULL, walkargs->errstring,
+ walkargs->flags);
+
+ /* Stop the walk early if the above operation failed */
+ if (ret != FPCFGA_OK) {
+ walkargs->ret = ret;
+ return (DI_WALK_TERMINATE);
+ }
+
+ return (DI_WALK_CONTINUE);
+}
+
+/*
+ * fp_rcm_info_table
+ *
+ * Takes an opaque rcm_info_t pointer and a character pointer, and appends
+ * the rcm_info_t data in the form of a table to the given character pointer.
+ */
+static fpcfga_ret_t
+fp_rcm_info_table(rcm_info_t *rinfo, char **table)
+{
+ int i;
+ size_t w;
+ size_t width = 0;
+ size_t w_rsrc = 0;
+ size_t w_info = 0;
+ size_t table_size = 0;
+ uint_t tuples = 0;
+ rcm_info_tuple_t *tuple = NULL;
+ char *rsrc;
+ char *info;
+ char *newtable;
+ static char format[MAX_FORMAT];
+ const char *info_info_str, *info_rsrc_str;
+
+ /* Protect against invalid arguments */
+ if (rinfo == NULL || table == NULL)
+ return (FPCFGA_ERR);
+
+ /* Set localized table header strings */
+ rsrc = gettext("Resource");
+ info = gettext("Information");
+
+ /* A first pass, to size up the RCM information */
+ while (tuple = rcm_info_next(rinfo, tuple)) {
+ info_info_str = rcm_info_info(tuple);
+ info_rsrc_str = rcm_info_rsrc(tuple);
+ if ((info_info_str != NULL) && (info_rsrc_str != NULL)) {
+ tuples++;
+ if ((w = strlen(info_rsrc_str)) > w_rsrc)
+ w_rsrc = w;
+ if ((w = strlen(info_info_str)) > w_info)
+ w_info = w;
+ }
+ }
+
+ /* If nothing was sized up above, stop early */
+ if (tuples == 0)
+ return (FPCFGA_OK);
+
+ /* Adjust column widths for column headings */
+ if ((w = strlen(rsrc)) > w_rsrc)
+ w_rsrc = w;
+ else if ((w_rsrc - w) % 2)
+ w_rsrc++;
+ if ((w = strlen(info)) > w_info)
+ w_info = w;
+ else if ((w_info - w) % 2)
+ w_info++;
+
+ /*
+ * Compute the total line width of each line,
+ * accounting for intercolumn spacing.
+ */
+ width = w_info + w_rsrc + 4;
+
+ /* Allocate space for the table */
+ table_size = (2 + tuples) * (width + 1) + 2;
+ if (*table == NULL)
+ *table = malloc(table_size);
+ else {
+ newtable = realloc(*table, strlen(*table) + table_size);
+ if (newtable != NULL)
+ *table = newtable;
+ }
+ if (*table == NULL)
+ return (FPCFGA_ERR);
+
+ /* Place a table header into the string */
+
+ /* The resource header */
+ (void) strcat(*table, "\n");
+ w = strlen(rsrc);
+ for (i = 0; i < ((w_rsrc - w) / 2); i++)
+ (void) strcat(*table, " ");
+ (void) strcat(*table, rsrc);
+ for (i = 0; i < ((w_rsrc - w) / 2); i++)
+ (void) strcat(*table, " ");
+
+ /* The information header */
+ (void) strcat(*table, " ");
+ w = strlen(info);
+ for (i = 0; i < ((w_info - w) / 2); i++)
+ (void) strcat(*table, " ");
+ (void) strcat(*table, info);
+ for (i = 0; i < ((w_info - w) / 2); i++)
+ (void) strcat(*table, " ");
+
+ /* Underline the headers */
+ (void) strcat(*table, "\n");
+ for (i = 0; i < w_rsrc; i++)
+ (void) strcat(*table, "-");
+ (void) strcat(*table, " ");
+ for (i = 0; i < w_info; i++)
+ (void) strcat(*table, "-");
+
+ /* Construct the format string */
+ (void) snprintf(format, MAX_FORMAT, "%%-%ds %%-%ds", w_rsrc, w_info);
+
+ /* Add the tuples to the table string */
+ tuple = NULL;
+ while ((tuple = rcm_info_next(rinfo, tuple)) != NULL) {
+ info_info_str = rcm_info_info(tuple);
+ info_rsrc_str = rcm_info_rsrc(tuple);
+ if ((info_info_str != NULL) && (info_rsrc_str != NULL)) {
+ (void) strcat(*table, "\n");
+ (void) sprintf(&((*table)[strlen(*table)]),
+ format, info_rsrc_str, info_info_str);
+ }
+ }
+
+ return (FPCFGA_OK);
+}
+
+/*
+ * chop_minor()
+ *
+ * Chops off the minor name portion of a resource. Allocates storage for
+ * the returned string. Caller must free the storage if return is non-NULL.
+ */
+static char *
+chop_minor(char *rsrc)
+{
+ char *rsrc_fixed;
+ char *cp;
+
+ if ((rsrc_fixed = strdup(rsrc)) == NULL)
+ return (NULL);
+ if ((cp = strrchr(rsrc_fixed, ':')) != NULL)
+ *cp = '\0';
+ return (rsrc_fixed);
+}
diff --git a/usr/src/lib/cfgadm_plugins/fp/common/cfga_rep.c b/usr/src/lib/cfgadm_plugins/fp/common/cfga_rep.c
new file mode 100644
index 0000000000..0ca8cb4e09
--- /dev/null
+++ b/usr/src/lib/cfgadm_plugins/fp/common/cfga_rep.c
@@ -0,0 +1,614 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+
+#include <libgen.h>
+#include "cfga_fp.h"
+
+/* The following are used by update_fabric_wwn_list() */
+#define COPY_EXT ".cpy." /* Extn used in naming backup file */
+#define TMP_EXT ".tmp." /* Extn used in naming temp file */
+static char *HDR =
+"#\n"
+"# fabric_WWN_map\n"
+"#\n"
+"# The physical ap_id list of configured fabric devices.\n"
+"# Do NOT edit this file by hand -- refer to the cfgadm_fp(1M)\n"
+"# man page and use cfgadm(1m) instead.\n"
+"#\n";
+
+/*
+ * This function searches for "srch_str" (of length "slen") in "buf" (of length
+ * "buflen"). If it is not found, "write_offset" has the offset in "buf" where
+ * "srch_str" would have to be added in "buf". If "srch_str" is found in "buf",
+ * "write_offset" has its offset in "buf"
+ *
+ * ARGUMENTS :
+ * buf - buffer to search in
+ * buflen - length of buffer
+ * srch_str - string to search
+ * slen - length of srch_str
+ * write_offset - Set in function on exit
+ * - It is the offset in buf where srch_str is or should be
+ * bytes_left - Set in function on exit
+ * - It is the # of bytes left beyond write_offset in buf
+ *
+ * Notes :
+ * - This function assumes "buf" is sorted in ascending order
+ * - If 'buflen' is > 0, it assumes it has a header on top and skips it
+ * - "srch_str" has '\n' at the end, but when update_fabric_wwn_list() calls
+ * this function, 'slen' does not include the last `\n'
+ *
+ * RETURN VALUES :
+ * Zero - "srch_str" found in "buf"... "write_offset" has offset in "buf"
+ * > 0 - "srch_str" NOT found in "buf" ... "write_offset" has offset in "buf"
+ * where "srch_str" can fit in.
+ * "buf" had contents > "srch_str"
+ * < 0 - "srch_str" NOT found in "buf" ... "write_offset" has offset in "buf"
+ * where "srch_str" can fit in.
+ * "buf" had contents < "srch_str"
+ */
+static int
+search_line(char *buf, int buflen, char *srch_str, int slen,
+ int *write_offset, int *bytes_left)
+{
+ int retval, sizeof_rep_hdr = strlen(HDR);
+ char *sol; /* Pointer to Start-Of-Line */
+ char *cur_pos; /* current position */
+
+ *bytes_left = buflen;
+ *write_offset = 0;
+
+ if (buf == NULL || *buf == NULL || buflen <= 0)
+ return (-2); /* Arbitrary -ve val. srch_str not found */
+
+ if (srch_str == NULL || *srch_str == NULL || slen <= 0)
+ return (0); /* This says srch_str was found */
+
+ sol = cur_pos = buf;
+ if (buflen >= sizeof_rep_hdr) {
+ /* skip header */
+ sol = cur_pos = buf + sizeof_rep_hdr;
+ *bytes_left -= sizeof_rep_hdr;
+ }
+
+ while (*bytes_left >= slen) {
+ if ((retval = strncmp(sol, srch_str, slen)) >= 0) {
+ /* strncmp will pass if srch_str is a substring */
+ if ((retval == 0) && (*bytes_left > slen) &&
+ (*(sol+slen) != '\n'))
+ retval = 1; /* Force it to be > 0 */
+ *write_offset = sol - buf;
+ return (retval);
+ }
+
+ /* retval < 0 */
+ if ((cur_pos = strchr(sol, (int)'\n')) == NULL) {
+ *write_offset = buflen;
+ return (retval);
+ }
+
+ /* Get the length of this line */
+ *cur_pos = '\0'; /* kludge to get string length */
+ *bytes_left -= (strlen(sol) + 1);
+ *cur_pos = '\n'; /* Put back the original char */
+
+ sol = cur_pos = cur_pos + 1;
+ }
+
+ if (*bytes_left > 0) {
+ /* In this case the bytes left will be less than slen */
+ if ((retval = strncmp(sol, srch_str, *bytes_left)) >= 0) {
+ *write_offset = sol - buf;
+ } else {
+ *write_offset = buflen;
+ }
+ return (retval);
+ }
+ *write_offset = sol - buf;
+ /* Should return a value < 0 to show that search string goes to eof */
+ return (-1);
+}
+
+/*
+ * This function sets an advisory lock on the file pointed to by the argument
+ * fd, which is a file descriptor. The lock is set using fcntl() which uses
+ * flock structure.
+ */
+int
+lock_register(int fd, int cmd, int type, off_t offset, int whence, off_t len)
+{
+ struct flock lock;
+
+ lock.l_type = type;
+ lock.l_start = offset;
+ lock.l_whence = whence;
+ lock.l_len = len;
+
+ return (fcntl(fd, cmd, &lock));
+}
+
+/* Lot of places to cleanup - Less chance of missing out using this macro */
+#define CLEANUP_N_RET(ret) \
+ if (fd != -1) { \
+ close(fd); \
+ } \
+ if (copy_fd != -1) { \
+ close(copy_fd); \
+ } \
+ if (tmp_fd != -1) { \
+ close(tmp_fd); \
+ } \
+ if (copy_rep != NULL) { \
+ remove(copy_rep); \
+ free(copy_rep); \
+ } \
+ if (tmp_rep != NULL) { \
+ remove(tmp_rep); \
+ free(tmp_rep); \
+ } \
+ if (upd_str != NULL) { \
+ free(upd_str); \
+ } \
+ if (repbuf != NULL) { \
+ munmap(repbuf, filesize); \
+ } \
+ if (c_repbuf != NULL) { \
+ munmap(c_repbuf, filesize); \
+ } \
+ if (t_repbuf != NULL) { \
+ munmap(t_repbuf, size); \
+ } \
+ return (ret)
+
+/*
+ * INPUTS:
+ * cmd - ADD_ENTRY or REMOVE_ENTRY
+ * update_str - string for repository operation
+ * - Assumed NOT to have a '\n' and that it is null terminated
+ * errstring - Pointer that will be updated by this function
+ * - Any error msgs that has to be sent back to caller
+ *
+ * RETURNS :
+ * FPCFGA_OK on success
+ * FPCFGA_LIB_ERR on error
+ *
+ * SYNOPSIS:
+ * This function adds or deletes 'update_str' from FAB_REPOSITORY based on
+ * value of 'cmd'. The repository has a warning line on the top to disallow
+ * manual editing of the file. If the repository is being created fresh or if
+ * it is of zero length or if it has only warning lines in it, the operation
+ * speicified by 'cmd' is performed and returned. If the repository exists
+ * and has some data, it is expected to be of atleast the size of the lenght
+ * of the warning header. This is the only check that is performed on the
+ * validity of the file. No other checks are performed. On a valid
+ * repository, to perform the update, this function basically makes use of
+ * 3 buffers - the original buffer (repbuf), a copy buffer (c_repbuf) and a
+ * temp buffer (t_repbuf).
+ * The contents of the repository are mmap-ed into the repbuf and then
+ * copied into the c_repbuf. All further operations are done using the copy.
+ * t_repbuf is created to be the size of c_repbuf +/- 'slen' (based on
+ * whether it is add or remove operation). After adding/removing the
+ * 'update_str', the c_repbuf is copied to a OLD_FAB_REPOSITORY and t_repbuf
+ * is made FAB_REPOSITORY.
+ *
+ */
+int
+update_fabric_wwn_list(int cmd, const char *update_str, char **errstring)
+{
+ int fd, copy_fd, tmp_fd, new_file_flag = 0;
+ int len, write_offset, bytes_left;
+ int sizeof_rep_hdr = strlen(HDR);
+ char *repbuf, *c_repbuf, *t_repbuf;
+ char *copy_rep, *tmp_rep, *upd_str;
+ off_t filesize, size;
+ struct stat stbuf;
+
+ /* Do some initializations */
+ fd = copy_fd = tmp_fd = -1;
+ repbuf = c_repbuf = t_repbuf = NULL;
+ copy_rep = tmp_rep = upd_str = NULL;
+ size = filesize = write_offset = bytes_left = 0;
+
+ /*
+ * Set the mode to read only. Root user can still open as RDWR.
+ * We ignore errors in general here. But, just notice ENOENTs
+ */
+ if ((chmod(FAB_REPOSITORY, S_IRUSR|S_IRGRP|S_IROTH) == -1) &&
+ (errno == ENOENT)) {
+ new_file_flag = 1;
+ mkdirp(FAB_REPOSITORY_DIR,
+ S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
+ }
+
+ /* Create the repository if its not there */
+ if ((fd = open(FAB_REPOSITORY, O_RDWR | O_CREAT)) == -1) {
+ cfga_err(errstring, errno, ERR_UPD_REP, 0);
+ return (FPCFGA_LIB_ERR);
+ }
+
+ /* Now try to chmod again. This time we dont ignore errors */
+ if (fchmod(fd, S_IRUSR | S_IRGRP | S_IROTH) < 0) {
+ close(fd);
+ cfga_err(errstring, errno, ERR_UPD_REP, 0);
+ return (FPCFGA_LIB_ERR);
+ }
+
+ if (lock_register(fd, F_SETLKW, F_WRLCK, 0, SEEK_SET, 0) < 0) {
+ close(fd);
+ cfga_err(errstring, 0, ERR_UPD_REP, 0);
+ return (FPCFGA_LIB_ERR);
+ }
+
+ if (fstat(fd, &stbuf) == -1) {
+ close(fd);
+ cfga_err(errstring, errno, ERR_UPD_REP, 0);
+ return (FPCFGA_LIB_ERR);
+ }
+
+ filesize = size = stbuf.st_size;
+
+ /* A very Minimal check on repository */
+ if (filesize && filesize < sizeof_rep_hdr) {
+ /*
+ * If there is some data, it should be atleast the size of
+ * the header
+ */
+ close(fd);
+ cfga_err(errstring, errno, ERR_UPD_REP, 0);
+ return (FPCFGA_LIB_ERR);
+ }
+
+ if ((len = strlen(update_str)) == 0) {
+ /*
+ * We are trying to add/remove a NULL string.
+ * Just return success
+ */
+ close(fd);
+ return (FPCFGA_OK);
+ }
+
+ if ((upd_str = calloc(1, len + 2)) == NULL) {
+ close(fd);
+ cfga_err(errstring, errno, ERR_UPD_REP, 0);
+ return (FPCFGA_LIB_ERR);
+ }
+
+ strcpy(upd_str, update_str);
+ strcat(upd_str, "\n"); /* Append a new line char */
+ len = strlen(upd_str);
+
+ if (filesize > 0) {
+ if ((copy_rep = (char *)calloc(1, strlen(FAB_REPOSITORY) +
+ sizeof (COPY_EXT) + sizeof (pid_t))) == NULL) {
+ cfga_err(errstring, errno, ERR_UPD_REP, 0);
+ CLEANUP_N_RET(FPCFGA_LIB_ERR);
+ }
+
+ (void) sprintf(copy_rep, "%s%s%ld", FAB_REPOSITORY, COPY_EXT,
+ getpid());
+
+ if ((copy_fd = open(copy_rep, O_RDWR | O_CREAT | O_TRUNC,
+ S_IRUSR | S_IWUSR)) < 0) {
+ cfga_err(errstring, errno, ERR_UPD_REP, 0);
+ CLEANUP_N_RET(FPCFGA_LIB_ERR);
+ }
+
+ if ((repbuf = (char *)mmap(0, filesize, PROT_READ,
+ MAP_SHARED, fd, 0)) == MAP_FAILED) {
+ close(fd);
+ free(upd_str);
+ cfga_err(errstring, errno, ERR_UPD_REP, 0);
+ return (FPCFGA_LIB_ERR);
+ }
+
+ if (lseek(copy_fd, filesize - 1, SEEK_SET) == -1) {
+ cfga_err(errstring, errno, ERR_UPD_REP, 0);
+ CLEANUP_N_RET(FPCFGA_LIB_ERR);
+ }
+
+ if (write(copy_fd, "", 1) != 1) {
+ cfga_err(errstring, errno, ERR_UPD_REP, 0);
+ CLEANUP_N_RET(FPCFGA_LIB_ERR);
+ }
+
+ if ((c_repbuf = (char *)mmap(0, filesize,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED, copy_fd, 0)) == MAP_FAILED) {
+ cfga_err(errstring, errno, ERR_UPD_REP, 0);
+ CLEANUP_N_RET(FPCFGA_LIB_ERR);
+ }
+
+ memcpy(c_repbuf, repbuf, filesize);
+ /*
+ * We cannot close the repository since we hold a lock
+ * But we'll free up the mmap-ed area.
+ */
+ munmap(repbuf, filesize);
+ repbuf = NULL;
+ }
+
+ /*
+ * If we just created this file, or it was an empty repository file
+ * add a header to the beginning of file.
+ * If it had was a repository file with just the header,
+ */
+ if (new_file_flag != 0 || filesize == 0 || filesize == sizeof_rep_hdr) {
+ if ((filesize != sizeof_rep_hdr) &&
+ (write(fd, HDR, sizeof_rep_hdr) != sizeof_rep_hdr)) {
+ cfga_err(errstring, errno, ERR_UPD_REP, 0);
+ CLEANUP_N_RET(FPCFGA_LIB_ERR);
+ }
+
+ /*
+ * We know its a new file, empty file or a file with only a
+ * header so lets get the update operation done with
+ */
+ switch (cmd) {
+ case ADD_ENTRY:
+ /* If there is a header, we have to skip it */
+ if (lseek(fd, 0, SEEK_END) == -1) {
+ cfga_err(errstring, errno, ERR_UPD_REP, 0);
+ CLEANUP_N_RET(FPCFGA_LIB_ERR);
+ }
+
+ if (write(fd, upd_str, len) != len) {
+ cfga_err(errstring, errno, ERR_UPD_REP, 0);
+ CLEANUP_N_RET(FPCFGA_LIB_ERR);
+ }
+
+ if (filesize > 0) {
+ /* Now create the '.old' file */
+ if (msync(c_repbuf, filesize, MS_SYNC) == -1) {
+ cfga_err(errstring, errno,
+ ERR_UPD_REP, 0);
+ CLEANUP_N_RET(FPCFGA_LIB_ERR);
+ }
+
+ if (fchmod(copy_fd,
+ S_IRUSR | S_IRGRP | S_IROTH) < 0) {
+ cfga_err(errstring, errno,
+ ERR_UPD_REP, 0);
+ CLEANUP_N_RET(FPCFGA_LIB_ERR);
+ }
+ rename(copy_rep, OLD_FAB_REPOSITORY);
+ }
+
+ CLEANUP_N_RET(FPCFGA_OK);
+
+ case REMOVE_ENTRY:
+ /*
+ * So, the side effect of a remove on an empty or
+ * non-existing repository is that the repository got
+ * created
+ */
+ CLEANUP_N_RET(FPCFGA_OK);
+
+ default:
+ cfga_err(errstring, 0, ERR_UPD_REP, 0);
+ CLEANUP_N_RET(FPCFGA_LIB_ERR);
+ }
+ }
+
+ /* Now, size and filesize are > sizeof_rep_hdr */
+
+ switch (cmd) {
+ case ADD_ENTRY:
+ size += len;
+ /*
+ * We'll search the full repository, header included, since
+ * we dont expect upd_str to match anything in the header.
+ */
+ if (search_line(c_repbuf, filesize, upd_str,
+ len - 1, &write_offset, &bytes_left) == 0) {
+ /* line already exists in repository or len == 0 */
+ CLEANUP_N_RET(FPCFGA_OK); /* SUCCESS */
+ }
+
+ /* construct temp file name using pid. */
+ if ((tmp_rep = (char *)calloc(1, strlen(FAB_REPOSITORY) +
+ sizeof (TMP_EXT) + sizeof (pid_t))) == NULL) {
+ cfga_err(errstring, errno, ERR_UPD_REP, 0);
+ CLEANUP_N_RET(FPCFGA_LIB_ERR);
+ }
+
+ (void) sprintf(tmp_rep, "%s%s%ld", FAB_REPOSITORY,
+ TMP_EXT, getpid());
+
+ /* Open tmp repository file in absolute mode */
+ if ((tmp_fd = open(tmp_rep, O_RDWR|O_CREAT|O_TRUNC,
+ S_IRUSR | S_IWUSR)) < 0) {
+ cfga_err(errstring, errno, ERR_UPD_REP, 0);
+ CLEANUP_N_RET(FPCFGA_LIB_ERR);
+ }
+
+ if (lseek(tmp_fd, size - 1, SEEK_SET) == -1) {
+ cfga_err(errstring, errno, ERR_UPD_REP, 0);
+ CLEANUP_N_RET(FPCFGA_LIB_ERR);
+ }
+
+ if (write(tmp_fd, "", 1) != 1) {
+ cfga_err(errstring, errno, ERR_UPD_REP, 0);
+ CLEANUP_N_RET(FPCFGA_LIB_ERR);
+ }
+
+ if ((t_repbuf = (char *)mmap(0, size, PROT_READ|PROT_WRITE,
+ MAP_SHARED, tmp_fd, 0)) == MAP_FAILED) {
+ cfga_err(errstring, errno, ERR_UPD_REP, 0);
+ CLEANUP_N_RET(FPCFGA_LIB_ERR);
+ }
+
+ memcpy(t_repbuf, c_repbuf, write_offset);
+ strncpy(t_repbuf + write_offset, upd_str, len);
+ if (write_offset != filesize) {
+ memcpy(t_repbuf + write_offset + len,
+ c_repbuf + write_offset, bytes_left);
+ }
+
+ /*
+ * we are using the copy of FAB_REPOSITORY and will
+ * do msync first since it will be renamed to '.old' file.
+ */
+ if (msync(c_repbuf, filesize, MS_SYNC) == -1) {
+ cfga_err(errstring, errno, ERR_UPD_REP, 0);
+ CLEANUP_N_RET(FPCFGA_LIB_ERR);
+ }
+
+ if (fchmod(copy_fd, S_IRUSR | S_IRGRP | S_IROTH) < 0) {
+ cfga_err(errstring, errno, ERR_UPD_REP, 0);
+ CLEANUP_N_RET(FPCFGA_LIB_ERR);
+ }
+
+ if (msync(t_repbuf, size, MS_SYNC) == -1) {
+ cfga_err(errstring, errno, ERR_UPD_REP, 0);
+ CLEANUP_N_RET(FPCFGA_LIB_ERR);
+ }
+
+ if (fchmod(tmp_fd, S_IRUSR | S_IRGRP | S_IROTH) < 0) {
+ cfga_err(errstring, errno, ERR_UPD_REP, 0);
+ CLEANUP_N_RET(FPCFGA_LIB_ERR);
+ }
+
+ close(copy_fd); copy_fd = -1;
+ close(tmp_fd); tmp_fd = -1;
+
+ /* here we do rename and rename before close fd */
+ rename(copy_rep, OLD_FAB_REPOSITORY);
+ rename(tmp_rep, FAB_REPOSITORY);
+
+ if (lock_register(fd, F_SETLK, F_UNLCK, 0, SEEK_SET, 0) < 0) {
+ cfga_err(errstring, errno, ERR_UPD_REP, 0);
+ CLEANUP_N_RET(FPCFGA_LIB_ERR);
+ }
+
+ CLEANUP_N_RET(FPCFGA_OK);
+
+ case REMOVE_ENTRY:
+ if (size >= sizeof_rep_hdr + len - 1) {
+ size -= len;
+ /*
+ * No need to init the 'else' part (size < len) because
+ * in that case, there will be nothing to delete from
+ * the file and so 'size' will not be used in the code
+ * below since search_line() will not find upd_str.
+ */
+ }
+
+ if (search_line(c_repbuf, filesize, upd_str, len - 1,
+ &write_offset, &bytes_left) != 0) {
+ /* this line does not exists - nothing to remove */
+ CLEANUP_N_RET(FPCFGA_OK); /* SUCCESS */
+ }
+
+ /* construct temp file name using pid. */
+ if ((tmp_rep = (char *)calloc(1, strlen(FAB_REPOSITORY) +
+ sizeof (TMP_EXT) + sizeof (pid_t))) == NULL) {
+ cfga_err(errstring, errno, ERR_UPD_REP, 0);
+ CLEANUP_N_RET(FPCFGA_LIB_ERR);
+ }
+
+ (void) sprintf(tmp_rep, "%s%s%ld", FAB_REPOSITORY,
+ TMP_EXT, getpid());
+
+ /* Open tmp repository file in absolute mode */
+ if ((tmp_fd = open(tmp_rep, O_RDWR|O_CREAT|O_TRUNC,
+ S_IRUSR | S_IWUSR)) < 0) {
+ cfga_err(errstring, errno, ERR_UPD_REP, 0);
+ CLEANUP_N_RET(FPCFGA_LIB_ERR);
+ }
+
+ if (size > 0) {
+ if (lseek(tmp_fd, size - 1, SEEK_SET) == -1) {
+ cfga_err(errstring, errno, ERR_UPD_REP, 0);
+ CLEANUP_N_RET(FPCFGA_LIB_ERR);
+ }
+
+ if (write(tmp_fd, "", 1) != 1) {
+ cfga_err(errstring, errno, ERR_UPD_REP, 0);
+ CLEANUP_N_RET(FPCFGA_LIB_ERR);
+ }
+
+ if ((t_repbuf = (char *)mmap(0, size,
+ PROT_READ|PROT_WRITE,
+ MAP_SHARED, tmp_fd, 0)) == MAP_FAILED) {
+ cfga_err(errstring, errno, ERR_UPD_REP, 0);
+ CLEANUP_N_RET(FPCFGA_LIB_ERR);
+ }
+
+ memcpy(t_repbuf, c_repbuf, write_offset);
+ if ((bytes_left - len) > 0) {
+ memcpy(t_repbuf + write_offset,
+ c_repbuf + write_offset + len,
+ bytes_left - len);
+ }
+
+ if (msync(t_repbuf, size, MS_SYNC) == -1) {
+ cfga_err(errstring, errno, ERR_UPD_REP, 0);
+ CLEANUP_N_RET(FPCFGA_LIB_ERR);
+ }
+ }
+
+ if (fchmod(tmp_fd, S_IRUSR | S_IRGRP | S_IROTH) < 0) {
+ cfga_err(errstring, errno, ERR_UPD_REP, 0);
+ CLEANUP_N_RET(FPCFGA_LIB_ERR);
+ }
+
+ /*
+ * we are using the copy of FAB_REPOSITORY and will
+ * do msync first since it will be renamed to bak file.
+ */
+ if (msync(c_repbuf, filesize, MS_SYNC) == -1) {
+ cfga_err(errstring, errno, ERR_UPD_REP, 0);
+ CLEANUP_N_RET(FPCFGA_LIB_ERR);
+ }
+
+ if (fchmod(copy_fd, S_IRUSR | S_IRGRP | S_IROTH) < 0) {
+ cfga_err(errstring, errno, ERR_UPD_REP, 0);
+ CLEANUP_N_RET(FPCFGA_LIB_ERR);
+ }
+
+ /* Close and invalidate the fd's */
+ close(copy_fd); copy_fd = -1;
+ close(tmp_fd); tmp_fd = -1;
+
+ /* here we do rename and rename before close fd */
+ rename(copy_rep, OLD_FAB_REPOSITORY);
+ rename(tmp_rep, FAB_REPOSITORY);
+
+ if (lock_register(fd, F_SETLK, F_UNLCK, 0, SEEK_SET, 0) < 0) {
+ cfga_err(errstring, errno, ERR_UPD_REP, 0);
+ CLEANUP_N_RET(FPCFGA_LIB_ERR);
+ }
+
+ CLEANUP_N_RET(FPCFGA_OK);
+
+ default:
+ /* Unexpected - just getout */
+ break;
+ }
+
+ CLEANUP_N_RET(FPCFGA_OK); /* SUCCESS */
+}
diff --git a/usr/src/lib/cfgadm_plugins/fp/common/cfga_utils.c b/usr/src/lib/cfgadm_plugins/fp/common/cfga_utils.c
new file mode 100644
index 0000000000..0ab98b77f5
--- /dev/null
+++ b/usr/src/lib/cfgadm_plugins/fp/common/cfga_utils.c
@@ -0,0 +1,1372 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#include "cfga_fp.h"
+
+/*
+ * This file contains helper routines for the FP plugin
+ */
+
+#if !defined(TEXT_DOMAIN)
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+
+typedef struct strlist {
+ const char *str;
+ struct strlist *next;
+} strlist_t;
+
+typedef struct {
+ fpcfga_ret_t fp_err;
+ cfga_err_t cfga_err;
+} errcvt_t;
+
+typedef struct {
+ fpcfga_cmd_t cmd;
+ int type;
+ int (*fcn)(const devctl_hdl_t);
+} set_state_cmd_t;
+
+typedef struct {
+ fpcfga_cmd_t cmd;
+ int type;
+ int (*state_fcn)(const devctl_hdl_t, uint_t *);
+} get_state_cmd_t;
+
+/* defines for nftw() */
+#define NFTW_DEPTH 1
+#define NFTW_CONTINUE 0
+#define NFTW_TERMINATE 1
+#define NFTW_ERROR -1
+#define MAX_RETRIES 10
+
+/* Function prototypes */
+static int do_recurse_dev(const char *path, const struct stat *sbuf,
+ int type, struct FTW *ftwp);
+static fpcfga_recur_t lookup_dev(const char *lpath, void *arg);
+static void msg_common(char **err_msgpp, int append_newline, int l_errno,
+ va_list ap);
+static void lunlist_free(struct luninfo_list *lunlist);
+
+/* Globals */
+struct {
+ mutex_t mp;
+ void *arg;
+ fpcfga_recur_t (*fcn)(const char *lpath, void *arg);
+} nftw_arg = {DEFAULTMUTEX};
+
+/*
+ * The string table contains most of the strings used by the fp cfgadm plugin.
+ * All strings which are to be internationalized must be in this table.
+ * Some strings which are not internationalized are also included here.
+ * Arguments to messages are NOT internationalized.
+ */
+msgcvt_t str_tbl[] = {
+
+/*
+ * The first element (ERR_UNKNOWN) MUST always be present in the array.
+ */
+#define UNKNOWN_ERR_IDX 0 /* Keep the index in sync */
+
+
+/* msg_code num_args, I18N msg_string */
+
+/* ERRORS */
+{ERR_UNKNOWN, 0, 1, "unknown error"},
+{ERR_OP_FAILED, 0, 1, "operation failed"},
+{ERR_CMD_INVAL, 0, 1, "invalid command"},
+{ERR_NOT_BUSAPID, 0, 1, "not a FP bus apid"},
+{ERR_APID_INVAL, 0, 1, "invalid FP ap_id"},
+{ERR_NOT_BUSOP, 0, 1, "operation not supported for FC bus"},
+{ERR_NOT_DEVOP, 0, 1, "operation not supported for FC device"},
+{ERR_UNAVAILABLE, 0, 1, "unavailable"},
+{ERR_CTRLR_CRIT, 0, 1, "critical partition controlled by FC HBA"},
+{ERR_BUS_GETSTATE, 0, 1, "failed to get state for FC bus"},
+{ERR_BUS_NOTCONNECTED, 0, 1, "FC bus not connected"},
+{ERR_BUS_CONNECTED, 0, 1, "FC bus not disconnected"},
+{ERR_BUS_QUIESCE, 0, 1, "FC bus quiesce failed"},
+{ERR_BUS_UNQUIESCE, 0, 1, "FC bus unquiesce failed"},
+{ERR_BUS_CONFIGURE, 0, 1, "failed to configure devices on FC bus"},
+{ERR_BUS_UNCONFIGURE, 0, 1, "failed to unconfigure FC bus"},
+{ERR_DEV_CONFIGURE, 0, 1, "failed to configure FC device"},
+{ERR_DEV_UNCONFIGURE, 0, 1, "failed to unconfigure FC device"},
+{ERR_FCA_CONFIGURE, 0, 1, "failed to configure ANY device on FCA port"},
+{ERR_FCA_UNCONFIGURE, 0, 1, "failed to unconfigure ANY device on FCA port"},
+{ERR_DEV_REPLACE, 0, 1, "replace operation failed"},
+{ERR_DEV_INSERT, 0, 1, "insert operation failed"},
+{ERR_DEV_GETSTATE, 0, 1, "failed to get state for FC device"},
+{ERR_RESET, 0, 1, "reset failed"},
+{ERR_LIST, 0, 1, "list operation failed"},
+{ERR_SIG_STATE, 0, 1, "could not restore signal disposition"},
+{ERR_MAYBE_BUSY, 0, 1, "device may be busy"},
+{ERR_BUS_DEV_MISMATCH, 0, 1, "mismatched FC bus and device"},
+{ERR_MEM_ALLOC, 0, 1, "Failed to allocated memory"},
+{ERR_DEVCTL_OFFLINE, 0, 1, "failed to offline device"},
+{ERR_UPD_REP, 0, 1, "Repository update failed"},
+{ERR_CONF_OK_UPD_REP, 0, 1,
+ "Configuration successful, but Repository update failed"},
+{ERR_UNCONF_OK_UPD_REP, 0, 1,
+ "Unconfiguration successful, but Repository update failed"},
+{ERR_PARTIAL_SUCCESS, 0, 1,
+ "Operation partially successful. Some failures seen"},
+{ERR_HBA_LOAD_LIBRARY, 0, 1,
+ "HBA load library failed"},
+{ERR_MATCHING_HBA_PORT, 0, 1,
+ "No match HBA port found"},
+{ERR_NO_ADAPTER_FOUND, 0, 1,
+ "No Fibre Channel adpaters found"},
+
+/* Errors with arguments */
+{ERRARG_OPT_INVAL, 1, 1, "invalid option: "},
+{ERRARG_HWCMD_INVAL, 1, 1, "invalid command: "},
+{ERRARG_DEVINFO, 1, 1, "libdevinfo failed on path: "},
+{ERRARG_NOT_IN_DEVLIST, 1, 1, "Device not found in fabric device list: "},
+{ERRARG_NOT_IN_DEVINFO, 1, 1, "Could not find entry in devinfo tree: "},
+{ERRARG_DI_GET_PROP, 1, 1, "Could not get libdevinfo property: "},
+{ERRARG_DC_DDEF_ALLOC, 1, 1, "failed to alloc ddef space: "},
+{ERRARG_DC_BYTE_ARRAY, 1, 1, "failed to add property: "},
+{ERRARG_DC_BUS_ACQUIRE, 1, 1, "failed to acquire bus handle: "},
+{ERRARG_BUS_DEV_CREATE, 1, 1, "failed to create device node: "},
+{ERRARG_BUS_DEV_CREATE_UNKNOWN, 1, 1,
+ "failed to create device node... Device may be unconfigurable: "},
+{ERRARG_DEV_ACQUIRE, 1, 1, "device acquire operation failed: "},
+{ERRARG_DEV_REMOVE, 1, 1, "remove operation failed: "},
+
+/* Fibre Channel operation Errors */
+{ERR_FC, 0, 1, "FC error"},
+{ERR_FC_GET_DEVLIST, 0, 1, "Failed to get fabric device list"},
+{ERR_FC_GET_NEXT_DEV, 0, 1, "Failed to get next device on device map"},
+{ERR_FC_GET_FIRST_DEV, 0, 1, "Failed to get first device on device map"},
+{ERRARG_FC_DEV_MAP_INIT, 1, 1,
+ "Failed to initialize device map for: "},
+{ERRARG_FC_PROP_LOOKUP_BYTES, 1, 1, "Failed to get property of "},
+{ERRARG_FC_INQUIRY, 1, 1, "inquiry failed: "},
+{ERRARG_FC_REP_LUNS, 1, 1, "report LUNs failed: "},
+{ERRARG_FC_TOPOLOGY, 1, 1, "failed to get port topology: "},
+{ERRARG_PATH_TOO_LONG, 1, 1, "Path length exceeds max possible: "},
+{ERRARG_INVALID_PATH, 1, 1, "Invalid path: "},
+{ERRARG_OPENDIR, 1, 1, "failure opening directory: "},
+
+/* MPXIO Errors */
+{ERRARG_VHCI_GET_PATHLIST, 1, 1, "failed to get path list from vHCI: "},
+{ERRARG_XPORT_NOT_IN_PHCI_LIST, 1, 1, "Transport not in pHCI list: "},
+
+/* RCM Errors */
+{ERR_RCM_HANDLE, 0, 1, "cannot get RCM handle"},
+{ERRARG_RCM_SUSPEND, 1, 1, "failed to suspend: "},
+{ERRARG_RCM_RESUME, 1, 1, "failed to resume: "},
+{ERRARG_RCM_OFFLINE, 1, 1, "failed to offline: "},
+{ERRARG_RCM_ONLINE, 1, 1, "failed to online: "},
+{ERRARG_RCM_REMOVE, 1, 1, "failed to remove: "},
+{ERRARG_RCM_INFO, 1, 1, "failed to query: "},
+
+/* Commands */
+{CMD_INSERT_DEV, 0, 0, "insert_device"},
+{CMD_REMOVE_DEV, 0, 0, "remove_device"},
+{CMD_REPLACE_DEV, 0, 0, "replace_device"},
+{CMD_RESET_DEV, 0, 0, "reset_device"},
+{CMD_RESET_BUS, 0, 0, "reset_bus"},
+{CMD_RESET_ALL, 0, 0, "reset_all"},
+
+/* help messages */
+{MSG_HELP_HDR, 0, 1, "\nfp attachment point specific options:\n"},
+{MSG_HELP_USAGE, 0, 0,
+ "\t-c configure -o force_update ap_id [ap_id..]\n"
+ "\t-c configure -o no_update ap_id [ap_id...]\n"
+ "\t-c unconfigure -o force_update ap_id [ap_id... ]\n"
+ "\t-c unconfigure -o no_update ap_id [ap_id... ]\n"},
+
+/* hotplug messages */
+{MSG_INSDEV, 1, 1, "Adding device to FC HBA: "},
+{MSG_RMDEV, 1, 1, "Removing FC device: "},
+{MSG_REPLDEV, 1, 1, "Replacing FC device: "},
+
+/* Hotplugging confirmation prompts */
+{CONF_QUIESCE_1, 1, 1,
+ "This operation will suspend activity on FC bus: "},
+
+{CONF_QUIESCE_2, 0, 1, "\nContinue"},
+
+{CONF_UNQUIESCE, 0, 1,
+ "FC bus quiesced successfully.\n"
+ "It is now safe to proceed with hotplug operation."
+ "\nEnter y if operation is complete or n to abort"},
+
+/* Misc. */
+{WARN_DISCONNECT, 0, 1,
+ "WARNING: Disconnecting critical partitions may cause system hang."
+ "\nContinue"}
+};
+
+
+#define N_STRS (sizeof (str_tbl) / sizeof (str_tbl[0]))
+
+#define GET_MSG_NARGS(i) (str_tbl[msg_idx(i)].nargs)
+#define GET_MSG_INTL(i) (str_tbl[msg_idx(i)].intl)
+
+static errcvt_t err_cvt_tbl[] = {
+ { FPCFGA_OK, CFGA_OK },
+ { FPCFGA_LIB_ERR, CFGA_LIB_ERROR },
+ { FPCFGA_APID_NOEXIST, CFGA_APID_NOEXIST },
+ { FPCFGA_NACK, CFGA_NACK },
+ { FPCFGA_BUSY, CFGA_BUSY },
+ { FPCFGA_SYSTEM_BUSY, CFGA_SYSTEM_BUSY },
+ { FPCFGA_OPNOTSUPP, CFGA_OPNOTSUPP },
+ { FPCFGA_PRIV, CFGA_PRIV },
+ { FPCFGA_UNKNOWN_ERR, CFGA_ERROR },
+ { FPCFGA_ERR, CFGA_ERROR }
+};
+
+#define N_ERR_CVT_TBL (sizeof (err_cvt_tbl)/sizeof (err_cvt_tbl[0]))
+
+#define DEV_OP 0
+#define BUS_OP 1
+static set_state_cmd_t set_state_cmds[] = {
+
+{ FPCFGA_BUS_QUIESCE, BUS_OP, devctl_bus_quiesce },
+{ FPCFGA_BUS_UNQUIESCE, BUS_OP, devctl_bus_unquiesce },
+{ FPCFGA_BUS_CONFIGURE, BUS_OP, devctl_bus_configure },
+{ FPCFGA_BUS_UNCONFIGURE, BUS_OP, devctl_bus_unconfigure },
+{ FPCFGA_RESET_BUS, BUS_OP, devctl_bus_reset },
+{ FPCFGA_RESET_ALL, BUS_OP, devctl_bus_resetall },
+{ FPCFGA_DEV_CONFIGURE, DEV_OP, devctl_device_online },
+{ FPCFGA_DEV_UNCONFIGURE, DEV_OP, devctl_device_offline },
+{ FPCFGA_DEV_REMOVE, DEV_OP, devctl_device_remove },
+{ FPCFGA_RESET_DEV, DEV_OP, devctl_device_reset }
+
+};
+
+#define N_SET_STATE_CMDS (sizeof (set_state_cmds)/sizeof (set_state_cmds[0]))
+
+static get_state_cmd_t get_state_cmds[] = {
+{ FPCFGA_BUS_GETSTATE, BUS_OP, devctl_bus_getstate },
+{ FPCFGA_DEV_GETSTATE, DEV_OP, devctl_device_getstate }
+};
+
+#define N_GET_STATE_CMDS (sizeof (get_state_cmds)/sizeof (get_state_cmds[0]))
+
+/* Order is important. Earlier directories are searched first */
+static const char *dev_dir_hints[] = {
+ CFGA_DEV_DIR,
+ DEV_RMT,
+ DEV_DSK,
+ DEV_RDSK,
+ DEV_DIR
+};
+
+#define N_DEV_DIR_HINTS (sizeof (dev_dir_hints) / sizeof (dev_dir_hints[0]))
+
+
+/*
+ * Routine to search the /dev directory or a subtree of /dev.
+ * If the entire /dev hierarchy is to be searched, the most likely directories
+ * are searched first.
+ */
+fpcfga_ret_t
+recurse_dev(
+ const char *basedir,
+ void *arg,
+ fpcfga_recur_t (*fcn)(const char *lpath, void *arg))
+{
+ int i, rv = NFTW_ERROR;
+
+ (void) mutex_lock(&nftw_arg.mp);
+
+ nftw_arg.arg = arg;
+ nftw_arg.fcn = fcn;
+
+ if (strcmp(basedir, DEV_DIR)) {
+ errno = 0;
+ rv = nftw(basedir, do_recurse_dev, NFTW_DEPTH, FTW_PHYS);
+ goto out;
+ }
+
+ /*
+ * Search certain selected subdirectories first if basedir == "/dev".
+ * Ignore errors as some of these directories may not exist.
+ */
+ for (i = 0; i < N_DEV_DIR_HINTS; i++) {
+ errno = 0;
+ if ((rv = nftw(dev_dir_hints[i], do_recurse_dev, NFTW_DEPTH,
+ FTW_PHYS)) == NFTW_TERMINATE) {
+ break;
+ }
+ }
+
+ /*FALLTHRU*/
+out:
+ (void) mutex_unlock(&nftw_arg.mp);
+ return (rv == NFTW_ERROR ? FPCFGA_ERR : FPCFGA_OK);
+}
+
+/*ARGSUSED*/
+static int
+do_recurse_dev(
+ const char *path,
+ const struct stat *sbuf,
+ int type,
+ struct FTW *ftwp)
+{
+ /* We want only VALID symlinks */
+ if (type != FTW_SL) {
+ return (NFTW_CONTINUE);
+ }
+
+ assert(nftw_arg.fcn != NULL);
+
+ if (nftw_arg.fcn(path, nftw_arg.arg) == FPCFGA_TERMINATE) {
+ /* terminate prematurely, but may not be error */
+ errno = 0;
+ return (NFTW_TERMINATE);
+ } else {
+ return (NFTW_CONTINUE);
+ }
+}
+
+cfga_err_t
+err_cvt(fpcfga_ret_t fp_err)
+{
+ int i;
+
+ for (i = 0; i < N_ERR_CVT_TBL; i++) {
+ if (err_cvt_tbl[i].fp_err == fp_err) {
+ return (err_cvt_tbl[i].cfga_err);
+ }
+ }
+
+ return (CFGA_ERROR);
+}
+
+/*
+ * Removes duplicate slashes from a pathname and any trailing slashes.
+ * Returns "/" if input is "/"
+ */
+char *
+pathdup(const char *path, int *l_errnop)
+{
+ int prev_was_slash = 0;
+ char c, *dp = NULL, *dup = NULL;
+ const char *sp = NULL;
+
+ *l_errnop = 0;
+
+ if (path == NULL) {
+ return (NULL);
+ }
+
+ if ((dup = calloc(1, strlen(path) + 1)) == NULL) {
+ *l_errnop = errno;
+ return (NULL);
+ }
+
+ prev_was_slash = 0;
+ for (sp = path, dp = dup; (c = *sp) != '\0'; sp++) {
+ if (!prev_was_slash || c != '/') {
+ *dp++ = c;
+ }
+ if (c == '/') {
+ prev_was_slash = 1;
+ } else {
+ prev_was_slash = 0;
+ }
+ }
+
+ /* Remove trailing slash except if it is the first char */
+ if (prev_was_slash && dp != dup && dp - 1 != dup) {
+ *(--dp) = '\0';
+ } else {
+ *dp = '\0';
+ }
+
+ return (dup);
+}
+
+fpcfga_ret_t
+apidt_create(const char *ap_id, apid_t *apidp, char **errstring)
+{
+ char *xport_phys = NULL, *dyn = NULL;
+ char *dyncomp = NULL;
+ struct luninfo_list *lunlistp = NULL;
+ int l_errno = 0;
+ size_t len = 0;
+ fpcfga_ret_t ret;
+
+ if ((xport_phys = pathdup(ap_id, &l_errno)) == NULL) {
+ cfga_err(errstring, l_errno, ERR_OP_FAILED, 0);
+ return (FPCFGA_LIB_ERR);
+ }
+
+ /* Extract the base(hba) and dynamic(device) component if any */
+ dyncomp = NULL;
+ if ((dyn = GET_DYN(xport_phys)) != NULL) {
+ len = strlen(DYN_TO_DYNCOMP(dyn)) + 1;
+ dyncomp = calloc(1, len);
+ if (dyncomp == NULL) {
+ cfga_err(errstring, errno, ERR_OP_FAILED, 0);
+ ret = FPCFGA_LIB_ERR;
+ goto err;
+ }
+ (void) strcpy(dyncomp, DYN_TO_DYNCOMP(dyn));
+ if (GET_LUN_DYN(dyncomp)) {
+ ret = FPCFGA_APID_NOEXIST;
+ goto err;
+ }
+
+ /* Remove the dynamic component from the base. */
+ *dyn = '\0';
+ }
+
+ /* Get the path of dynamic attachment point if already configured. */
+ if (dyncomp != NULL) {
+ ret = dyn_apid_to_path(xport_phys, dyncomp,
+ &lunlistp, &l_errno);
+ if ((ret != FPCFGA_OK) && (ret != FPCFGA_APID_NOCONFIGURE)) {
+ cfga_err(errstring, l_errno, ERR_OP_FAILED, 0);
+ goto err;
+ }
+ }
+
+ assert(xport_phys != NULL);
+
+ apidp->xport_phys = xport_phys;
+ apidp->dyncomp = dyncomp;
+ apidp->lunlist = lunlistp;
+ apidp->flags = 0;
+
+ return (FPCFGA_OK);
+
+err:
+ S_FREE(xport_phys);
+ S_FREE(dyncomp);
+ lunlist_free(lunlistp);
+ return (ret);
+}
+
+static void
+lunlist_free(struct luninfo_list *lunlist)
+{
+struct luninfo_list *lunp;
+
+ while (lunlist != NULL) {
+ lunp = lunlist->next;
+ S_FREE(lunlist->path);
+ S_FREE(lunlist);
+ lunlist = lunp;
+ }
+}
+
+void
+apidt_free(apid_t *apidp)
+{
+ if (apidp == NULL)
+ return;
+
+ S_FREE(apidp->xport_phys);
+ S_FREE(apidp->dyncomp);
+ lunlist_free(apidp->lunlist);
+}
+
+fpcfga_ret_t
+walk_tree(
+ const char *physpath,
+ void *arg,
+ uint_t init_flags,
+ walkarg_t *up,
+ fpcfga_cmd_t cmd,
+ int *l_errnop)
+{
+ int rv;
+ di_node_t root, tree_root, fpnode;
+ char *root_path, *cp = NULL;
+ char *devfs_fp_path;
+ size_t len;
+ fpcfga_ret_t ret;
+ int found = 0;
+
+ *l_errnop = 0;
+
+ if ((root_path = strdup(physpath)) == NULL) {
+ *l_errnop = errno;
+ return (FPCFGA_LIB_ERR);
+ }
+
+ /* Fix up path for di_init() */
+ len = strlen(DEVICES_DIR);
+ if (strncmp(root_path, DEVICES_DIR SLASH,
+ len + strlen(SLASH)) == 0) {
+ cp = root_path + len;
+ (void) memmove(root_path, cp, strlen(cp) + 1);
+ } else if (*root_path != '/') {
+ *l_errnop = 0;
+ ret = FPCFGA_ERR;
+ goto out;
+ }
+
+ /* Remove dynamic component if any */
+ if ((cp = GET_DYN(root_path)) != NULL) {
+ *cp = '\0';
+ }
+
+ /* Remove minor name if any */
+ if ((cp = strrchr(root_path, ':')) != NULL) {
+ *cp = '\0';
+ }
+
+ /*
+ * If force_flag is set
+ * do di_init with DINFOFORCE flag and get to the input fp node
+ * from the device tree.
+ *
+ * In order to get the link between path_info node and scsi_vhci node
+ * it is required to take the snapshot of the whole device tree.
+ * this behavior of libdevinfo is inefficient. For a specific
+ * fca port DINFOPROP was sufficient on the fca path prior to
+ * scsi_vhci node support.
+ *
+ */
+ if ((up->flags & FLAG_DEVINFO_FORCE) == FLAG_DEVINFO_FORCE) {
+ tree_root = di_init("/", init_flags | DINFOFORCE);
+ } else {
+ tree_root = di_init("/", init_flags);
+ }
+
+ if (tree_root == DI_NODE_NIL) {
+ *l_errnop = errno;
+ ret = FPCFGA_LIB_ERR;
+ goto out;
+ }
+
+ fpnode = di_drv_first_node("fp", tree_root);
+
+ while (fpnode) {
+ devfs_fp_path = di_devfs_path(fpnode);
+ if ((devfs_fp_path) && !(strncmp(devfs_fp_path,
+ root_path, strlen(root_path)))) {
+ found = 1;
+ di_devfs_path_free(devfs_fp_path);
+ break;
+ }
+ di_devfs_path_free(devfs_fp_path);
+ fpnode = di_drv_next_node(fpnode);
+ }
+ if (!(found)) {
+ ret = FPCFGA_LIB_ERR;
+ goto out;
+ } else {
+ root = fpnode;
+ }
+
+ /* Walk the tree */
+ errno = 0;
+ if (cmd == FPCFGA_WALK_NODE) {
+ rv = di_walk_node(root, up->walkmode.node_args.flags, arg,
+ up->walkmode.node_args.fcn);
+ } else {
+ assert(cmd == FPCFGA_WALK_MINOR);
+ rv = di_walk_minor(root, up->walkmode.minor_args.nodetype, 0,
+ arg, up->walkmode.minor_args.fcn);
+ }
+
+ if (rv != 0) {
+ *l_errnop = errno;
+ ret = FPCFGA_LIB_ERR;
+ } else {
+ if ((up->flags & FLAG_PATH_INFO_WALK) == FLAG_PATH_INFO_WALK) {
+ ret = stat_path_info_node(root, arg, l_errnop);
+ } else {
+ *l_errnop = 0;
+ ret = FPCFGA_OK;
+ }
+ }
+
+ di_fini(tree_root);
+
+ /*FALLTHRU*/
+out:
+ S_FREE(root_path);
+ return (ret);
+}
+
+
+int
+msg_idx(msgid_t msgid)
+{
+ int idx = 0;
+
+ /* The string table index and the error id may or may not be same */
+ if (msgid >= 0 && msgid <= N_STRS - 1 &&
+ str_tbl[msgid].msgid == msgid) {
+ idx = msgid;
+ } else {
+ for (idx = 0; idx < N_STRS; idx++) {
+ if (str_tbl[idx].msgid == msgid)
+ break;
+ }
+ if (idx >= N_STRS) {
+ idx = UNKNOWN_ERR_IDX;
+ }
+ }
+
+ return (idx);
+}
+
+/*
+ * cfga_err() accepts a variable number of message IDs and constructs
+ * a corresponding error string which is returned via the errstring argument.
+ * cfga_err() calls dgettext() to internationalize proper messages.
+ * May be called with a NULL argument.
+ */
+void
+cfga_err(char **errstring, int l_errno, ...)
+{
+ va_list ap;
+ int append_newline = 0;
+ char *tmp_str, *tmp_err_str = NULL;
+
+ if (errstring == NULL) {
+ return;
+ }
+
+ /*
+ * Don't append a newline, the application (for example cfgadm)
+ * should do that.
+ */
+ append_newline = 0;
+
+ va_start(ap, l_errno);
+ msg_common(&tmp_err_str, append_newline, l_errno, ap);
+ va_end(ap);
+
+ if (*errstring == NULL) {
+ *errstring = tmp_err_str;
+ return;
+ }
+
+ /*
+ * *errstring != NULL
+ * There was something in errstring prior to this call.
+ * So, concatenate the old and new strings
+ */
+ if ((tmp_str = calloc(1,
+ strlen(*errstring) + strlen(tmp_err_str) + 2)) == NULL) {
+ /* In case of error, retain only the earlier message */
+ free(tmp_err_str);
+ return;
+ }
+
+ sprintf(tmp_str, "%s\n%s", *errstring, tmp_err_str);
+ free(tmp_err_str);
+ free(*errstring);
+ *errstring = tmp_str;
+}
+
+/*
+ * This routine accepts a variable number of message IDs and constructs
+ * a corresponding message string which is printed via the message print
+ * routine argument.
+ */
+void
+cfga_msg(struct cfga_msg *msgp, ...)
+{
+ char *p = NULL;
+ int append_newline = 0, l_errno = 0;
+ va_list ap;
+
+ if (msgp == NULL || msgp->message_routine == NULL) {
+ return;
+ }
+
+ /* Append a newline after message */
+ append_newline = 1;
+ l_errno = 0;
+
+ va_start(ap, msgp);
+ msg_common(&p, append_newline, l_errno, ap);
+ va_end(ap);
+
+ (void) (*msgp->message_routine)(msgp->appdata_ptr, p);
+
+ S_FREE(p);
+}
+
+/*
+ * Get internationalized string corresponding to message id
+ * Caller must free the memory allocated.
+ */
+char *
+cfga_str(int append_newline, ...)
+{
+ char *p = NULL;
+ int l_errno = 0;
+ va_list ap;
+
+ va_start(ap, append_newline);
+ msg_common(&p, append_newline, l_errno, ap);
+ va_end(ap);
+
+ return (p);
+}
+
+static void
+msg_common(char **msgpp, int append_newline, int l_errno, va_list ap)
+{
+ int a = 0;
+ size_t len = 0;
+ int i = 0, n = 0;
+ char *s = NULL, *t = NULL;
+ strlist_t dummy;
+ strlist_t *savep = NULL, *sp = NULL, *tailp = NULL;
+
+ if (*msgpp != NULL) {
+ return;
+ }
+
+ dummy.next = NULL;
+ tailp = &dummy;
+ for (len = 0; (a = va_arg(ap, int)) != 0; ) {
+ n = GET_MSG_NARGS(a); /* 0 implies no additional args */
+ for (i = 0; i <= n; i++) {
+ sp = calloc(1, sizeof (*sp));
+ if (sp == NULL) {
+ goto out;
+ }
+ if (i == 0 && GET_MSG_INTL(a)) {
+ sp->str = dgettext(TEXT_DOMAIN, GET_MSG_STR(a));
+ } else if (i == 0) {
+ sp->str = GET_MSG_STR(a);
+ } else {
+ sp->str = va_arg(ap, char *);
+ }
+ len += (strlen(sp->str));
+ sp->next = NULL;
+ tailp->next = sp;
+ tailp = sp;
+ }
+ }
+
+ len += 1; /* terminating NULL */
+
+ s = t = NULL;
+ if (l_errno) {
+ s = dgettext(TEXT_DOMAIN, ": ");
+ t = S_STR(strerror(l_errno));
+ if (s != NULL && t != NULL) {
+ len += strlen(s) + strlen(t);
+ }
+ }
+
+ if (append_newline) {
+ len++;
+ }
+
+ if ((*msgpp = calloc(1, len)) == NULL) {
+ goto out;
+ }
+
+ **msgpp = '\0';
+ for (sp = dummy.next; sp != NULL; sp = sp->next) {
+ (void) strcat(*msgpp, sp->str);
+ }
+
+ if (s != NULL && t != NULL) {
+ (void) strcat(*msgpp, s);
+ (void) strcat(*msgpp, t);
+ }
+
+ if (append_newline) {
+ (void) strcat(*msgpp, dgettext(TEXT_DOMAIN, "\n"));
+ }
+
+ /* FALLTHROUGH */
+out:
+ sp = dummy.next;
+ while (sp != NULL) {
+ savep = sp->next;
+ S_FREE(sp);
+ sp = savep;
+ }
+}
+
+fpcfga_ret_t
+devctl_cmd(
+ const char *physpath,
+ fpcfga_cmd_t cmd,
+ uint_t *statep,
+ int *l_errnop)
+{
+ int rv = -1, i, type;
+ devctl_hdl_t hdl = NULL;
+ char *cp = NULL, *path = NULL;
+ int (*func)(const devctl_hdl_t);
+ int (*state_func)(const devctl_hdl_t, uint_t *);
+
+ *l_errnop = 0;
+
+ if (statep != NULL) *statep = 0;
+
+ func = NULL;
+ state_func = NULL;
+ type = 0;
+
+ for (i = 0; i < N_GET_STATE_CMDS; i++) {
+ if (get_state_cmds[i].cmd == cmd) {
+ state_func = get_state_cmds[i].state_fcn;
+ type = get_state_cmds[i].type;
+ assert(statep != NULL);
+ break;
+ }
+ }
+
+ if (state_func == NULL) {
+ for (i = 0; i < N_SET_STATE_CMDS; i++) {
+ if (set_state_cmds[i].cmd == cmd) {
+ func = set_state_cmds[i].fcn;
+ type = set_state_cmds[i].type;
+ assert(statep == NULL);
+ break;
+ }
+ }
+ }
+
+ assert(type == BUS_OP || type == DEV_OP);
+
+ if (func == NULL && state_func == NULL) {
+ return (FPCFGA_ERR);
+ }
+
+ /*
+ * Fix up path for calling devctl.
+ */
+ if ((path = strdup(physpath)) == NULL) {
+ *l_errnop = errno;
+ return (FPCFGA_LIB_ERR);
+ }
+
+ /* Remove dynamic component if any */
+ if ((cp = GET_DYN(path)) != NULL) {
+ *cp = '\0';
+ }
+
+ /* Remove minor name */
+ if ((cp = strrchr(path, ':')) != NULL) {
+ *cp = '\0';
+ }
+
+ errno = 0;
+
+ if (type == BUS_OP) {
+ hdl = devctl_bus_acquire(path, 0);
+ } else {
+ hdl = devctl_device_acquire(path, 0);
+ }
+ *l_errnop = errno;
+
+ S_FREE(path);
+
+ if (hdl == NULL) {
+ return (FPCFGA_ERR);
+ }
+
+ errno = 0;
+ /* Only getstate functions require a second argument */
+ if (func != NULL && statep == NULL) {
+ rv = func(hdl);
+ *l_errnop = errno;
+ } else if (state_func != NULL && statep != NULL) {
+ rv = state_func(hdl, statep);
+ *l_errnop = errno;
+ } else {
+ rv = -1;
+ *l_errnop = 0;
+ }
+
+ devctl_release(hdl);
+
+ return ((rv == -1) ? FPCFGA_ERR : FPCFGA_OK);
+}
+
+/*
+ * Is device in a known state ? (One of BUSY, ONLINE, OFFLINE)
+ * BUSY --> One or more device special files are open. Implies online
+ * ONLINE --> driver attached
+ * OFFLINE --> CF1 with offline flag set.
+ * UNKNOWN --> None of the above
+ */
+int
+known_state(di_node_t node)
+{
+ uint_t state;
+
+ state = di_state(node);
+
+ /*
+ * CF1 without offline flag set is considered unknown state.
+ * We are in a known state if either CF2 (driver attached) or
+ * offline.
+ */
+ if ((state & DI_DEVICE_OFFLINE) == DI_DEVICE_OFFLINE ||
+ (state & DI_DRIVER_DETACHED) != DI_DRIVER_DETACHED) {
+ return (1);
+ }
+
+ return (0);
+}
+
+void
+list_free(ldata_list_t **llpp)
+{
+ ldata_list_t *lp, *olp;
+
+ lp = *llpp;
+ while (lp != NULL) {
+ olp = lp;
+ lp = olp->next;
+ S_FREE(olp);
+ }
+
+ *llpp = NULL;
+}
+
+/*
+ * Obtain the devlink from a /devices path
+ */
+fpcfga_ret_t
+physpath_to_devlink(
+ const char *basedir,
+ char *xport_phys,
+ char **xport_logpp,
+ int *l_errnop,
+ int match_minor)
+{
+ pathm_t pmt = {NULL};
+ fpcfga_ret_t ret;
+
+ pmt.phys = xport_phys;
+ pmt.ret = FPCFGA_NO_REC;
+ pmt.match_minor = match_minor;
+
+ /*
+ * Search the /dev hierarchy starting at basedir.
+ */
+ ret = recurse_dev(basedir, &pmt, lookup_dev);
+ if (ret == FPCFGA_OK && (ret = pmt.ret) == FPCFGA_OK) {
+ assert(pmt.log != NULL);
+ *xport_logpp = pmt.log;
+ } else {
+ if (pmt.log != NULL) {
+ S_FREE(pmt.log);
+ }
+
+ *xport_logpp = NULL;
+ *l_errnop = pmt.l_errno;
+ }
+
+ return (ret);
+}
+
+static fpcfga_recur_t
+lookup_dev(const char *lpath, void *arg)
+{
+ char ppath[PATH_MAX];
+ pathm_t *pmtp = (pathm_t *)arg;
+
+ if (realpath(lpath, ppath) == NULL) {
+ return (FPCFGA_CONTINUE);
+ }
+
+ ppath[sizeof (ppath) - 1] = '\0';
+
+ /* Is this the physical path we are looking for */
+ if (dev_cmp(ppath, pmtp->phys, pmtp->match_minor)) {
+ return (FPCFGA_CONTINUE);
+ }
+
+ if ((pmtp->log = strdup(lpath)) == NULL) {
+ pmtp->l_errno = errno;
+ pmtp->ret = FPCFGA_LIB_ERR;
+ } else {
+ pmtp->ret = FPCFGA_OK;
+ }
+
+ return (FPCFGA_TERMINATE);
+}
+
+/* Compare HBA physical ap_id and device path */
+int
+hba_dev_cmp(const char *hba, const char *devpath)
+{
+ char *cp = NULL;
+ int rv;
+ size_t hba_len, dev_len;
+ char l_hba[MAXPATHLEN], l_dev[MAXPATHLEN];
+
+ (void) snprintf(l_hba, sizeof (l_hba), "%s", hba);
+ (void) snprintf(l_dev, sizeof (l_dev), "%s", devpath);
+
+ /* Remove dynamic component if any */
+ if ((cp = GET_DYN(l_hba)) != NULL) {
+ *cp = '\0';
+ }
+
+ if ((cp = GET_DYN(l_dev)) != NULL) {
+ *cp = '\0';
+ }
+
+
+ /* Remove minor names */
+ if ((cp = strrchr(l_hba, ':')) != NULL) {
+ *cp = '\0';
+ }
+
+ if ((cp = strrchr(l_dev, ':')) != NULL) {
+ *cp = '\0';
+ }
+
+ hba_len = strlen(l_hba);
+ dev_len = strlen(l_dev);
+
+ /* Check if HBA path is component of device path */
+ if (rv = strncmp(l_hba, l_dev, hba_len)) {
+ return (rv);
+ }
+
+ /* devpath must have '/' and 1 char in addition to hba path */
+ if (dev_len >= hba_len + 2 && l_dev[hba_len] == '/') {
+ return (0);
+ } else {
+ return (-1);
+ }
+}
+
+int
+dev_cmp(const char *dev1, const char *dev2, int match_minor)
+{
+ char l_dev1[MAXPATHLEN], l_dev2[MAXPATHLEN];
+ char *mn1, *mn2;
+ int rv;
+
+ (void) snprintf(l_dev1, sizeof (l_dev1), "%s", dev1);
+ (void) snprintf(l_dev2, sizeof (l_dev2), "%s", dev2);
+
+ if ((mn1 = GET_DYN(l_dev1)) != NULL) {
+ *mn1 = '\0';
+ }
+
+ if ((mn2 = GET_DYN(l_dev2)) != NULL) {
+ *mn2 = '\0';
+ }
+
+ /* Separate out the minor names */
+ if ((mn1 = strrchr(l_dev1, ':')) != NULL) {
+ *mn1++ = '\0';
+ }
+
+ if ((mn2 = strrchr(l_dev2, ':')) != NULL) {
+ *mn2++ = '\0';
+ }
+
+ if ((rv = strcmp(l_dev1, l_dev2)) != 0 || !match_minor) {
+ return (rv);
+ }
+
+ /*
+ * Compare minor names
+ */
+ if (mn1 == NULL && mn2 == NULL) {
+ return (0);
+ } else if (mn1 == NULL) {
+ return (-1);
+ } else if (mn2 == NULL) {
+ return (1);
+ } else {
+ return (strcmp(mn1, mn2));
+ }
+}
+
+/*
+ * Returns non-zero on failure (aka, HBA_STATUS_ERROR_*
+ * Will handle retries if applicable.
+ */
+int
+getAdapterAttrs(HBA_HANDLE handle, HBA_ADAPTERATTRIBUTES *attrs)
+{
+ int count = 0;
+ HBA_STATUS status = HBA_STATUS_ERROR_TRY_AGAIN; /* force first pass */
+
+ /* Loop as long as we have a retryable error */
+ while ((status == HBA_STATUS_ERROR_TRY_AGAIN ||
+ status == HBA_STATUS_ERROR_BUSY) &&
+ count++ < HBA_MAX_RETRIES) {
+ status = HBA_GetAdapterAttributes(handle, attrs);
+ if (status == HBA_STATUS_OK) {
+ break;
+ }
+ sleep(1);
+ }
+ return (status);
+}
+
+/*
+ * Returns non-zero on failure (aka, HBA_STATUS_ERROR_*
+ * Will handle retries if applicable.
+ */
+int
+getPortAttrsByWWN(HBA_HANDLE handle, HBA_WWN wwn, HBA_PORTATTRIBUTES *attrs)
+{
+ int count = 0;
+ HBA_STATUS status = HBA_STATUS_ERROR_TRY_AGAIN; /* force first pass */
+
+ /* Loop as long as we have a retryable error */
+ while ((status == HBA_STATUS_ERROR_TRY_AGAIN ||
+ status == HBA_STATUS_ERROR_BUSY) &&
+ count++ < HBA_MAX_RETRIES) {
+ status = HBA_GetPortAttributesByWWN(handle, wwn, attrs);
+ if (status == HBA_STATUS_OK) {
+ break;
+ }
+
+ /* The odds of this occuring are very slim, but possible. */
+ if (status == HBA_STATUS_ERROR_STALE_DATA) {
+ /*
+ * If we hit a stale data scenario,
+ * we'll just tell the user to try again.
+ */
+ status = HBA_STATUS_ERROR_TRY_AGAIN;
+ break;
+ }
+ sleep(1);
+ }
+ return (status);
+}
+
+/*
+ * Returns non-zero on failure (aka, HBA_STATUS_ERROR_*
+ * Will handle retries if applicable.
+ */
+int
+getAdapterPortAttrs(HBA_HANDLE handle, int portIndex,
+ HBA_PORTATTRIBUTES *attrs)
+{
+ int count = 0;
+ HBA_STATUS status = HBA_STATUS_ERROR_TRY_AGAIN; /* force first pass */
+
+ /* Loop as long as we have a retryable error */
+ while ((status == HBA_STATUS_ERROR_TRY_AGAIN ||
+ status == HBA_STATUS_ERROR_BUSY) &&
+ count++ < HBA_MAX_RETRIES) {
+ status = HBA_GetAdapterPortAttributes(handle, portIndex, attrs);
+ if (status == HBA_STATUS_OK) {
+ break;
+ }
+
+ /* The odds of this occuring are very slim, but possible. */
+ if (status == HBA_STATUS_ERROR_STALE_DATA) {
+ /*
+ * If we hit a stale data scenario,
+ * we'll just tell the user to try again.
+ */
+ status = HBA_STATUS_ERROR_TRY_AGAIN;
+ break;
+ }
+ sleep(1);
+ }
+ return (status);
+}
+
+/*
+ * Returns non-zero on failure (aka, HBA_STATUS_ERROR_*
+ * Will handle retries if applicable.
+ */
+int
+getDiscPortAttrs(HBA_HANDLE handle, int portIndex, int discIndex,
+ HBA_PORTATTRIBUTES *attrs)
+{
+ int count = 0;
+ HBA_STATUS status = HBA_STATUS_ERROR_TRY_AGAIN; /* force first pass */
+
+ /* Loop as long as we have a retryable error */
+ while ((status == HBA_STATUS_ERROR_TRY_AGAIN ||
+ status == HBA_STATUS_ERROR_BUSY) &&
+ count++ < HBA_MAX_RETRIES) {
+ status = HBA_GetDiscoveredPortAttributes(handle, portIndex,
+ discIndex, attrs);
+ if (status == HBA_STATUS_OK) {
+ break;
+ }
+
+ /* The odds of this occuring are very slim, but possible. */
+ if (status == HBA_STATUS_ERROR_STALE_DATA) {
+ /*
+ * If we hit a stale data scenario, we'll just tell the
+ * user to try again.
+ */
+ status = HBA_STATUS_ERROR_TRY_AGAIN;
+ break;
+ }
+ sleep(1);
+ }
+ return (status);
+}
+
+/*
+ * Find the Adapter port that matches the portPath.
+ * When the matching port is found the caller have to close handle
+ * and free library.
+ */
+fpcfga_ret_t
+findMatchingAdapterPort(char *portPath, HBA_HANDLE *matchingHandle,
+ int *matchingPortIndex, HBA_PORTATTRIBUTES *matchingPortAttrs,
+ char **errstring)
+{
+ HBA_HANDLE handle;
+ HBA_ADAPTERATTRIBUTES hbaAttrs;
+ HBA_PORTATTRIBUTES portAttrs;
+ HBA_STATUS status = HBA_STATUS_OK;
+ int count, retry = 0, l_errno = 0;
+ int adapterIndex, portIndex;
+ char adapterName[256];
+ char *cfg_ptr, *tmpPtr;
+ char *logical_apid = NULL;
+
+ status = HBA_LoadLibrary();
+ if (status != HBA_STATUS_OK) {
+ cfga_err(errstring, 0, ERR_HBA_LOAD_LIBRARY, 0);
+ return (FPCFGA_LIB_ERR);
+ }
+ count = HBA_GetNumberOfAdapters();
+ if (count == 0) {
+ cfga_err(errstring, 0, ERR_NO_ADAPTER_FOUND, 0);
+ HBA_FreeLibrary();
+ return (FPCFGA_LIB_ERR);
+ }
+
+ /* Loop over all HBAs */
+ for (adapterIndex = 0; adapterIndex < count; adapterIndex ++) {
+ status = HBA_GetAdapterName(adapterIndex, (char *)&adapterName);
+ if (status != HBA_STATUS_OK) {
+ /* May have been DR'd */
+ continue;
+ }
+ handle = HBA_OpenAdapter(adapterName);
+ if (handle == 0) {
+ /* May have been DR'd */
+ continue;
+ }
+
+ do {
+ if (getAdapterAttrs(handle, &hbaAttrs)) {
+ /* Should never happen */
+ HBA_CloseAdapter(handle);
+ continue;
+ }
+
+ /* Loop over all HBA Ports */
+ for (portIndex = 0;
+ portIndex < hbaAttrs.NumberOfPorts; portIndex++) {
+ if ((status = getAdapterPortAttrs(handle, portIndex,
+ &portAttrs)) != HBA_STATUS_OK) {
+ /* Need to refresh adapter */
+ if (status == HBA_STATUS_ERROR_STALE_DATA) {
+ HBA_RefreshInformation(handle);
+ break;
+ } else {
+ continue;
+ }
+ }
+
+ /*
+ * check to see if OSDeviceName is a /dev/cfg link
+ * or the physical path
+ */
+ if (strncmp(portAttrs.OSDeviceName, CFGA_DEV_DIR,
+ strlen(CFGA_DEV_DIR)) != 0) {
+ tmpPtr = strstr(portAttrs.OSDeviceName, MINOR_SEP);
+ if (tmpPtr != NULL) {
+ if (strncmp(portPath,
+ portAttrs.OSDeviceName,
+ strlen(portAttrs.OSDeviceName) -
+ strlen(tmpPtr)) == 0) {
+ if (matchingHandle)
+ *matchingHandle = handle;
+ if (matchingPortIndex)
+ *matchingPortIndex = portIndex;
+ if (matchingPortAttrs)
+ *matchingPortAttrs = portAttrs;
+ return (FPCFGA_OK);
+ }
+ }
+ } else {
+ /*
+ * strip off the /dev/cfg/ portion of the
+ * OSDeviceName
+ * make sure that the OSDeviceName is at least
+ * strlen("/dev/cfg") + 1 + 1 long.
+ * first 1 is for the / after /dev/cfg
+ * second 1 is to make sure there is somthing
+ * after
+ */
+ if (strlen(portAttrs.OSDeviceName) <
+ (strlen(CFGA_DEV_DIR) + 1 + 1))
+ continue;
+ cfg_ptr = portAttrs.OSDeviceName +
+ strlen(CFGA_DEV_DIR) + 1;
+ if (logical_apid == NULL) {
+ /* get the /dev/cfg link from the portPath */
+ if (make_xport_logid(portPath, &logical_apid,
+ &l_errno) != FPCFGA_OK) {
+ cfga_err(errstring, l_errno,
+ ERR_LIST, 0);
+ HBA_FreeLibrary();
+ return (FPCFGA_LIB_ERR);
+ }
+ }
+ /* compare logical ap_id */
+ if (strcmp(logical_apid, cfg_ptr) == 0) {
+ if (matchingHandle)
+ *matchingHandle = handle;
+ if (matchingPortIndex)
+ *matchingPortIndex = portIndex;
+ if (matchingPortAttrs)
+ *matchingPortAttrs = portAttrs;
+ S_FREE(logical_apid);
+ return (FPCFGA_OK);
+ }
+ }
+ }
+ if (logical_apid != NULL)
+ S_FREE(logical_apid);
+ } while ((status == HBA_STATUS_ERROR_STALE_DATA) &&
+ (retry++ < HBA_MAX_RETRIES));
+
+ HBA_CloseAdapter(handle);
+ }
+ free(logical_apid);
+
+ /* Got here. No mathcing adatper port found. */
+ cfga_err(errstring, 0, ERR_MATCHING_HBA_PORT, 0);
+ HBA_FreeLibrary();
+ return (FPCFGA_LIB_ERR);
+}
diff --git a/usr/src/lib/cfgadm_plugins/fp/common/devices-fc-fabric.xml b/usr/src/lib/cfgadm_plugins/fp/common/devices-fc-fabric.xml
new file mode 100644
index 0000000000..4d9b91031f
--- /dev/null
+++ b/usr/src/lib/cfgadm_plugins/fp/common/devices-fc-fabric.xml
@@ -0,0 +1,89 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<!--
+
+CDDL HEADER START
+
+The contents of this file are subject to the terms of the
+Common Development and Distribution License (the "License").
+You may not use this file except in compliance with the License.
+
+You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+or http://www.opensolaris.org/os/licensing.
+See the License for the specific language governing permissions
+and limitations under the License.
+
+When distributing Covered Code, include this CDDL HEADER in each
+file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+If applicable, add the following below this CDDL HEADER, with the
+fields enclosed by brackets "[]" replaced with your own identifying
+information: Portions Copyright [yyyy] [name of copyright owner]
+
+CDDL HEADER END
+
+Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+Use is subject to license terms.
+
+
+-->
+
+<service_bundle type='manifest' name='devices-fc-fabric'>
+
+<service
+ name='system/device/fc-fabric'
+ type='service'
+ version='1'>
+
+ <create_default_instance enabled='true' />
+
+ <single_instance/>
+
+ <dependency
+ name='sysevent'
+ grouping='require_all'
+ restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/system/sysevent' />
+ </dependency>
+
+ <dependent
+ name='devices'
+ grouping='require_all'
+ restart_on='none'>
+ <service_fmri value='svc:/milestone/devices' />
+ </dependent>
+
+ <exec_method
+ type='method'
+ name='start'
+ exec='/lib/svc/method/fc-fabric'
+ timeout_seconds='600' />
+
+ <exec_method
+ type='method'
+ name='stop'
+ exec=':true'
+ timeout_seconds='3' />
+
+ <property_group name='startd' type='framework'>
+ <propval name='duration' type='astring'
+ value='transient' />
+ </property_group>
+
+ <stability value='Unstable' />
+
+ <template>
+ <common_name>
+ <loctext xml:lang='C'>
+ Solaris FC fabric device configuration.
+ </loctext>
+ </common_name>
+ <documentation>
+ <manpage title='cfgadm_fp' section='1M'
+ manpath='/usr/share/man' />
+ </documentation>
+ </template>
+</service>
+
+</service_bundle>
+
diff --git a/usr/src/lib/cfgadm_plugins/fp/common/fc-fabric b/usr/src/lib/cfgadm_plugins/fp/common/fc-fabric
new file mode 100644
index 0000000000..3b0a475563
--- /dev/null
+++ b/usr/src/lib/cfgadm_plugins/fp/common/fc-fabric
@@ -0,0 +1,45 @@
+#!/sbin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#
+# This script is used to reconfigure fabric device(s) which were configured
+# prior to boot. The luxadm command used below is an expert level command
+# and has a restrictive usage for boot time reconfiguration of fabric
+# devices.
+#
+# DO NOT USE the command for any other purpose.
+#
+
+if [ -s /etc/cfg/fp/fabric_WWN_map ]
+then
+ /usr/sbin/luxadm -e create_fabric_device \
+ -f /etc/cfg/fp/fabric_WWN_map
+else
+ if [ -s /etc/cfg/fp/fabric_WWN_map.old ]
+ then
+ /usr/sbin/luxadm -e create_fabric_device \
+ -f /etc/cfg/fp/fabric_WWN_map.old
+ fi
+fi
diff --git a/usr/src/lib/cfgadm_plugins/fp/common/mapfile-vers b/usr/src/lib/cfgadm_plugins/fp/common/mapfile-vers
new file mode 100644
index 0000000000..4dd3e93023
--- /dev/null
+++ b/usr/src/lib/cfgadm_plugins/fp/common/mapfile-vers
@@ -0,0 +1,39 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#
+
+SUNWprivate_1.1 {
+ global:
+ cfga_ap_id_cmp;
+ cfga_change_state;
+ cfga_help;
+ cfga_list_ext;
+ cfga_private_func;
+ cfga_test;
+ cfga_version;
+
+ local:
+ *;
+};
diff --git a/usr/src/lib/cfgadm_plugins/fp/fp.xcl b/usr/src/lib/cfgadm_plugins/fp/fp.xcl
new file mode 100644
index 0000000000..6748a7325d
--- /dev/null
+++ b/usr/src/lib/cfgadm_plugins/fp/fp.xcl
@@ -0,0 +1,47 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#
+# lib/cfgadm_plugins/fp/fp.xcl
+#
+msgid "%s%s"
+msgid "\n"
+msgid " "
+msgid " "
+msgid "-"
+msgid "%%-%ds %%-%ds"
+msgid "insert_device"
+msgid "remove_device"
+msgid "replace_device"
+msgid "reset_device"
+msgid "reset_bus"
+msgid "reset_all"
+msgid "\t-c configure -o force_update ap_id [ap_id..]\n"
+ "\t-c configure -o no_update ap_id [ap_id...]\n"
+ "\t-c unconfigure -o force_update ap_id [ap_id... ]\n"
+ "\t-c unconfigure -o no_update ap_id [ap_id... ]\n"
+msgid "/"
+msgid "fp"
+msgid ": "
+msgid "%s"
diff --git a/usr/src/lib/cfgadm_plugins/fp/i386/Makefile b/usr/src/lib/cfgadm_plugins/fp/i386/Makefile
new file mode 100644
index 0000000000..de6709e39c
--- /dev/null
+++ b/usr/src/lib/cfgadm_plugins/fp/i386/Makefile
@@ -0,0 +1,30 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+
+.KEEP_STATE:
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTMANIFEST) $(ROOTSVCMETHOD)
diff --git a/usr/src/lib/cfgadm_plugins/fp/sparc/Makefile b/usr/src/lib/cfgadm_plugins/fp/sparc/Makefile
new file mode 100644
index 0000000000..272e508d0f
--- /dev/null
+++ b/usr/src/lib/cfgadm_plugins/fp/sparc/Makefile
@@ -0,0 +1,31 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#
+
+include ../Makefile.com
+
+.KEEP_STATE:
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTMANIFEST) $(ROOTSVCMETHOD)
diff --git a/usr/src/lib/cfgadm_plugins/fp/sparcv9/Makefile b/usr/src/lib/cfgadm_plugins/fp/sparcv9/Makefile
new file mode 100644
index 0000000000..23dfadaca8
--- /dev/null
+++ b/usr/src/lib/cfgadm_plugins/fp/sparcv9/Makefile
@@ -0,0 +1,33 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#
+
+include ../Makefile.com
+include ../../../Makefile.lib.64
+
+
+.KEEP_STATE:
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64)