2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A */
2N/A
2N/A
2N/A#include "cfga_fp.h"
2N/A
2N/A/*
2N/A * This file contains helper routines for the FP plugin
2N/A */
2N/A
2N/A#if !defined(TEXT_DOMAIN)
2N/A#define TEXT_DOMAIN "SYS_TEST"
2N/A#endif
2N/A
2N/Atypedef struct strlist {
2N/A const char *str;
2N/A struct strlist *next;
2N/A} strlist_t;
2N/A
2N/Atypedef struct {
2N/A fpcfga_ret_t fp_err;
2N/A cfga_err_t cfga_err;
2N/A} errcvt_t;
2N/A
2N/Atypedef struct {
2N/A fpcfga_cmd_t cmd;
2N/A int type;
2N/A int (*fcn)(const devctl_hdl_t);
2N/A} set_state_cmd_t;
2N/A
2N/Atypedef struct {
2N/A fpcfga_cmd_t cmd;
2N/A int type;
2N/A int (*state_fcn)(const devctl_hdl_t, uint_t *);
2N/A} get_state_cmd_t;
2N/A
2N/A/* defines for nftw() */
2N/A#define NFTW_DEPTH 1
2N/A#define NFTW_CONTINUE 0
2N/A#define NFTW_TERMINATE 1
2N/A#define NFTW_ERROR -1
2N/A#define MAX_RETRIES 10
2N/A
2N/A/* Function prototypes */
2N/Astatic int do_recurse_dev(const char *path, const struct stat *sbuf,
2N/A int type, struct FTW *ftwp);
2N/Astatic fpcfga_recur_t lookup_dev(const char *lpath, void *arg);
2N/Astatic void msg_common(char **err_msgpp, int append_newline, int l_errno,
2N/A va_list ap);
2N/Astatic void lunlist_free(struct luninfo_list *lunlist);
2N/A
2N/A/* Globals */
2N/Astruct {
2N/A mutex_t mp;
2N/A void *arg;
2N/A fpcfga_recur_t (*fcn)(const char *lpath, void *arg);
2N/A} nftw_arg = {DEFAULTMUTEX};
2N/A
2N/A/*
2N/A * The string table contains most of the strings used by the fp cfgadm plugin.
2N/A * All strings which are to be internationalized must be in this table.
2N/A * Some strings which are not internationalized are also included here.
2N/A * Arguments to messages are NOT internationalized.
2N/A */
2N/Amsgcvt_t str_tbl[] = {
2N/A
2N/A/*
2N/A * The first element (ERR_UNKNOWN) MUST always be present in the array.
2N/A */
2N/A#define UNKNOWN_ERR_IDX 0 /* Keep the index in sync */
2N/A
2N/A
2N/A/* msg_code num_args, I18N msg_string */
2N/A
2N/A/* ERRORS */
2N/A{ERR_UNKNOWN, 0, 1, "unknown error"},
2N/A{ERR_OP_FAILED, 0, 1, "operation failed"},
2N/A{ERR_CMD_INVAL, 0, 1, "invalid command"},
2N/A{ERR_NOT_BUSAPID, 0, 1, "not a FP bus apid"},
2N/A{ERR_APID_INVAL, 0, 1, "invalid FP ap_id"},
2N/A{ERR_NOT_BUSOP, 0, 1, "operation not supported for FC bus"},
2N/A{ERR_NOT_DEVOP, 0, 1, "operation not supported for FC device"},
2N/A{ERR_UNAVAILABLE, 0, 1, "unavailable"},
2N/A{ERR_CTRLR_CRIT, 0, 1, "critical partition controlled by FC HBA"},
2N/A{ERR_BUS_GETSTATE, 0, 1, "failed to get state for FC bus"},
2N/A{ERR_BUS_NOTCONNECTED, 0, 1, "FC bus not connected"},
2N/A{ERR_BUS_CONNECTED, 0, 1, "FC bus not disconnected"},
2N/A{ERR_BUS_QUIESCE, 0, 1, "FC bus quiesce failed"},
2N/A{ERR_BUS_UNQUIESCE, 0, 1, "FC bus unquiesce failed"},
2N/A{ERR_BUS_CONFIGURE, 0, 1, "failed to configure devices on FC bus"},
2N/A{ERR_BUS_UNCONFIGURE, 0, 1, "failed to unconfigure FC bus"},
2N/A{ERR_DEV_CONFIGURE, 0, 1, "failed to configure FC device"},
2N/A{ERR_DEV_UNCONFIGURE, 0, 1, "failed to unconfigure FC device"},
2N/A{ERR_FCA_CONFIGURE, 0, 1, "failed to configure ANY device on FCA port"},
2N/A{ERR_FCA_UNCONFIGURE, 0, 1, "failed to unconfigure ANY device on FCA port"},
2N/A{ERR_DEV_REPLACE, 0, 1, "replace operation failed"},
2N/A{ERR_DEV_INSERT, 0, 1, "insert operation failed"},
2N/A{ERR_DEV_GETSTATE, 0, 1, "failed to get state for FC device"},
2N/A{ERR_RESET, 0, 1, "reset failed"},
2N/A{ERR_LIST, 0, 1, "list operation failed"},
2N/A{ERR_SIG_STATE, 0, 1, "could not restore signal disposition"},
2N/A{ERR_MAYBE_BUSY, 0, 1, "device may be busy"},
2N/A{ERR_BUS_DEV_MISMATCH, 0, 1, "mismatched FC bus and device"},
2N/A{ERR_MEM_ALLOC, 0, 1, "Failed to allocated memory"},
2N/A{ERR_DEVCTL_OFFLINE, 0, 1, "failed to offline device"},
2N/A{ERR_UPD_REP, 0, 1, "Repository update failed"},
2N/A{ERR_CONF_OK_UPD_REP, 0, 1,
2N/A "Configuration successful, but Repository update failed"},
2N/A{ERR_UNCONF_OK_UPD_REP, 0, 1,
2N/A "Unconfiguration successful, but Repository update failed"},
2N/A{ERR_PARTIAL_SUCCESS, 0, 1,
2N/A "Operation partially successful. Some failures seen"},
2N/A{ERR_HBA_LOAD_LIBRARY, 0, 1,
2N/A "HBA load library failed"},
2N/A{ERR_MATCHING_HBA_PORT, 0, 1,
2N/A "No match HBA port found"},
2N/A{ERR_NO_ADAPTER_FOUND, 0, 1,
2N/A "No Fibre Channel adpaters found"},
2N/A
2N/A/* Errors with arguments */
2N/A{ERRARG_OPT_INVAL, 1, 1, "invalid option: "},
2N/A{ERRARG_HWCMD_INVAL, 1, 1, "invalid command: "},
2N/A{ERRARG_DEVINFO, 1, 1, "libdevinfo failed on path: "},
2N/A{ERRARG_NOT_IN_DEVLIST, 1, 1, "Device not found in fabric device list: "},
2N/A{ERRARG_NOT_IN_DEVINFO, 1, 1, "Could not find entry in devinfo tree: "},
2N/A{ERRARG_DI_GET_PROP, 1, 1, "Could not get libdevinfo property: "},
2N/A{ERRARG_DC_DDEF_ALLOC, 1, 1, "failed to alloc ddef space: "},
2N/A{ERRARG_DC_BYTE_ARRAY, 1, 1, "failed to add property: "},
2N/A{ERRARG_DC_BUS_ACQUIRE, 1, 1, "failed to acquire bus handle: "},
2N/A{ERRARG_BUS_DEV_CREATE, 1, 1, "failed to create device node: "},
2N/A{ERRARG_BUS_DEV_CREATE_UNKNOWN, 1, 1,
2N/A "failed to create device node... Device may be unconfigurable: "},
2N/A{ERRARG_DEV_ACQUIRE, 1, 1, "device acquire operation failed: "},
2N/A{ERRARG_DEV_REMOVE, 1, 1, "remove operation failed: "},
2N/A
2N/A/* Fibre Channel operation Errors */
2N/A{ERR_FC, 0, 1, "FC error"},
2N/A{ERR_FC_GET_DEVLIST, 0, 1, "Failed to get fabric device list"},
2N/A{ERR_FC_GET_NEXT_DEV, 0, 1, "Failed to get next device on device map"},
2N/A{ERR_FC_GET_FIRST_DEV, 0, 1, "Failed to get first device on device map"},
2N/A{ERRARG_FC_DEV_MAP_INIT, 1, 1,
2N/A "Failed to initialize device map for: "},
2N/A{ERRARG_FC_PROP_LOOKUP_BYTES, 1, 1, "Failed to get property of "},
2N/A{ERRARG_FC_INQUIRY, 1, 1, "inquiry failed: "},
2N/A{ERRARG_FC_REP_LUNS, 1, 1, "report LUNs failed: "},
2N/A{ERRARG_FC_TOPOLOGY, 1, 1, "failed to get port topology: "},
2N/A{ERRARG_PATH_TOO_LONG, 1, 1, "Path length exceeds max possible: "},
2N/A{ERRARG_INVALID_PATH, 1, 1, "Invalid path: "},
2N/A{ERRARG_OPENDIR, 1, 1, "failure opening directory: "},
2N/A
2N/A/* MPXIO Errors */
2N/A{ERRARG_VHCI_GET_PATHLIST, 1, 1, "failed to get path list from vHCI: "},
2N/A{ERRARG_XPORT_NOT_IN_PHCI_LIST, 1, 1, "Transport not in pHCI list: "},
2N/A
2N/A/* RCM Errors */
2N/A{ERR_RCM_HANDLE, 0, 1, "cannot get RCM handle"},
2N/A{ERRARG_RCM_SUSPEND, 1, 1, "failed to suspend: "},
2N/A{ERRARG_RCM_RESUME, 1, 1, "failed to resume: "},
2N/A{ERRARG_RCM_OFFLINE, 1, 1, "failed to offline: "},
2N/A{ERRARG_RCM_ONLINE, 1, 1, "failed to online: "},
2N/A{ERRARG_RCM_REMOVE, 1, 1, "failed to remove: "},
2N/A{ERRARG_RCM_INFO, 1, 1, "failed to query: "},
2N/A
2N/A/* Commands */
2N/A{CMD_INSERT_DEV, 0, 0, "insert_device"},
2N/A{CMD_REMOVE_DEV, 0, 0, "remove_device"},
2N/A{CMD_REPLACE_DEV, 0, 0, "replace_device"},
2N/A{CMD_RESET_DEV, 0, 0, "reset_device"},
2N/A{CMD_RESET_BUS, 0, 0, "reset_bus"},
2N/A{CMD_RESET_ALL, 0, 0, "reset_all"},
2N/A
2N/A/* help messages */
2N/A{MSG_HELP_HDR, 0, 1, "\nfp attachment point specific options:\n"},
2N/A{MSG_HELP_USAGE, 0, 0,
2N/A "\t-c configure -o force_update ap_id [ap_id..]\n"
2N/A "\t-c configure -o no_update ap_id [ap_id...]\n"
2N/A "\t-c unconfigure -o force_update ap_id [ap_id... ]\n"
2N/A "\t-c unconfigure -o no_update ap_id [ap_id... ]\n"},
2N/A
2N/A/* hotplug messages */
2N/A{MSG_INSDEV, 1, 1, "Adding device to FC HBA: "},
2N/A{MSG_RMDEV, 1, 1, "Removing FC device: "},
2N/A{MSG_REPLDEV, 1, 1, "Replacing FC device: "},
2N/A
2N/A/* Hotplugging confirmation prompts */
2N/A{CONF_QUIESCE_1, 1, 1,
2N/A "This operation will suspend activity on FC bus: "},
2N/A
2N/A{CONF_QUIESCE_2, 0, 1, "\nContinue"},
2N/A
2N/A{CONF_UNQUIESCE, 0, 1,
2N/A "FC bus quiesced successfully.\n"
2N/A "It is now safe to proceed with hotplug operation."
2N/A "\nEnter y if operation is complete or n to abort"},
2N/A
2N/A/* Misc. */
2N/A{WARN_DISCONNECT, 0, 1,
2N/A "WARNING: Disconnecting critical partitions may cause system hang."
2N/A "\nContinue"}
2N/A};
2N/A
2N/A
2N/A#define N_STRS (sizeof (str_tbl) / sizeof (str_tbl[0]))
2N/A
2N/A#define GET_MSG_NARGS(i) (str_tbl[msg_idx(i)].nargs)
2N/A#define GET_MSG_INTL(i) (str_tbl[msg_idx(i)].intl)
2N/A
2N/Astatic errcvt_t err_cvt_tbl[] = {
2N/A { FPCFGA_OK, CFGA_OK },
2N/A { FPCFGA_LIB_ERR, CFGA_LIB_ERROR },
2N/A { FPCFGA_APID_NOEXIST, CFGA_APID_NOEXIST },
2N/A { FPCFGA_NACK, CFGA_NACK },
2N/A { FPCFGA_BUSY, CFGA_BUSY },
2N/A { FPCFGA_SYSTEM_BUSY, CFGA_SYSTEM_BUSY },
2N/A { FPCFGA_OPNOTSUPP, CFGA_OPNOTSUPP },
2N/A { FPCFGA_PRIV, CFGA_PRIV },
2N/A { FPCFGA_UNKNOWN_ERR, CFGA_ERROR },
2N/A { FPCFGA_ERR, CFGA_ERROR }
2N/A};
2N/A
2N/A#define N_ERR_CVT_TBL (sizeof (err_cvt_tbl)/sizeof (err_cvt_tbl[0]))
2N/A
2N/A#define DEV_OP 0
2N/A#define BUS_OP 1
2N/Astatic set_state_cmd_t set_state_cmds[] = {
2N/A
2N/A{ FPCFGA_BUS_QUIESCE, BUS_OP, devctl_bus_quiesce },
2N/A{ FPCFGA_BUS_UNQUIESCE, BUS_OP, devctl_bus_unquiesce },
2N/A{ FPCFGA_BUS_CONFIGURE, BUS_OP, devctl_bus_configure },
2N/A{ FPCFGA_BUS_UNCONFIGURE, BUS_OP, devctl_bus_unconfigure },
2N/A{ FPCFGA_RESET_BUS, BUS_OP, devctl_bus_reset },
2N/A{ FPCFGA_RESET_ALL, BUS_OP, devctl_bus_resetall },
2N/A{ FPCFGA_DEV_CONFIGURE, DEV_OP, devctl_device_online },
2N/A{ FPCFGA_DEV_UNCONFIGURE, DEV_OP, devctl_device_offline },
2N/A{ FPCFGA_DEV_REMOVE, DEV_OP, devctl_device_remove },
2N/A{ FPCFGA_RESET_DEV, DEV_OP, devctl_device_reset }
2N/A
2N/A};
2N/A
2N/A#define N_SET_STATE_CMDS (sizeof (set_state_cmds)/sizeof (set_state_cmds[0]))
2N/A
2N/Astatic get_state_cmd_t get_state_cmds[] = {
2N/A{ FPCFGA_BUS_GETSTATE, BUS_OP, devctl_bus_getstate },
2N/A{ FPCFGA_DEV_GETSTATE, DEV_OP, devctl_device_getstate }
2N/A};
2N/A
2N/A#define N_GET_STATE_CMDS (sizeof (get_state_cmds)/sizeof (get_state_cmds[0]))
2N/A
2N/A/* Order is important. Earlier directories are searched first */
2N/Astatic const char *dev_dir_hints[] = {
2N/A CFGA_DEV_DIR,
2N/A DEV_RMT,
2N/A DEV_DSK,
2N/A DEV_RDSK,
2N/A DEV_DIR
2N/A};
2N/A
2N/A#define N_DEV_DIR_HINTS (sizeof (dev_dir_hints) / sizeof (dev_dir_hints[0]))
2N/A
2N/A
2N/A/*
2N/A * Routine to search the /dev directory or a subtree of /dev.
2N/A * If the entire /dev hierarchy is to be searched, the most likely directories
2N/A * are searched first.
2N/A */
2N/Afpcfga_ret_t
2N/Arecurse_dev(
2N/A const char *basedir,
2N/A void *arg,
2N/A fpcfga_recur_t (*fcn)(const char *lpath, void *arg))
2N/A{
2N/A int i, rv = NFTW_ERROR;
2N/A
2N/A (void) mutex_lock(&nftw_arg.mp);
2N/A
2N/A nftw_arg.arg = arg;
2N/A nftw_arg.fcn = fcn;
2N/A
2N/A if (strcmp(basedir, DEV_DIR)) {
2N/A errno = 0;
2N/A rv = nftw(basedir, do_recurse_dev, NFTW_DEPTH, FTW_PHYS);
2N/A goto out;
2N/A }
2N/A
2N/A /*
2N/A * Search certain selected subdirectories first if basedir == "/dev".
2N/A * Ignore errors as some of these directories may not exist.
2N/A */
2N/A for (i = 0; i < N_DEV_DIR_HINTS; i++) {
2N/A errno = 0;
2N/A if ((rv = nftw(dev_dir_hints[i], do_recurse_dev, NFTW_DEPTH,
2N/A FTW_PHYS)) == NFTW_TERMINATE) {
2N/A break;
2N/A }
2N/A }
2N/A
2N/A /*FALLTHRU*/
2N/Aout:
2N/A (void) mutex_unlock(&nftw_arg.mp);
2N/A return (rv == NFTW_ERROR ? FPCFGA_ERR : FPCFGA_OK);
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Astatic int
2N/Ado_recurse_dev(
2N/A const char *path,
2N/A const struct stat *sbuf,
2N/A int type,
2N/A struct FTW *ftwp)
2N/A{
2N/A /* We want only VALID symlinks */
2N/A if (type != FTW_SL) {
2N/A return (NFTW_CONTINUE);
2N/A }
2N/A
2N/A assert(nftw_arg.fcn != NULL);
2N/A
2N/A if (nftw_arg.fcn(path, nftw_arg.arg) == FPCFGA_TERMINATE) {
2N/A /* terminate prematurely, but may not be error */
2N/A errno = 0;
2N/A return (NFTW_TERMINATE);
2N/A } else {
2N/A return (NFTW_CONTINUE);
2N/A }
2N/A}
2N/A
2N/Acfga_err_t
2N/Aerr_cvt(fpcfga_ret_t fp_err)
2N/A{
2N/A int i;
2N/A
2N/A for (i = 0; i < N_ERR_CVT_TBL; i++) {
2N/A if (err_cvt_tbl[i].fp_err == fp_err) {
2N/A return (err_cvt_tbl[i].cfga_err);
2N/A }
2N/A }
2N/A
2N/A return (CFGA_ERROR);
2N/A}
2N/A
2N/A/*
2N/A * Removes duplicate slashes from a pathname and any trailing slashes.
2N/A * Returns "/" if input is "/"
2N/A */
2N/Achar *
2N/Apathdup(const char *path, int *l_errnop)
2N/A{
2N/A int prev_was_slash = 0;
2N/A char c, *dp = NULL, *dup = NULL;
2N/A const char *sp = NULL;
2N/A
2N/A *l_errnop = 0;
2N/A
2N/A if (path == NULL) {
2N/A return (NULL);
2N/A }
2N/A
2N/A if ((dup = calloc(1, strlen(path) + 1)) == NULL) {
2N/A *l_errnop = errno;
2N/A return (NULL);
2N/A }
2N/A
2N/A prev_was_slash = 0;
2N/A for (sp = path, dp = dup; (c = *sp) != '\0'; sp++) {
2N/A if (!prev_was_slash || c != '/') {
2N/A *dp++ = c;
2N/A }
2N/A if (c == '/') {
2N/A prev_was_slash = 1;
2N/A } else {
2N/A prev_was_slash = 0;
2N/A }
2N/A }
2N/A
2N/A /* Remove trailing slash except if it is the first char */
2N/A if (prev_was_slash && dp != dup && dp - 1 != dup) {
2N/A *(--dp) = '\0';
2N/A } else {
2N/A *dp = '\0';
2N/A }
2N/A
2N/A return (dup);
2N/A}
2N/A
2N/Afpcfga_ret_t
2N/Aapidt_create(const char *ap_id, apid_t *apidp, char **errstring)
2N/A{
2N/A char *xport_phys = NULL, *dyn = NULL;
2N/A char *dyncomp = NULL;
2N/A struct luninfo_list *lunlistp = NULL;
2N/A int l_errno = 0;
2N/A size_t len = 0;
2N/A fpcfga_ret_t ret;
2N/A
2N/A if ((xport_phys = pathdup(ap_id, &l_errno)) == NULL) {
2N/A cfga_err(errstring, l_errno, ERR_OP_FAILED, 0);
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A
2N/A /* Extract the base(hba) and dynamic(device) component if any */
2N/A dyncomp = NULL;
2N/A if ((dyn = GET_DYN(xport_phys)) != NULL) {
2N/A len = strlen(DYN_TO_DYNCOMP(dyn)) + 1;
2N/A dyncomp = calloc(1, len);
2N/A if (dyncomp == NULL) {
2N/A cfga_err(errstring, errno, ERR_OP_FAILED, 0);
2N/A ret = FPCFGA_LIB_ERR;
2N/A goto err;
2N/A }
2N/A (void) strcpy(dyncomp, DYN_TO_DYNCOMP(dyn));
2N/A if (GET_LUN_DYN(dyncomp)) {
2N/A ret = FPCFGA_APID_NOEXIST;
2N/A goto err;
2N/A }
2N/A
2N/A /* Remove the dynamic component from the base. */
2N/A *dyn = '\0';
2N/A }
2N/A
2N/A /* Get the path of dynamic attachment point if already configured. */
2N/A if (dyncomp != NULL) {
2N/A ret = dyn_apid_to_path(xport_phys, dyncomp,
2N/A &lunlistp, &l_errno);
2N/A if ((ret != FPCFGA_OK) && (ret != FPCFGA_APID_NOCONFIGURE)) {
2N/A cfga_err(errstring, l_errno, ERR_OP_FAILED, 0);
2N/A goto err;
2N/A }
2N/A }
2N/A
2N/A assert(xport_phys != NULL);
2N/A
2N/A apidp->xport_phys = xport_phys;
2N/A apidp->dyncomp = dyncomp;
2N/A apidp->lunlist = lunlistp;
2N/A apidp->flags = 0;
2N/A
2N/A return (FPCFGA_OK);
2N/A
2N/Aerr:
2N/A S_FREE(xport_phys);
2N/A S_FREE(dyncomp);
2N/A lunlist_free(lunlistp);
2N/A return (ret);
2N/A}
2N/A
2N/Astatic void
2N/Alunlist_free(struct luninfo_list *lunlist)
2N/A{
2N/Astruct luninfo_list *lunp;
2N/A
2N/A while (lunlist != NULL) {
2N/A lunp = lunlist->next;
2N/A S_FREE(lunlist->path);
2N/A S_FREE(lunlist);
2N/A lunlist = lunp;
2N/A }
2N/A}
2N/A
2N/Avoid
2N/Aapidt_free(apid_t *apidp)
2N/A{
2N/A if (apidp == NULL)
2N/A return;
2N/A
2N/A S_FREE(apidp->xport_phys);
2N/A S_FREE(apidp->dyncomp);
2N/A lunlist_free(apidp->lunlist);
2N/A}
2N/A
2N/Afpcfga_ret_t
2N/Awalk_tree(
2N/A const char *physpath,
2N/A void *arg,
2N/A uint_t init_flags,
2N/A walkarg_t *up,
2N/A fpcfga_cmd_t cmd,
2N/A int *l_errnop)
2N/A{
2N/A int rv;
2N/A di_node_t root, tree_root, fpnode;
2N/A char *root_path, *cp = NULL;
2N/A char *devfs_fp_path;
2N/A size_t len;
2N/A fpcfga_ret_t ret;
2N/A int found = 0;
2N/A
2N/A *l_errnop = 0;
2N/A
2N/A if ((root_path = strdup(physpath)) == NULL) {
2N/A *l_errnop = errno;
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A
2N/A /* Fix up path for di_init() */
2N/A len = strlen(DEVICES_DIR);
2N/A if (strncmp(root_path, DEVICES_DIR SLASH,
2N/A len + strlen(SLASH)) == 0) {
2N/A cp = root_path + len;
2N/A (void) memmove(root_path, cp, strlen(cp) + 1);
2N/A } else if (*root_path != '/') {
2N/A *l_errnop = 0;
2N/A ret = FPCFGA_ERR;
2N/A goto out;
2N/A }
2N/A
2N/A /* Remove dynamic component if any */
2N/A if ((cp = GET_DYN(root_path)) != NULL) {
2N/A *cp = '\0';
2N/A }
2N/A
2N/A /* Remove minor name if any */
2N/A if ((cp = strrchr(root_path, ':')) != NULL) {
2N/A *cp = '\0';
2N/A }
2N/A
2N/A /*
2N/A * If force_flag is set
2N/A * do di_init with DINFOFORCE flag and get to the input fp node
2N/A * from the device tree.
2N/A *
2N/A * In order to get the link between path_info node and scsi_vhci node
2N/A * it is required to take the snapshot of the whole device tree.
2N/A * this behavior of libdevinfo is inefficient. For a specific
2N/A * fca port DINFOPROP was sufficient on the fca path prior to
2N/A * scsi_vhci node support.
2N/A *
2N/A */
2N/A if ((up->flags & FLAG_DEVINFO_FORCE) == FLAG_DEVINFO_FORCE) {
2N/A tree_root = di_init("/", init_flags | DINFOFORCE);
2N/A } else {
2N/A tree_root = di_init("/", init_flags);
2N/A }
2N/A
2N/A if (tree_root == DI_NODE_NIL) {
2N/A *l_errnop = errno;
2N/A ret = FPCFGA_LIB_ERR;
2N/A goto out;
2N/A }
2N/A
2N/A fpnode = di_drv_first_node("fp", tree_root);
2N/A
2N/A while (fpnode) {
2N/A devfs_fp_path = di_devfs_path(fpnode);
2N/A if ((devfs_fp_path) && !(strncmp(devfs_fp_path,
2N/A root_path, strlen(root_path)))) {
2N/A found = 1;
2N/A di_devfs_path_free(devfs_fp_path);
2N/A break;
2N/A }
2N/A di_devfs_path_free(devfs_fp_path);
2N/A fpnode = di_drv_next_node(fpnode);
2N/A }
2N/A if (!(found)) {
2N/A ret = FPCFGA_LIB_ERR;
2N/A goto out;
2N/A } else {
2N/A root = fpnode;
2N/A }
2N/A
2N/A /* Walk the tree */
2N/A errno = 0;
2N/A if (cmd == FPCFGA_WALK_NODE) {
2N/A rv = di_walk_node(root, up->walkmode.node_args.flags, arg,
2N/A up->walkmode.node_args.fcn);
2N/A } else {
2N/A assert(cmd == FPCFGA_WALK_MINOR);
2N/A rv = di_walk_minor(root, up->walkmode.minor_args.nodetype, 0,
2N/A arg, up->walkmode.minor_args.fcn);
2N/A }
2N/A
2N/A if (rv != 0) {
2N/A *l_errnop = errno;
2N/A ret = FPCFGA_LIB_ERR;
2N/A } else {
2N/A if ((up->flags & FLAG_PATH_INFO_WALK) == FLAG_PATH_INFO_WALK) {
2N/A ret = stat_path_info_node(root, arg, l_errnop);
2N/A } else {
2N/A *l_errnop = 0;
2N/A ret = FPCFGA_OK;
2N/A }
2N/A }
2N/A
2N/A di_fini(tree_root);
2N/A
2N/A /*FALLTHRU*/
2N/Aout:
2N/A S_FREE(root_path);
2N/A return (ret);
2N/A}
2N/A
2N/A
2N/Aint
2N/Amsg_idx(msgid_t msgid)
2N/A{
2N/A int idx = 0;
2N/A
2N/A /* The string table index and the error id may or may not be same */
2N/A if (msgid >= 0 && msgid <= N_STRS - 1 &&
2N/A str_tbl[msgid].msgid == msgid) {
2N/A idx = msgid;
2N/A } else {
2N/A for (idx = 0; idx < N_STRS; idx++) {
2N/A if (str_tbl[idx].msgid == msgid)
2N/A break;
2N/A }
2N/A if (idx >= N_STRS) {
2N/A idx = UNKNOWN_ERR_IDX;
2N/A }
2N/A }
2N/A
2N/A return (idx);
2N/A}
2N/A
2N/A/*
2N/A * cfga_err() accepts a variable number of message IDs and constructs
2N/A * a corresponding error string which is returned via the errstring argument.
2N/A * cfga_err() calls dgettext() to internationalize proper messages.
2N/A * May be called with a NULL argument.
2N/A */
2N/Avoid
2N/Acfga_err(char **errstring, int l_errno, ...)
2N/A{
2N/A va_list ap;
2N/A int append_newline = 0;
2N/A char *tmp_str, *tmp_err_str = NULL;
2N/A
2N/A if (errstring == NULL) {
2N/A return;
2N/A }
2N/A
2N/A /*
2N/A * Don't append a newline, the application (for example cfgadm)
2N/A * should do that.
2N/A */
2N/A append_newline = 0;
2N/A
2N/A va_start(ap, l_errno);
2N/A msg_common(&tmp_err_str, append_newline, l_errno, ap);
2N/A va_end(ap);
2N/A
2N/A if (*errstring == NULL) {
2N/A *errstring = tmp_err_str;
2N/A return;
2N/A }
2N/A
2N/A /*
2N/A * *errstring != NULL
2N/A * There was something in errstring prior to this call.
2N/A * So, concatenate the old and new strings
2N/A */
2N/A if ((tmp_str = calloc(1,
2N/A strlen(*errstring) + strlen(tmp_err_str) + 2)) == NULL) {
2N/A /* In case of error, retain only the earlier message */
2N/A free(tmp_err_str);
2N/A return;
2N/A }
2N/A
2N/A sprintf(tmp_str, "%s\n%s", *errstring, tmp_err_str);
2N/A free(tmp_err_str);
2N/A free(*errstring);
2N/A *errstring = tmp_str;
2N/A}
2N/A
2N/A/*
2N/A * This routine accepts a variable number of message IDs and constructs
2N/A * a corresponding message string which is printed via the message print
2N/A * routine argument.
2N/A */
2N/Avoid
2N/Acfga_msg(struct cfga_msg *msgp, ...)
2N/A{
2N/A char *p = NULL;
2N/A int append_newline = 0, l_errno = 0;
2N/A va_list ap;
2N/A
2N/A if (msgp == NULL || msgp->message_routine == NULL) {
2N/A return;
2N/A }
2N/A
2N/A /* Append a newline after message */
2N/A append_newline = 1;
2N/A l_errno = 0;
2N/A
2N/A va_start(ap, msgp);
2N/A msg_common(&p, append_newline, l_errno, ap);
2N/A va_end(ap);
2N/A
2N/A (void) (*msgp->message_routine)(msgp->appdata_ptr, p);
2N/A
2N/A S_FREE(p);
2N/A}
2N/A
2N/A/*
2N/A * Get internationalized string corresponding to message id
2N/A * Caller must free the memory allocated.
2N/A */
2N/Achar *
2N/Acfga_str(int append_newline, ...)
2N/A{
2N/A char *p = NULL;
2N/A int l_errno = 0;
2N/A va_list ap;
2N/A
2N/A va_start(ap, append_newline);
2N/A msg_common(&p, append_newline, l_errno, ap);
2N/A va_end(ap);
2N/A
2N/A return (p);
2N/A}
2N/A
2N/Astatic void
2N/Amsg_common(char **msgpp, int append_newline, int l_errno, va_list ap)
2N/A{
2N/A int a = 0;
2N/A size_t len = 0;
2N/A int i = 0, n = 0;
2N/A char *s = NULL, *t = NULL;
2N/A strlist_t dummy;
2N/A strlist_t *savep = NULL, *sp = NULL, *tailp = NULL;
2N/A
2N/A if (*msgpp != NULL) {
2N/A return;
2N/A }
2N/A
2N/A dummy.next = NULL;
2N/A tailp = &dummy;
2N/A for (len = 0; (a = va_arg(ap, int)) != 0; ) {
2N/A n = GET_MSG_NARGS(a); /* 0 implies no additional args */
2N/A for (i = 0; i <= n; i++) {
2N/A sp = calloc(1, sizeof (*sp));
2N/A if (sp == NULL) {
2N/A goto out;
2N/A }
2N/A if (i == 0 && GET_MSG_INTL(a)) {
2N/A sp->str = dgettext(TEXT_DOMAIN, GET_MSG_STR(a));
2N/A } else if (i == 0) {
2N/A sp->str = GET_MSG_STR(a);
2N/A } else {
2N/A sp->str = va_arg(ap, char *);
2N/A }
2N/A len += (strlen(sp->str));
2N/A sp->next = NULL;
2N/A tailp->next = sp;
2N/A tailp = sp;
2N/A }
2N/A }
2N/A
2N/A len += 1; /* terminating NULL */
2N/A
2N/A s = t = NULL;
2N/A if (l_errno) {
2N/A s = dgettext(TEXT_DOMAIN, ": ");
2N/A t = S_STR(strerror(l_errno));
2N/A if (s != NULL && t != NULL) {
2N/A len += strlen(s) + strlen(t);
2N/A }
2N/A }
2N/A
2N/A if (append_newline) {
2N/A len++;
2N/A }
2N/A
2N/A if ((*msgpp = calloc(1, len)) == NULL) {
2N/A goto out;
2N/A }
2N/A
2N/A **msgpp = '\0';
2N/A for (sp = dummy.next; sp != NULL; sp = sp->next) {
2N/A (void) strcat(*msgpp, sp->str);
2N/A }
2N/A
2N/A if (s != NULL && t != NULL) {
2N/A (void) strcat(*msgpp, s);
2N/A (void) strcat(*msgpp, t);
2N/A }
2N/A
2N/A if (append_newline) {
2N/A (void) strcat(*msgpp, dgettext(TEXT_DOMAIN, "\n"));
2N/A }
2N/A
2N/A /* FALLTHROUGH */
2N/Aout:
2N/A sp = dummy.next;
2N/A while (sp != NULL) {
2N/A savep = sp->next;
2N/A S_FREE(sp);
2N/A sp = savep;
2N/A }
2N/A}
2N/A
2N/Afpcfga_ret_t
2N/Adevctl_cmd(
2N/A const char *physpath,
2N/A fpcfga_cmd_t cmd,
2N/A uint_t *statep,
2N/A int *l_errnop)
2N/A{
2N/A int rv = -1, i, type;
2N/A devctl_hdl_t hdl = NULL;
2N/A char *cp = NULL, *path = NULL;
2N/A int (*func)(const devctl_hdl_t);
2N/A int (*state_func)(const devctl_hdl_t, uint_t *);
2N/A
2N/A *l_errnop = 0;
2N/A
2N/A if (statep != NULL) *statep = 0;
2N/A
2N/A func = NULL;
2N/A state_func = NULL;
2N/A type = 0;
2N/A
2N/A for (i = 0; i < N_GET_STATE_CMDS; i++) {
2N/A if (get_state_cmds[i].cmd == cmd) {
2N/A state_func = get_state_cmds[i].state_fcn;
2N/A type = get_state_cmds[i].type;
2N/A assert(statep != NULL);
2N/A break;
2N/A }
2N/A }
2N/A
2N/A if (state_func == NULL) {
2N/A for (i = 0; i < N_SET_STATE_CMDS; i++) {
2N/A if (set_state_cmds[i].cmd == cmd) {
2N/A func = set_state_cmds[i].fcn;
2N/A type = set_state_cmds[i].type;
2N/A assert(statep == NULL);
2N/A break;
2N/A }
2N/A }
2N/A }
2N/A
2N/A assert(type == BUS_OP || type == DEV_OP);
2N/A
2N/A if (func == NULL && state_func == NULL) {
2N/A return (FPCFGA_ERR);
2N/A }
2N/A
2N/A /*
2N/A * Fix up path for calling devctl.
2N/A */
2N/A if ((path = strdup(physpath)) == NULL) {
2N/A *l_errnop = errno;
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A
2N/A /* Remove dynamic component if any */
2N/A if ((cp = GET_DYN(path)) != NULL) {
2N/A *cp = '\0';
2N/A }
2N/A
2N/A /* Remove minor name */
2N/A if ((cp = strrchr(path, ':')) != NULL) {
2N/A *cp = '\0';
2N/A }
2N/A
2N/A errno = 0;
2N/A
2N/A if (type == BUS_OP) {
2N/A hdl = devctl_bus_acquire(path, 0);
2N/A } else {
2N/A hdl = devctl_device_acquire(path, 0);
2N/A }
2N/A *l_errnop = errno;
2N/A
2N/A S_FREE(path);
2N/A
2N/A if (hdl == NULL) {
2N/A return (FPCFGA_ERR);
2N/A }
2N/A
2N/A errno = 0;
2N/A /* Only getstate functions require a second argument */
2N/A if (func != NULL && statep == NULL) {
2N/A rv = func(hdl);
2N/A *l_errnop = errno;
2N/A } else if (state_func != NULL && statep != NULL) {
2N/A rv = state_func(hdl, statep);
2N/A *l_errnop = errno;
2N/A } else {
2N/A rv = -1;
2N/A *l_errnop = 0;
2N/A }
2N/A
2N/A devctl_release(hdl);
2N/A
2N/A return ((rv == -1) ? FPCFGA_ERR : FPCFGA_OK);
2N/A}
2N/A
2N/A/*
2N/A * Is device in a known state ? (One of BUSY, ONLINE, OFFLINE)
2N/A * BUSY --> One or more device special files are open. Implies online
2N/A * ONLINE --> driver attached
2N/A * OFFLINE --> CF1 with offline flag set.
2N/A * UNKNOWN --> None of the above
2N/A */
2N/Aint
2N/Aknown_state(di_node_t node)
2N/A{
2N/A uint_t state;
2N/A
2N/A state = di_state(node);
2N/A
2N/A /*
2N/A * CF1 without offline flag set is considered unknown state.
2N/A * We are in a known state if either CF2 (driver attached) or
2N/A * offline.
2N/A */
2N/A if ((state & DI_DEVICE_OFFLINE) == DI_DEVICE_OFFLINE ||
2N/A (state & DI_DRIVER_DETACHED) != DI_DRIVER_DETACHED) {
2N/A return (1);
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Avoid
2N/Alist_free(ldata_list_t **llpp)
2N/A{
2N/A ldata_list_t *lp, *olp;
2N/A
2N/A lp = *llpp;
2N/A while (lp != NULL) {
2N/A olp = lp;
2N/A lp = olp->next;
2N/A S_FREE(olp);
2N/A }
2N/A
2N/A *llpp = NULL;
2N/A}
2N/A
2N/A/*
2N/A * Obtain the devlink from a /devices path
2N/A */
2N/Afpcfga_ret_t
2N/Aphyspath_to_devlink(
2N/A const char *basedir,
2N/A char *xport_phys,
2N/A char **xport_logpp,
2N/A int *l_errnop,
2N/A int match_minor)
2N/A{
2N/A pathm_t pmt = {NULL};
2N/A fpcfga_ret_t ret;
2N/A
2N/A pmt.phys = xport_phys;
2N/A pmt.ret = FPCFGA_NO_REC;
2N/A pmt.match_minor = match_minor;
2N/A
2N/A /*
2N/A * Search the /dev hierarchy starting at basedir.
2N/A */
2N/A ret = recurse_dev(basedir, &pmt, lookup_dev);
2N/A if (ret == FPCFGA_OK && (ret = pmt.ret) == FPCFGA_OK) {
2N/A assert(pmt.log != NULL);
2N/A *xport_logpp = pmt.log;
2N/A } else {
2N/A if (pmt.log != NULL) {
2N/A S_FREE(pmt.log);
2N/A }
2N/A
2N/A *xport_logpp = NULL;
2N/A *l_errnop = pmt.l_errno;
2N/A }
2N/A
2N/A return (ret);
2N/A}
2N/A
2N/Astatic fpcfga_recur_t
2N/Alookup_dev(const char *lpath, void *arg)
2N/A{
2N/A char ppath[PATH_MAX];
2N/A pathm_t *pmtp = (pathm_t *)arg;
2N/A
2N/A if (realpath(lpath, ppath) == NULL) {
2N/A return (FPCFGA_CONTINUE);
2N/A }
2N/A
2N/A ppath[sizeof (ppath) - 1] = '\0';
2N/A
2N/A /* Is this the physical path we are looking for */
2N/A if (dev_cmp(ppath, pmtp->phys, pmtp->match_minor)) {
2N/A return (FPCFGA_CONTINUE);
2N/A }
2N/A
2N/A if ((pmtp->log = strdup(lpath)) == NULL) {
2N/A pmtp->l_errno = errno;
2N/A pmtp->ret = FPCFGA_LIB_ERR;
2N/A } else {
2N/A pmtp->ret = FPCFGA_OK;
2N/A }
2N/A
2N/A return (FPCFGA_TERMINATE);
2N/A}
2N/A
2N/A/* Compare HBA physical ap_id and device path */
2N/Aint
2N/Ahba_dev_cmp(const char *hba, const char *devpath)
2N/A{
2N/A char *cp = NULL;
2N/A int rv;
2N/A size_t hba_len, dev_len;
2N/A char l_hba[MAXPATHLEN], l_dev[MAXPATHLEN];
2N/A
2N/A (void) snprintf(l_hba, sizeof (l_hba), "%s", hba);
2N/A (void) snprintf(l_dev, sizeof (l_dev), "%s", devpath);
2N/A
2N/A /* Remove dynamic component if any */
2N/A if ((cp = GET_DYN(l_hba)) != NULL) {
2N/A *cp = '\0';
2N/A }
2N/A
2N/A if ((cp = GET_DYN(l_dev)) != NULL) {
2N/A *cp = '\0';
2N/A }
2N/A
2N/A
2N/A /* Remove minor names */
2N/A if ((cp = strrchr(l_hba, ':')) != NULL) {
2N/A *cp = '\0';
2N/A }
2N/A
2N/A if ((cp = strrchr(l_dev, ':')) != NULL) {
2N/A *cp = '\0';
2N/A }
2N/A
2N/A hba_len = strlen(l_hba);
2N/A dev_len = strlen(l_dev);
2N/A
2N/A /* Check if HBA path is component of device path */
2N/A if (rv = strncmp(l_hba, l_dev, hba_len)) {
2N/A return (rv);
2N/A }
2N/A
2N/A /* devpath must have '/' and 1 char in addition to hba path */
2N/A if (dev_len >= hba_len + 2 && l_dev[hba_len] == '/') {
2N/A return (0);
2N/A } else {
2N/A return (-1);
2N/A }
2N/A}
2N/A
2N/Aint
2N/Adev_cmp(const char *dev1, const char *dev2, int match_minor)
2N/A{
2N/A char l_dev1[MAXPATHLEN], l_dev2[MAXPATHLEN];
2N/A char *mn1, *mn2;
2N/A int rv;
2N/A
2N/A (void) snprintf(l_dev1, sizeof (l_dev1), "%s", dev1);
2N/A (void) snprintf(l_dev2, sizeof (l_dev2), "%s", dev2);
2N/A
2N/A if ((mn1 = GET_DYN(l_dev1)) != NULL) {
2N/A *mn1 = '\0';
2N/A }
2N/A
2N/A if ((mn2 = GET_DYN(l_dev2)) != NULL) {
2N/A *mn2 = '\0';
2N/A }
2N/A
2N/A /* Separate out the minor names */
2N/A if ((mn1 = strrchr(l_dev1, ':')) != NULL) {
2N/A *mn1++ = '\0';
2N/A }
2N/A
2N/A if ((mn2 = strrchr(l_dev2, ':')) != NULL) {
2N/A *mn2++ = '\0';
2N/A }
2N/A
2N/A if ((rv = strcmp(l_dev1, l_dev2)) != 0 || !match_minor) {
2N/A return (rv);
2N/A }
2N/A
2N/A /*
2N/A * Compare minor names
2N/A */
2N/A if (mn1 == NULL && mn2 == NULL) {
2N/A return (0);
2N/A } else if (mn1 == NULL) {
2N/A return (-1);
2N/A } else if (mn2 == NULL) {
2N/A return (1);
2N/A } else {
2N/A return (strcmp(mn1, mn2));
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Returns non-zero on failure (aka, HBA_STATUS_ERROR_*
2N/A * Will handle retries if applicable.
2N/A */
2N/Aint
2N/AgetAdapterAttrs(HBA_HANDLE handle, HBA_ADAPTERATTRIBUTES *attrs)
2N/A{
2N/A int count = 0;
2N/A HBA_STATUS status = HBA_STATUS_ERROR_TRY_AGAIN; /* force first pass */
2N/A
2N/A /* Loop as long as we have a retryable error */
2N/A while ((status == HBA_STATUS_ERROR_TRY_AGAIN ||
2N/A status == HBA_STATUS_ERROR_BUSY) &&
2N/A count++ < HBA_MAX_RETRIES) {
2N/A status = HBA_GetAdapterAttributes(handle, attrs);
2N/A if (status == HBA_STATUS_OK) {
2N/A break;
2N/A }
2N/A sleep(1);
2N/A }
2N/A return (status);
2N/A}
2N/A
2N/A/*
2N/A * Returns non-zero on failure (aka, HBA_STATUS_ERROR_*
2N/A * Will handle retries if applicable.
2N/A */
2N/Aint
2N/AgetPortAttrsByWWN(HBA_HANDLE handle, HBA_WWN wwn, HBA_PORTATTRIBUTES *attrs)
2N/A{
2N/A int count = 0;
2N/A HBA_STATUS status = HBA_STATUS_ERROR_TRY_AGAIN; /* force first pass */
2N/A
2N/A /* Loop as long as we have a retryable error */
2N/A while ((status == HBA_STATUS_ERROR_TRY_AGAIN ||
2N/A status == HBA_STATUS_ERROR_BUSY) &&
2N/A count++ < HBA_MAX_RETRIES) {
2N/A status = HBA_GetPortAttributesByWWN(handle, wwn, attrs);
2N/A if (status == HBA_STATUS_OK) {
2N/A break;
2N/A }
2N/A
2N/A /* The odds of this occuring are very slim, but possible. */
2N/A if (status == HBA_STATUS_ERROR_STALE_DATA) {
2N/A /*
2N/A * If we hit a stale data scenario,
2N/A * we'll just tell the user to try again.
2N/A */
2N/A status = HBA_STATUS_ERROR_TRY_AGAIN;
2N/A break;
2N/A }
2N/A sleep(1);
2N/A }
2N/A return (status);
2N/A}
2N/A
2N/A/*
2N/A * Returns non-zero on failure (aka, HBA_STATUS_ERROR_*
2N/A * Will handle retries if applicable.
2N/A */
2N/Aint
2N/AgetAdapterPortAttrs(HBA_HANDLE handle, int portIndex,
2N/A HBA_PORTATTRIBUTES *attrs)
2N/A{
2N/A int count = 0;
2N/A HBA_STATUS status = HBA_STATUS_ERROR_TRY_AGAIN; /* force first pass */
2N/A
2N/A /* Loop as long as we have a retryable error */
2N/A while ((status == HBA_STATUS_ERROR_TRY_AGAIN ||
2N/A status == HBA_STATUS_ERROR_BUSY) &&
2N/A count++ < HBA_MAX_RETRIES) {
2N/A status = HBA_GetAdapterPortAttributes(handle, portIndex, attrs);
2N/A if (status == HBA_STATUS_OK) {
2N/A break;
2N/A }
2N/A
2N/A /* The odds of this occuring are very slim, but possible. */
2N/A if (status == HBA_STATUS_ERROR_STALE_DATA) {
2N/A /*
2N/A * If we hit a stale data scenario,
2N/A * we'll just tell the user to try again.
2N/A */
2N/A status = HBA_STATUS_ERROR_TRY_AGAIN;
2N/A break;
2N/A }
2N/A sleep(1);
2N/A }
2N/A return (status);
2N/A}
2N/A
2N/A/*
2N/A * Returns non-zero on failure (aka, HBA_STATUS_ERROR_*
2N/A * Will handle retries if applicable.
2N/A */
2N/Aint
2N/AgetDiscPortAttrs(HBA_HANDLE handle, int portIndex, int discIndex,
2N/A HBA_PORTATTRIBUTES *attrs)
2N/A{
2N/A int count = 0;
2N/A HBA_STATUS status = HBA_STATUS_ERROR_TRY_AGAIN; /* force first pass */
2N/A
2N/A /* Loop as long as we have a retryable error */
2N/A while ((status == HBA_STATUS_ERROR_TRY_AGAIN ||
2N/A status == HBA_STATUS_ERROR_BUSY) &&
2N/A count++ < HBA_MAX_RETRIES) {
2N/A status = HBA_GetDiscoveredPortAttributes(handle, portIndex,
2N/A discIndex, attrs);
2N/A if (status == HBA_STATUS_OK) {
2N/A break;
2N/A }
2N/A
2N/A /* The odds of this occuring are very slim, but possible. */
2N/A if (status == HBA_STATUS_ERROR_STALE_DATA) {
2N/A /*
2N/A * If we hit a stale data scenario, we'll just tell the
2N/A * user to try again.
2N/A */
2N/A status = HBA_STATUS_ERROR_TRY_AGAIN;
2N/A break;
2N/A }
2N/A sleep(1);
2N/A }
2N/A return (status);
2N/A}
2N/A
2N/A/*
2N/A * Find the Adapter port that matches the portPath.
2N/A * When the matching port is found the caller have to close handle
2N/A * and free library.
2N/A */
2N/Afpcfga_ret_t
2N/AfindMatchingAdapterPort(char *portPath, HBA_HANDLE *matchingHandle,
2N/A int *matchingPortIndex, HBA_PORTATTRIBUTES *matchingPortAttrs,
2N/A char **errstring)
2N/A{
2N/A HBA_HANDLE handle;
2N/A HBA_ADAPTERATTRIBUTES hbaAttrs;
2N/A HBA_PORTATTRIBUTES portAttrs;
2N/A HBA_STATUS status = HBA_STATUS_OK;
2N/A int count, retry = 0, l_errno = 0;
2N/A int adapterIndex, portIndex;
2N/A char adapterName[256];
2N/A char *cfg_ptr, *tmpPtr;
2N/A char *logical_apid = NULL;
2N/A
2N/A status = HBA_LoadLibrary();
2N/A if (status != HBA_STATUS_OK) {
2N/A cfga_err(errstring, 0, ERR_HBA_LOAD_LIBRARY, 0);
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A count = HBA_GetNumberOfAdapters();
2N/A if (count == 0) {
2N/A cfga_err(errstring, 0, ERR_NO_ADAPTER_FOUND, 0);
2N/A HBA_FreeLibrary();
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A
2N/A /* Loop over all HBAs */
2N/A for (adapterIndex = 0; adapterIndex < count; adapterIndex ++) {
2N/A status = HBA_GetAdapterName(adapterIndex, (char *)&adapterName);
2N/A if (status != HBA_STATUS_OK) {
2N/A /* May have been DR'd */
2N/A continue;
2N/A }
2N/A handle = HBA_OpenAdapter(adapterName);
2N/A if (handle == 0) {
2N/A /* May have been DR'd */
2N/A continue;
2N/A }
2N/A
2N/A do {
2N/A if (getAdapterAttrs(handle, &hbaAttrs)) {
2N/A /* Should never happen */
2N/A HBA_CloseAdapter(handle);
2N/A continue;
2N/A }
2N/A
2N/A /* Loop over all HBA Ports */
2N/A for (portIndex = 0;
2N/A portIndex < hbaAttrs.NumberOfPorts; portIndex++) {
2N/A if ((status = getAdapterPortAttrs(handle, portIndex,
2N/A &portAttrs)) != HBA_STATUS_OK) {
2N/A /* Need to refresh adapter */
2N/A if (status == HBA_STATUS_ERROR_STALE_DATA) {
2N/A HBA_RefreshInformation(handle);
2N/A break;
2N/A } else {
2N/A continue;
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * check to see if OSDeviceName is a /dev/cfg link
2N/A * or the physical path
2N/A */
2N/A if (strncmp(portAttrs.OSDeviceName, CFGA_DEV_DIR,
2N/A strlen(CFGA_DEV_DIR)) != 0) {
2N/A tmpPtr = strstr(portAttrs.OSDeviceName, MINOR_SEP);
2N/A if (tmpPtr != NULL) {
2N/A if (strncmp(portPath,
2N/A portAttrs.OSDeviceName,
2N/A strlen(portAttrs.OSDeviceName) -
2N/A strlen(tmpPtr)) == 0) {
2N/A if (matchingHandle)
2N/A *matchingHandle = handle;
2N/A if (matchingPortIndex)
2N/A *matchingPortIndex = portIndex;
2N/A if (matchingPortAttrs)
2N/A *matchingPortAttrs = portAttrs;
2N/A return (FPCFGA_OK);
2N/A }
2N/A }
2N/A } else {
2N/A /*
2N/A * strip off the /dev/cfg/ portion of the
2N/A * OSDeviceName
2N/A * make sure that the OSDeviceName is at least
2N/A * strlen("/dev/cfg") + 1 + 1 long.
2N/A * first 1 is for the / after /dev/cfg
2N/A * second 1 is to make sure there is somthing
2N/A * after
2N/A */
2N/A if (strlen(portAttrs.OSDeviceName) <
2N/A (strlen(CFGA_DEV_DIR) + 1 + 1))
2N/A continue;
2N/A cfg_ptr = portAttrs.OSDeviceName +
2N/A strlen(CFGA_DEV_DIR) + 1;
2N/A if (logical_apid == NULL) {
2N/A /* get the /dev/cfg link from the portPath */
2N/A if (make_xport_logid(portPath, &logical_apid,
2N/A &l_errno) != FPCFGA_OK) {
2N/A cfga_err(errstring, l_errno,
2N/A ERR_LIST, 0);
2N/A HBA_FreeLibrary();
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A }
2N/A /* compare logical ap_id */
2N/A if (strcmp(logical_apid, cfg_ptr) == 0) {
2N/A if (matchingHandle)
2N/A *matchingHandle = handle;
2N/A if (matchingPortIndex)
2N/A *matchingPortIndex = portIndex;
2N/A if (matchingPortAttrs)
2N/A *matchingPortAttrs = portAttrs;
2N/A S_FREE(logical_apid);
2N/A return (FPCFGA_OK);
2N/A }
2N/A }
2N/A }
2N/A if (logical_apid != NULL)
2N/A S_FREE(logical_apid);
2N/A } while ((status == HBA_STATUS_ERROR_STALE_DATA) &&
2N/A (retry++ < HBA_MAX_RETRIES));
2N/A
2N/A HBA_CloseAdapter(handle);
2N/A }
2N/A free(logical_apid);
2N/A
2N/A /* Got here. No mathcing adatper port found. */
2N/A cfga_err(errstring, 0, ERR_MATCHING_HBA_PORT, 0);
2N/A HBA_FreeLibrary();
2N/A return (FPCFGA_LIB_ERR);
2N/A}