root/usr/src/lib/cfgadm_plugins/ib/common/cfga_ib.c
/*
 * 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 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 * Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
 */

#include "cfga_ib.h"

/*
 * cfga_ib.c:
 *      All cfgadm entry points that are defined in the config_admin(3CFGADM)
 *      needed for InfiniBand support are described here. These cfgadm
 *      interfaces issue ioctl(s) to the IB nexus driver. Attachment points
 *      supported are - IOC, VPPA, Port, HCA_SVC and Pseudo dynamic ap_ids,
 *      the HCA static ap_id, and the IB static ap_id.
 *
 *      Given InfiniBand bus is fabric based, #of dynamic ap_ids present are
 *      unknown at any given point of time. Hence this plugin uses a
 *      packed nvlist data structure to hold ap_id related information.
 *      The IB nexus driver allocates the nvlist data in the kernel
 *      and this plugin processes the data (it is freed by IB nexus driver).
 */


/* function prototypes */
static int              ib_get_link(di_devlink_t, void *);
static icfga_ret_t      ib_physpath_to_devlink(char *, char **, int *);
static const char       *ib_get_msg(uint_t, msgcvt_t *, uint_t);
static void             ib_set_msg(char **, ...);
static cfga_err_t       ib_err_msg(char **, cfga_ib_ret_t, const char *, int);
static int              ib_verify_valid_apid(const char *);
static cfga_ib_ret_t    ib_verify_params(const char *, const char *, char **);
static void             ib_cleanup_after_devctl_cmd(devctl_hdl_t, nvlist_t *);
static cfga_ib_ret_t    ib_setup_for_devctl_cmd(char *, boolean_t,
                            devctl_hdl_t *, nvlist_t **);
static cfga_ib_ret_t    ib_device_configured(devctl_hdl_t, nvlist_t *,
                            ap_rstate_t *);
static cfga_ib_ret_t    ib_device_connected(devctl_hdl_t, nvlist_t *,
                            ap_ostate_t *);
static cfga_ib_ret_t    ib_do_control_ioctl(char *, uint_t, uint_t, uint_t,
                            void **, size_t *);
cfga_err_t              cfga_change_state(cfga_cmd_t, const char *,
                            const char *, struct cfga_confirm *,
                            struct cfga_msg *, char **, cfga_flags_t);
cfga_err_t              cfga_private_func(const char *, const char *,
                            const char *, struct cfga_confirm *,
                            struct cfga_msg *, char **, cfga_flags_t);
cfga_err_t              cfga_test(const char *, const char *, struct cfga_msg *,
                            char **, cfga_flags_t);
static cfga_ib_ret_t    ib_fill_static_apids(char *, cfga_list_data_t *);
cfga_err_t              cfga_list_ext(const char *, cfga_list_data_t **, int *,
                            const char *, const char *, char **, cfga_flags_t);
void                    cfga_msg(struct cfga_msg *, const char *);
cfga_err_t              cfga_help(struct cfga_msg *, const char *,
                            cfga_flags_t);
static int              ib_confirm(struct cfga_confirm *, char *);
static char             *ib_get_devicepath(const char *);


/* External function prototypes */
extern cfga_ib_ret_t    ib_rcm_offline(const char *, char **, char *,
                            cfga_flags_t);
extern cfga_ib_ret_t    ib_rcm_online(const char *, char **, char *,
                            cfga_flags_t);
extern cfga_ib_ret_t    ib_rcm_remove(const char *, char **, char *,
                            cfga_flags_t);
extern int              ib_add_service(char **);
extern int              ib_delete_service(char **);
extern int              ib_list_services(struct cfga_msg *, char **);


/* Globals */
int             cfga_version = CFGA_HSL_V2;     /* Set the version number for */
                                                /* the cfgadm library's use. */

static char     *ib_help[] = {  /* Help messages */
        NULL,
        /* CFGA_IB_HELP_HEADER */       "IB specific commands:\n",
        /* CFGA_IB_HELP_CONFIG */       "cfgadm -c [configure|unconfigure] "
            "ap_id [ap_id...]\n",
        /* CFGA_IB_HELP_LIST */         "cfgadm -x list_clients hca_ap_id "
            "[hca_ap_id...]\n",
        /* CFGA_IB_HELP_UPD_PKEY */     "cfgadm -x update_pkey_tbls ib\n",
        /* CFGA_IB_HELP_CONF_FILE1 */   "cfgadm -o comm=[port|vppa|hca-svc],"
            "service=<name> -x [add_service|delete_service] ib\n",
        /* CFGA_IB_HELP_CONF_FILE2 */   "cfgadm -x list_services ib\n",
        /* CFGA_IB_HELP_UPD_IOC_CONF */ "cfgadm -x update_ioc_config "
            "[ib | ioc_apid]\n",
        /* CFGA_IB_HELP_UNCFG_CLNTS */  "cfgadm -x unconfig_clients hca_ap_id "
            "[hca_ap_id...]\n",
        /* CFGA_IB_HELP_UNKNOWN */      "\tunknown command or option: ",
        NULL
};

static msgcvt_t ib_error_msgs[] = {     /* Error messages */
        /* CFGA_IB_OK */                { CVT, CFGA_OK, "ok" },
        /* CFGA_IB_UNKNOWN */           { CVT, CFGA_LIB_ERROR,
            "Unknown message; internal error " },
        /* CFGA_IB_INTERNAL_ERR */      { CVT, CFGA_LIB_ERROR,
            "Internal error " },
        /* CFGA_IB_INVAL_ARG_ERR */     { CVT, CFGA_LIB_ERROR,
            "Invalid input args " },
        /* CFGA_IB_OPTIONS_ERR */       { CVT, CFGA_ERROR,
            "Hardware specific options not supported " },
        /* CFGA_IB_AP_ERR */            { CVT, CFGA_APID_NOEXIST, "" },
        /* CFGA_IB_DEVCTL_ERR */        { CVT, CFGA_LIB_ERROR,
            "Cannot issue devctl to " },
        /* CFGA_IB_NOT_CONNECTED */     { CVT, CFGA_INSUFFICENT_CONDITION,
            "No device connected to " },
        /* CFGA_IB_NOT_CONFIGURED */    { CVT, CFGA_INSUFFICENT_CONDITION,
            "No device configured to " },
        /* CFGA_IB_ALREADY_CONNECTED */ { CVT, CFGA_INSUFFICENT_CONDITION,
            "already connected; cannot connect again " },
        /* CFGA_IB_ALREADY_CONFIGURED */ { CVT, CFGA_INSUFFICENT_CONDITION,
            "already configured " },
        /* CFGA_IB_CONFIG_OP_ERR */     { CVT, CFGA_ERROR,
            "configure operation failed " },
        /* CFGA_IB_UNCONFIG_OP_ERR */   { CVT, CFGA_ERROR,
            "unconfigure operation failed " },
        /* CFGA_IB_OPEN_ERR */          { CVT, CFGA_LIB_ERROR, "Cannot open " },
        /* CFGA_IB_IOCTL_ERR */         { CVT, CFGA_LIB_ERROR,
            "Driver ioctl failed " },
        /* CFGA_IB_BUSY_ERR */          { CVT, CFGA_SYSTEM_BUSY, " " },
        /* CFGA_IB_ALLOC_FAIL */        { CVT, CFGA_LIB_ERROR,
            "Memory allocation failure " },
        /* CFGA_IB_OPNOTSUPP */         { CVT, CFGA_OPNOTSUPP,
            "Operation not supported " },
        /* CFGA_IB_INVAL_APID_ERR */    { CVT, CFGA_LIB_ERROR,
            "Invalid ap_id supplied " },
        /* CFGA_IB_DEVLINK_ERR */       { CVT, CFGA_LIB_ERROR,
            "Could not find /dev/cfg link for " },
        /* CFGA_IB_PRIV_ERR */          { CVT, CFGA_PRIV, " " },
        /* CFGA_IB_NVLIST_ERR */        { CVT, CFGA_ERROR,
            "Internal error (nvlist) " },
        /* CFGA_IB_HCA_LIST_ERR */      { CVT, CFGA_ERROR,
            "Listing HCA's clients failed " },
        /* CFGA_IB_HCA_UNCONFIG_ERR */  { CVT, CFGA_ERROR,
            "Unconfiguring HCA's clients failed " },
        /* CFGA_IB_UPD_PKEY_TBLS_ERR */ { CVT, CFGA_ERROR,
            "Updating P_Key tables failed " },
        /* CFGA_IB_RCM_HANDLE_ERR */    { CVT, CFGA_ERROR,
            "Opening ib.conf file failed " },
        /* CFGA_IB_LOCK_FILE_ERR */     { CVT, CFGA_LIB_ERROR,
            "Locking ib.conf file failed " },
        /* CFGA_IB_UNLOCK_FILE_ERR */   { CVT, CFGA_LIB_ERROR,
            "Unlocking ib.conf file failed " },
        /* CFGA_IB_COMM_INVAL_ERR */    { CVT, CFGA_INVAL,
            "Communication type incorrectly specified " },
        /* CFGA_IB_SVC_INVAL_ERR */     { CVT, CFGA_INVAL,
            "Service name incorrectly specified " },
        /* CFGA_IB_SVC_LEN_ERR_ERR */   { CVT, CFGA_INVAL,
            "Service name len should be <= to 4, " },
        /* CFGA_IB_SVC_EXISTS_ERR */    { CVT, CFGA_INVAL, " "},
        /* CFGA_IB_SVC_NO_EXIST_ERR */  { CVT, CFGA_INVAL, " " },
        /* CFGA_IB_UCFG_CLNTS_ERR */    { CVT, CFGA_INVAL,
            "unconfig_clients failed for HCA " },
        /* CFGA_IB_INVALID_OP_ERR */    { CVT, CFGA_OPNOTSUPP, "on " },
        /* CFGA_IB_RCM_HANDLE */        { CVT, CFGA_ERROR,
            "cannot get RCM handle "},
        /* CFGA_IB_RCM_ONLINE_ERR */    { CVT, CFGA_SYSTEM_BUSY,
            "failed to online: "},
        /* CFGA_IB_RCM_OFFLINE_ERR */   { CVT, CFGA_SYSTEM_BUSY,
            "failed to offline: "}
};

/*
 * these are the only valid sub-options for services.
 */
static char             *ib_service_subopts[] = {
                                "comm",
                                "service",
                                NULL
                        };

/* Communication Service name : "port" or "vppa" or "hca-svc" */
static char             *comm_name = NULL;

char                    *service_name = NULL;   /* service name */
ib_service_type_t       service_type = IB_NONE; /* service type */


/* ========================================================================= */
/*
 * The next two funcs are imported from cfgadm_scsi.
 * ib_physpath_to_devlink is the only func directly used by cfgadm_ib.
 * ib_get_link supports it.
 */

/*
 * Function:
 *      ib_get_link
 * Input:
 *      devlink         - devlink for the device path
 *      arg             - argument passed to this "walker" function
 * Output:
 *      NONE
 * Returns:
 *      Continue "walking" or not
 * Description:
 *      Routine to search the /dev directory or a subtree of /dev.
 */
static int
ib_get_link(di_devlink_t devlink, void *arg)
{
        walk_link_t     *larg = (walk_link_t *)arg;

        /*
         * When path is specified, it's the node path without minor
         * name. Therefore, the ../.. prefixes needs to be stripped.
         */
        if (larg->path) {
                char *content = (char *)di_devlink_content(devlink);
                char *start = strstr(content, "/devices/");

                /* line content must have minor node */
                if (start == NULL ||
                    strncmp(start, larg->path, larg->len) != 0 ||
                    start[larg->len] != ':') {
                        return (DI_WALK_CONTINUE);
                }
        }

        *(larg->linkpp) = strdup(di_devlink_path(devlink));
        return (DI_WALK_TERMINATE);
}


/*
 * Function:
 *      ib_physpath_to_devlink
 * Input:
 *      node_path       - Physical path of the ap_id node
 * Output:
 *      logpp           - Logical path to the ap_id node
 *      l_errnop        - "errno"
 * Returns:
 *      ICFGA_OK if everything was fine; otherwise an error with
 *      l_errnop set.
 * Description:
 *      Given a physical path to an ap_id ensure that it exists
 */
/* ARGSUSED */
static icfga_ret_t
ib_physpath_to_devlink(char *node_path, char **logpp, int *l_errnop)
{
        char                    *minor_path;
        walk_link_t             larg;
        di_devlink_handle_t     hdl;

        if ((hdl = di_devlink_init(NULL, 0)) == NULL) {
                *l_errnop = errno;
                return (ICFGA_LIB_ERR);
        }

        *logpp = NULL;
        larg.linkpp = logpp;
        minor_path = (char *)node_path + strlen("/devices");
        larg.path = NULL;
        larg.len = 0;

        (void) di_devlink_walk(hdl, "^cfg/", minor_path, DI_PRIMARY_LINK,
            (void *)&larg, ib_get_link);

        di_devlink_fini(&hdl);

        if (*logpp == NULL) {
                *l_errnop = errno;
                return (ICFGA_LIB_ERR);
        }

        return (ICFGA_OK);
}


/* ========================================================================= */
/* Utilities */

/*
 * Function:
 *      ib_get_msg
 * Input:
 *      msg_index       - Index into the message table
 *      msg_tbl         - the message table
 *      tbl_size        - size of the message table
 * Output:
 *      NONE
 * Returns:
 *      Message string if valid, otherwise an error
 * Description:
 *      Given the index into a table (msgcvt_t) of messages,
 *      get the message string, converting it to the proper
 *      locale if necessary.
 *
 *      NOTE: See cfga_ib.h
 */
static const char *
ib_get_msg(uint_t msg_index, msgcvt_t *msg_tbl, uint_t tbl_size)
{
        if (msg_index >= tbl_size) {
                DPRINTF("get_error_msg: bad error msg index: %d\n", msg_index);
                msg_index = CFGA_IB_UNKNOWN;
        }

        return ((msg_tbl[msg_index].intl) ?
            dgettext(TEXT_DOMAIN, msg_tbl[msg_index].msgstr) :
            msg_tbl[msg_index].msgstr);
}


/*
 * Function:
 *      ib_set_msg
 * Input:
 *      NONE
 * Output:
 *      ret_str - Returned "message" string.
 * Returns:
 *      NONE
 * Description:
 *      Allocates and creates a message string (in *ret_str),
 *      by concatenating all the (char *) args together, in order.
 *      Last arg MUST be NULL.
 */
static void
ib_set_msg(char **ret_str, ...)
{
        char    *str;
        size_t  total_len, ret_str_len;
        va_list valist;

        va_start(valist, ret_str);

        total_len = (*ret_str == NULL) ? 0 : strlen(*ret_str);

        while ((str = va_arg(valist, char *)) != NULL) {
                size_t  len = strlen(str);
                char    *old_str = *ret_str;

                ret_str_len = total_len + len + 1;
                *ret_str = (char *)realloc(*ret_str, ret_str_len);
                if (*ret_str == NULL) {
                        free(old_str);
                        DPRINTF("ib_set_msg: realloc failed.\n");
                        va_end(valist);
                        return;
                }

                (void) strlcpy(*ret_str + total_len, str, ret_str_len);
                total_len += len;
        }

        va_end(valist);
}


/*
 * Function:
 *      ib_err_msg
 * Input:
 *      ap_id           - The attachment point of an IB fabric
 * Output:
 *      errstring       - Fill in the error msg string
 *      l_errno         - The "errno" to be filled in.
 * Returns:
 *      CFGA_IB_OK if we are able to fill in error msg;
 *      otherwise emit an error.
 * Description:
 *      Error message handling.
 *
 *      For the rv passed in, looks up the corresponding error message
 *      string(s), internationalized it if necessary, and concatenates
 *      it into a new memory buffer, and points *errstring to it.
 *      Note not all "rv"s will result in an error message return, as
 *      not all error conditions warrant a IB-specific error message.
 *
 *      Some messages may display ap_id or errno, which is why they are
 *      passed in.
 */
static cfga_err_t
ib_err_msg(char **errstring, cfga_ib_ret_t rv, const char *ap_id, int l_errno)
{
        char *errno_str;

        if (errstring == NULL) {
                return (ib_error_msgs[rv].cfga_err);
        }

        /* Generate the appropriate IB-specific error message(s) (if any). */
        switch (rv) {
        case CFGA_IB_OK:        /* Special case - do nothing.  */
                break;
        case CFGA_IB_AP_ERR:
        case CFGA_IB_UNKNOWN:
        case CFGA_IB_INTERNAL_ERR:
        case CFGA_IB_OPTIONS_ERR:
        case CFGA_IB_ALLOC_FAIL:
                /* These messages require no additional strings passed. */
                ib_set_msg(errstring, ERR_STR(rv), NULL);
                break;
        case CFGA_IB_NOT_CONNECTED:
        case CFGA_IB_NOT_CONFIGURED:
        case CFGA_IB_ALREADY_CONNECTED:
        case CFGA_IB_ALREADY_CONFIGURED:
        case CFGA_IB_CONFIG_OP_ERR:
        case CFGA_IB_UNCONFIG_OP_ERR:
        case CFGA_IB_BUSY_ERR:
        case CFGA_IB_DEVLINK_ERR:
        case CFGA_IB_RCM_HANDLE_ERR:
        case CFGA_IB_RCM_ONLINE_ERR:
        case CFGA_IB_RCM_OFFLINE_ERR:
        case CFGA_IB_DEVCTL_ERR:
        case CFGA_IB_COMM_INVAL_ERR:
        case CFGA_IB_SVC_INVAL_ERR:
        case CFGA_IB_SVC_LEN_ERR:
        case CFGA_IB_SVC_EXISTS_ERR:
        case CFGA_IB_SVC_NO_EXIST_ERR:
        case CFGA_IB_LOCK_FILE_ERR:
        case CFGA_IB_CONFIG_FILE_ERR:
        case CFGA_IB_UNLOCK_FILE_ERR:
        case CFGA_IB_UCFG_CLNTS_ERR:
        case CFGA_IB_INVALID_OP_ERR:
                /* These messages also print ap_id.  */
                ib_set_msg(errstring, ERR_STR(rv), "ap_id: ", ap_id, "", NULL);
                break;
        case CFGA_IB_IOCTL_ERR: /* These messages also print errno.  */
        case CFGA_IB_NVLIST_ERR:
                errno_str = l_errno ? strerror(l_errno) : "";
                ib_set_msg(errstring, ERR_STR(rv), errno_str,
                    l_errno ? "\n" : "", NULL);
                break;
        case CFGA_IB_OPEN_ERR: /* This messages also prints apid and errno.  */
        case CFGA_IB_PRIV_ERR:
        case CFGA_IB_HCA_LIST_ERR:
        case CFGA_IB_OPNOTSUPP:
        case CFGA_IB_INVAL_ARG_ERR:
        case CFGA_IB_INVAL_APID_ERR:
        case CFGA_IB_HCA_UNCONFIG_ERR:
        case CFGA_IB_UPD_PKEY_TBLS_ERR:
                errno_str = l_errno ? strerror(l_errno) : "";
                ib_set_msg(errstring, ERR_STR(rv), "ap_id: ", ap_id, "\n",
                    errno_str, l_errno ? "\n" : "", NULL);
                break;
        default:
                DPRINTF("ib_err_msg: Unrecognized message index: %d\n", rv);
                ib_set_msg(errstring, ERR_STR(CFGA_IB_INTERNAL_ERR), NULL);
        }

        /*
         * Determine the proper error code to send back to the cfgadm library.
         */
        return (ib_error_msgs[rv].cfga_err);
}


/*
 * Function:
 *      ib_verify_valid_apid
 * Input:
 *      ap_id           - The attachment point of an IB fabric
 * Output:
 *      NONE
 * Returns:
 *      0 if ap_id is valid; otherwise -1
 * Description:
 *      Check if ap_id is valid or not.
 *      Ensure the ap_id passed is in the correct (physical ap_id) form:
 *      path/device:xx[.xx]+
 *      where xx is a one or two-digit number.
 *
 *      Note the library always calls the plugin with a physical ap_id.
 *      Called by ib_verify_params().
 */
static int
ib_verify_valid_apid(const char *ap_id)
{
        char    *l_ap_id;

        if (ap_id == NULL) {
                return (-1);
        }

        l_ap_id = strchr(ap_id, *MINOR_SEP);
        l_ap_id++;

        /* fabric apids */
        if (strstr((char *)ap_id, IBNEX_FABRIC) != NULL) {
                DPRINTF("ib_valid_apid: l_apid = %s\n", l_ap_id);
                /* if the ap_id is "ib::" then report an error */
                if ((strlen(l_ap_id) == strlen(IBNEX_FABRIC) + 1) ||
                    (strlen(l_ap_id) == strlen(IBNEX_FABRIC) + 2)) {
                        return (-1);
                }

                if (strstr(l_ap_id, "...") != NULL) {
                        return (-1);
                }

        } else {        /* HCA ap_ids */
                /* ap_id has 1..2 or more than 2 dots */
                if (strstr(l_ap_id, "..") != NULL) {
                        return (-1);
                }
        }

        return (0);
}


/*
 * Function:
 *      ib_verify_params
 * Input:
 *      ap_id           - The attachment point of an IB fabric
 *      options         - command options passed by the cfgadm(8)
 *      errstring       - This contains error msg if command fails
 * Output:
 *      NONE
 * Returns:
 *      CFGA_IB_OK if parameters are valid; otherwise emit an error.
 * Description:
 *      Check if "options" and "errstring" are valid and if ap_id is
 *      valid or not.
 */
static cfga_ib_ret_t
ib_verify_params(const char *ap_id, const char *options, char **errstring)
{
        if (errstring != NULL) {
                *errstring = NULL;
        }

        if (options != NULL) {
                DPRINTF("ib_verify_params: h/w-specific options not "
                    "supported.\n");
                return (CFGA_IB_OPTIONS_ERR);
        }

        if (ib_verify_valid_apid(ap_id) != 0) {
                DPRINTF("ib_verify_params: not an IB ap_id.\n");
                return (CFGA_IB_AP_ERR);
        }
        return (CFGA_IB_OK);
}


/*
 * Function:
 *      ib_cleanup_after_devctl_cmd
 * Input:
 *      devctl_hdl      - Handler to devctl
 *      user_nvlistp    - Name-value-pair list pointer
 * Output:
 *      NONE
 * Returns:
 *      NONE
 * Description:
 *      Cleanup an initialization/setup done in the next function i.e.
 *      ib_setup_for_devctl_cmd().
 */
static void
ib_cleanup_after_devctl_cmd(devctl_hdl_t devctl_hdl, nvlist_t *user_nvlist)
{
        if (user_nvlist != NULL) {
                nvlist_free(user_nvlist);
        }

        if (devctl_hdl != NULL) {
                devctl_release(devctl_hdl);
        }
}


/*
 * Function:
 *      ib_setup_for_devctl_cmd
 * Input:
 *      ap_id           - Attachment point for the IB device in question
 *      use_static_ap_id - Whether to use static ap_id or not flag
 * Output:
 *      devctl_hdl      - Handler to devctl
 *      user_nvlistp    - Name-value-pair list pointer
 * Returns:
 *      CFGA_IB_OK if it succeeds or an appropriate error.
 * Description:
 *      For any IB device  that is doing a cfgadm operation this function
 *      sets up a devctl_hdl and allocates a nvlist_t. The devctl_hdl
 *      is acquired using libdevice APIs. The nvlist_t is filled up with
 *      the ap_id (as a string). This nvlist_t is looked up in the kernel
 *      to figure out which ap_id we are currently dealing with.
 *
 *      "use_static_ap_id" flag tells if one should do a devctl_ap_acquire
 *      with IB_STATIC_APID or not. NOTE: We need an actual file-system
 *      vnode to do a devctl_ap_acquire.
 *
 *      NOTE: always call ib_cleanup_after_devctl_cmd() after this function.
 */
static cfga_ib_ret_t
ib_setup_for_devctl_cmd(char *ap_id, boolean_t use_static_ap_id,
    devctl_hdl_t *devctl_hdl, nvlist_t **user_nvlistp)
{
        char    *apid = (use_static_ap_id == B_TRUE) ? IB_STATIC_APID : ap_id;

        /* Get a handle to the ap */
        if ((*devctl_hdl = devctl_ap_acquire(apid, 0)) == NULL) {
                DPRINTF("ib_setup_for_devctl_cmd: devctl_ap_acquire "
                    "errno: %d\n", errno);
                ib_cleanup_after_devctl_cmd(*devctl_hdl, *user_nvlistp);
                return (CFGA_IB_DEVCTL_ERR);
        }

        /* Set up to pass dynamic ap_id down to driver */
        if (nvlist_alloc(user_nvlistp, NV_UNIQUE_NAME_TYPE, 0) != 0) {
                DPRINTF("ib_setup_for_devctl: nvlist_alloc errno: %d\n", errno);
                *user_nvlistp = NULL;   /* Prevent possible incorrect free in */
                                        /* ib_cleanup_after_devctl_cmd */
                ib_cleanup_after_devctl_cmd(*devctl_hdl, *user_nvlistp);
                return (CFGA_IB_NVLIST_ERR);
        }

        /* create a "string" entry */
        if (nvlist_add_string(*user_nvlistp, IB_APID, ap_id) == -1) {
                DPRINTF("ib_setup_for_devctl_cmd: nvlist_add_string failed. "
                    "errno: %d\n", errno);
                ib_cleanup_after_devctl_cmd(*devctl_hdl, *user_nvlistp);
                return (CFGA_IB_NVLIST_ERR);
        }

        return (CFGA_IB_OK);
}


/*
 * Function:
 *      ib_device_configured
 * Input:
 *      hdl             - Handler to devctl
 *      nvl             - Name-value-pair list pointer
 * Output:
 *      rstate          - Receptacle state for the apid
 * Returns:
 *      CFGA_IB_OK if it succeeds or an appropriate error.
 * Description:
 *      Checks if there is a device actually configured to the ap? If so,
 *      issues a "devctl" to get the Receptacle state for that ap_id.
 *      If the ap_id is already configured it returns CFGA_IB_OK.
 *      Otherwise it returns a failure.
 */
static cfga_ib_ret_t
ib_device_configured(devctl_hdl_t hdl, nvlist_t *nvl, ap_rstate_t *rstate)
{
        cfga_ib_ret_t           rv;
        devctl_ap_state_t       devctl_ap_state;

        /* get ap_id's "devctl_ap_state" first */
        if (devctl_ap_getstate(hdl, nvl, &devctl_ap_state) == -1) {
                DPRINTF("ib_device_configured failed, errno: %d\n", errno);
                return (CFGA_IB_DEVCTL_ERR);
        }

        rv = CFGA_IB_ALREADY_CONFIGURED;
        *rstate = devctl_ap_state.ap_rstate;
        if (devctl_ap_state.ap_ostate != AP_OSTATE_CONFIGURED) {
                return (CFGA_IB_NOT_CONFIGURED);
        }

        return (rv);
}


/*
 * Function:
 *      ib_device_connected
 * Input:
 *      hdl             - Handler to devctl
 *      nvl             - Name-value-pair list pointer
 * Output:
 *      ostate          - Occupant state for the apid
 * Returns:
 *      CFGA_IB_OK if it succeeds or an appropriate error.
 * Description:
 *      Checks if there is a device actually connected to the ap? If so,
 *      issues a "devctl" to get the Occupant state for that ap_id.
 *      If the ap_id is already connected it returns CFGA_IB_OK.
 *      Otherwise it returns a failure.
 */
static cfga_ib_ret_t
ib_device_connected(devctl_hdl_t hdl, nvlist_t *list, ap_ostate_t *ostate)
{
        cfga_ib_ret_t           rv = CFGA_IB_ALREADY_CONNECTED;
        devctl_ap_state_t       devctl_ap_state;

        if (devctl_ap_getstate(hdl, list, &devctl_ap_state) == -1) {
                DPRINTF("ib_device_connected failed, errno: %d\n", errno);
                return (CFGA_IB_DEVCTL_ERR);
        }

        *ostate =  devctl_ap_state.ap_ostate;
        if (devctl_ap_state.ap_rstate != AP_RSTATE_CONNECTED) {
                return (CFGA_IB_NOT_CONNECTED);
        }

        return (rv);
}


/*
 * Function:
 *      ib_do_control_ioctl
 * Input:
 *      ap_id           - The dynamic attachment point of an IB device
 *      sub_cmd1        - Sub Command 1 to DEVCTL_AP_CONTROL devctl
 *      sub_cmd2        - Sub Command 2 to DEVCTL_AP_CONTROL devctl
 *                              (Mandatory except for IBNEX_NUM_HCA_NODES,
 *                              IBNEX_NUM_DEVICE_NODES,
 *                              IBNEX_UPDATE_PKEY_TBLS &
 *                              IBNEX_UPDATE_IOC_CONF)
 *      misc_arg        - optional arguments to DEVCTL_AP_CONTROL devctl
 * Output:
 *      descrp          - Buffer containing data back from kernel
 *      sizep           - Length of the buffer back from kernel
 * Returns:
 *      CFGA_IB_OK if it succeeds or an appropriate error.
 * Description:
 *      Issues DEVCTL_AP_CONTROL devctl with sub_cmd1 first which actually
 *      queries the IBNEX module in the kernel on the size of the data to
 *      be returned.
 *
 *      Next issues DEVCTL_AP_CONTROL devctl with a buffer of that much
 *      size and gets the actual data back.
 *      Passes the data and the size back to caller.
 */
static cfga_ib_ret_t
ib_do_control_ioctl(char *ap_id, uint_t sub_cmd1, uint_t sub_cmd2,
    uint_t misc_arg, void **descrp, size_t *sizep)
{
        int                     fd = -1;
        uint32_t                local_size = 0;
        cfga_ib_ret_t           rv = CFGA_IB_OK;
        struct ibnex_ioctl_data ioctl_data;

        /* try to open the ONLY static ap_id */
        if ((fd = open(IB_STATIC_APID, O_RDONLY)) == -1) {
                DPRINTF("ib_do_control_ioctl: open failed: "
                    "errno = %d\n", errno);
                /* Provides a more useful error msg */
                rv = (errno == EBUSY) ? CFGA_IB_BUSY_ERR : CFGA_IB_OPEN_ERR;
                return (rv);
        }

        /*
         * Find out first how large a buffer is needed?
         * NOTE: Ioctls only accept/return a 32-bit int for a get_size
         * to avoid 32/64 and BE/LE issues.
         */
        ioctl_data.cmd = sub_cmd1;
        ioctl_data.misc_arg = (uint_t)misc_arg;
        ioctl_data.buf = (caddr_t)&local_size;
        ioctl_data.bufsiz = sizeof (local_size);

        /* Pass "ap_id" up for all other commands */
        if (sub_cmd1 != IBNEX_NUM_DEVICE_NODES &&
            sub_cmd1 != IBNEX_NUM_HCA_NODES &&
            sub_cmd1 != IBNEX_UPDATE_PKEY_TBLS) {
                ioctl_data.ap_id = (caddr_t)ap_id;
                ioctl_data.ap_id_len = strlen(ap_id);

        } else {
                ioctl_data.ap_id = NULL;
                ioctl_data.ap_id_len = 0;
        }

        if (ioctl(fd, DEVCTL_AP_CONTROL, &ioctl_data) != 0) {
                DPRINTF("ib_do_control_ioctl: size ioctl ERR, errno: %d\n",
                    errno);
                (void) close(fd);
                rv = (errno == EBUSY) ? CFGA_IB_BUSY_ERR : CFGA_IB_IOCTL_ERR;
                return (rv);
        }
        *sizep = local_size;

        /*
         * Don't do the second ioctl only in these cases
         * (NOTE: the data is returned in the first ioctl itself; if any)
         */
        if (sub_cmd1 == IBNEX_NUM_DEVICE_NODES ||
            sub_cmd1 == IBNEX_NUM_HCA_NODES ||
            sub_cmd1 == IBNEX_UPDATE_PKEY_TBLS ||
            sub_cmd1 == IBNEX_UPDATE_IOC_CONF) {
                (void) close(fd);
                return (rv);
        }

        if (local_size == 0 || (*descrp = malloc(*sizep)) == NULL) {
                DPRINTF("ib_do_control_ioctl: malloc failed\n");
                (void) close(fd);
                return (CFGA_IB_ALLOC_FAIL);
        }

        /* Get the data */
        ioctl_data.cmd = sub_cmd2;
        ioctl_data.buf = (caddr_t)*descrp;
        ioctl_data.bufsiz = *sizep;

        if (ioctl(fd, DEVCTL_AP_CONTROL, &ioctl_data) != 0) {
                DPRINTF("ib_do_control_ioctl: ioctl failed: errno:%d\n", errno);
                if (*descrp != NULL) {
                        free(*descrp);
                        *descrp = NULL;
                }
                rv = (errno == EBUSY) ? CFGA_IB_BUSY_ERR : CFGA_IB_IOCTL_ERR;
        }

        (void) close(fd);
        return (rv);
}


/* ========================================================================== */
/* Entry points */

/*
 * Function:
 *      cfga_change_state
 * Input:
 *      state_change_cmd - Argument to the cfgadm -c command
 *      ap_id           - The attachment point of an IB fabric
 *      options         - State Change command options passed by the cfgadm(8)
 *      confp           - Whether this command requires confirmation?
 *      msgp            - cfgadm error message for this plugin
 *      errstring       - This contains error msg if command fails
 *      flags           - cfgadm(8) flags
 * Output:
 *      NONE
 * Returns:
 *      If the command succeeded perform the cfgadm -c <cmd>;
 *      otherwise emit an error
 * Description:
 *      Do cfgadm -c <cmd>
 */
/*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)
{
        int             ret;
        char            *devpath;
        nvlist_t        *nvl = NULL;
        boolean_t       static_ap_id = B_TRUE;
        ap_rstate_t     rstate;
        ap_ostate_t     ostate;
        devctl_hdl_t    hdl = NULL;
        cfga_ib_ret_t   rv = CFGA_IB_OK;

        if ((rv = ib_verify_params(ap_id, options, errstring)) != CFGA_IB_OK) {
                (void) cfga_help(msgp, options, flags);
                return (ib_err_msg(errstring, CFGA_IB_INVAL_APID_ERR,
                    ap_id, errno));
        }

        /*
         * All subcommands which can change state of device require
         * root privileges.
         */
        if (geteuid() != 0) {
                return (ib_err_msg(errstring, CFGA_IB_PRIV_ERR, ap_id, errno));
        }

        if (strstr((char *)ap_id, IB_FABRIC_APID_STR) == NULL)
                static_ap_id = B_FALSE;

        if ((rv = ib_setup_for_devctl_cmd((char *)ap_id, static_ap_id,
            &hdl, &nvl)) != CFGA_IB_OK) {
                ib_cleanup_after_devctl_cmd(hdl, nvl);
                return (ib_err_msg(errstring, rv, ap_id, errno));
        }

        switch (state_change_cmd) {
        case CFGA_CMD_CONFIGURE:
                rv = ib_device_connected(hdl, nvl, &ostate);
                if (rv != CFGA_IB_ALREADY_CONNECTED) {
                        ret = (rv != CFGA_IB_NOT_CONNECTED) ?
                            CFGA_IB_CONFIG_OP_ERR : rv;
                        ib_cleanup_after_devctl_cmd(hdl, nvl);
                        return (ib_err_msg(errstring, ret, ap_id, errno));
                }

                if (rv == CFGA_IB_ALREADY_CONNECTED) {
                        /*
                         * special case handling for
                         * SLM based cfgadm disconnects
                         */
                        if (ostate == AP_OSTATE_CONFIGURED) {
                                ib_cleanup_after_devctl_cmd(hdl, nvl);
                                return (ib_err_msg(errstring,
                                    CFGA_IB_ALREADY_CONFIGURED, ap_id,
                                    errno));
                        }
                }


                rv = CFGA_IB_OK;        /* Other status don't matter */

                if (devctl_ap_configure(hdl, nvl) != 0) {
                        DPRINTF("cfga_change_state: devctl_ap_configure "
                            "failed. errno: %d\n", errno);
                        rv = CFGA_IB_CONFIG_OP_ERR;
                        break;
                }

                devpath = ib_get_devicepath(ap_id);
                if (devpath == NULL) {
                        int i;

                        /*
                         * try for some time as IB hotplug thread
                         * takes a while to create the path
                         * and then eventually give up
                         */
                        for (i = 0;
                            i < IB_RETRY_DEVPATH && (devpath == NULL); i++) {
                                sleep(IB_MAX_DEVPATH_DELAY);
                                devpath = ib_get_devicepath(ap_id);
                        }

                        if (devpath == NULL) {
                                DPRINTF("cfga_change_state: get device "
                                    "path failed i = %d\n", i);
                                rv = CFGA_IB_CONFIG_OP_ERR;
                                break;
                        }
                }
                S_FREE(devpath);
                break;

        case CFGA_CMD_UNCONFIGURE:
                if ((rv = ib_device_connected(hdl, nvl, &ostate)) !=
                    CFGA_IB_ALREADY_CONNECTED) {
                        ib_cleanup_after_devctl_cmd(hdl, nvl);
                        if (rv == CFGA_IB_DEVCTL_ERR)
                                rv = CFGA_IB_INVALID_OP_ERR;
                        return (ib_err_msg(errstring, rv, ap_id, errno));
                }

                /* check if it is already unconfigured */
                if ((rv = ib_device_configured(hdl, nvl, &rstate)) ==
                    CFGA_IB_NOT_CONFIGURED) {
                        ib_cleanup_after_devctl_cmd(hdl, nvl);
                        return (ib_err_msg(errstring, rv, ap_id, errno));
                }

                rv = CFGA_IB_OK;        /* Other statuses don't matter */

                if (!ib_confirm(confp, IB_CONFIRM1)) {
                        ib_cleanup_after_devctl_cmd(hdl, nvl);
                        return (CFGA_NACK);
                }

                devpath = ib_get_devicepath(ap_id);
                if (devpath == NULL) {
                        DPRINTF("cfga_change_state: get device path failed\n");
                        rv = CFGA_IB_UNCONFIG_OP_ERR;
                        break;
                }

                if ((rv = ib_rcm_offline(ap_id, errstring, devpath, flags)) !=
                    CFGA_IB_OK) {
                        S_FREE(devpath);
                        break;
                }

                ret = devctl_ap_unconfigure(hdl, nvl);
                if (ret != 0) {
                        DPRINTF("cfga_change_state: devctl_ap_unconfigure "
                            "failed with errno: %d\n", errno);
                        rv = CFGA_IB_UNCONFIG_OP_ERR;
                        if (errno == EBUSY) {
                                rv = CFGA_IB_BUSY_ERR;
                        }
                        (void) ib_rcm_online(ap_id, errstring, devpath, flags);

                } else {
                        (void) ib_rcm_remove(ap_id, errstring, devpath, flags);
                }

                S_FREE(devpath);
                break;

        case CFGA_CMD_LOAD:
        case CFGA_CMD_UNLOAD:
        case CFGA_CMD_CONNECT:
        case CFGA_CMD_DISCONNECT:
                (void) cfga_help(msgp, options, flags);
                rv = CFGA_IB_OPNOTSUPP;
                break;

        case CFGA_CMD_NONE:
        default:
                (void) cfga_help(msgp, options, flags);
                rv = CFGA_IB_INTERNAL_ERR;
        }

        ib_cleanup_after_devctl_cmd(hdl, nvl);
        return (ib_err_msg(errstring, rv, ap_id, errno));
}


/*
 * Function:
 *      cfga_private_func
 * Input:
 *      func            - The private function (passed w/ -x option)
 *      ap_id           - The attachment point of an IB fabric
 *      options         - Private function command options passed
 *                              by the cfgadm(8)
 *      confp           - Whether this command requires confirmation?
 *      msgp            - cfgadm error message for this plugin
 *      errstring       - This contains error msg if command fails
 *      flags           - cfgadm(8) flags
 * Output:
 *      NONE
 * Returns:
 *      If the command succeeded perform the 'cfgadm -x <func>'; otherwise
 *      return failure.
 * Description:
 *      Do cfgadm -x <func>
 */
/*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)
{
        int             len, ret, count = 0;
        char            *clnt_name = NULL, *alt_hca = NULL;
        char            *clnt_apid = NULL, *clnt_devpath = NULL;
        char            *name, *msg = NULL;
        char            *fab_apid = strstr((char *)ap_id, IBNEX_FABRIC);
        size_t          info_len = 0;
        uchar_t         *info = NULL;
        nvlist_t        *nvl;
        nvpair_t        *nvp = NULL;
        ap_rstate_t     rstate;
        devctl_hdl_t    hdl = NULL;
        cfga_ib_ret_t   rv;

        if ((rv = ib_verify_params(ap_id, NULL, errstring)) != CFGA_IB_OK) {
                DPRINTF("cfga_private_func: ib_verify_params "
                    "failed with rv: %d\n", rv);
                return (ib_err_msg(errstring, rv, ap_id, errno));
        }

        if (func == NULL) {
                DPRINTF("cfga_private_func: func is NULL\n");
                return (ib_err_msg(errstring, CFGA_IB_INVAL_ARG_ERR, ap_id,
                    errno));
        }

        /*
         * check first if IB static ap_id is "configured" for use
         */
        if (fab_apid != NULL) {
                if ((rv = ib_setup_for_devctl_cmd(fab_apid, B_TRUE, &hdl,
                    &nvl)) != CFGA_IB_OK) {
                        ib_cleanup_after_devctl_cmd(hdl, nvl);
                        return (ib_err_msg(errstring, rv, ap_id, errno));
                }
                if ((rv = ib_device_configured(hdl, nvl, &rstate)) ==
                    CFGA_IB_NOT_CONFIGURED) {
                        return (ib_err_msg(errstring, rv, ap_id, errno));
                }
                ib_cleanup_after_devctl_cmd(hdl, nvl);
        }

        rv = CFGA_IB_OK;
        DPRINTF("cfga_private_func: func is %s\n", func);
        if (strcmp(func, IB_LIST_HCA_CLIENTS) == 0) {   /* -x list_clients */

                /* only supported on HCA ap_ids */
                if (fab_apid != NULL) {
                        DPRINTF("cfga_private_func: fabric apid supplied\n");
                        return (ib_err_msg(errstring, CFGA_IB_INVALID_OP_ERR,
                            ap_id, errno));
                }

                if ((msg = (char *)calloc(256, 1)) == NULL) {
                        DPRINTF("cfga_private_func: malloc for msg failed. "
                            "errno: %d\n", errno);
                        return (ib_err_msg(errstring, CFGA_IB_ALLOC_FAIL,
                            ap_id, errno));
                }

                if ((rv = ib_do_control_ioctl((char *)ap_id, IBNEX_HCA_LIST_SZ,
                    IBNEX_HCA_LIST_INFO, 0, (void **)&info, &info_len)) != 0) {
                        DPRINTF("cfga_private_func: "
                            "ib_do_control_ioctl list failed :%d\n", rv);
                        S_FREE(msg);
                        return (ib_err_msg(errstring, CFGA_IB_HCA_LIST_ERR,
                            ap_id, errno));
                }

                if (nvlist_unpack((char *)info, info_len, &nvl, 0)) {
                        DPRINTF("cfga_private_func: "
                            "nvlist_unpack 2 failed %p\n", info);
                        S_FREE(info);
                        S_FREE(msg);
                        return (ib_err_msg(errstring, CFGA_IB_NVLIST_ERR, ap_id,
                            errno));
                }

                (void) snprintf(msg, 256, "Ap_Id\t\t\t       IB Client\t\t "
                    "Alternate HCA\n");
                cfga_msg(msgp, msg);

                /* Walk the NVPAIR data */
                while (nvp = nvlist_next_nvpair(nvl, nvp)) {
                        name = nvpair_name(nvp);
                        if (strcmp(name, "Client") == 0) {
                                (void) nvpair_value_string(nvp, &clnt_name);
                                ++count;
                        } else if (strcmp(name, "Alt_HCA") == 0) {
                                (void) nvpair_value_string(nvp, &alt_hca);
                                ++count;
                        } else if (strcmp(name, "ApID") == 0) {
                                (void) nvpair_value_string(nvp, &clnt_apid);
                                ++count;
                        }

                        /* check at the end; print message per client found */
                        if (count == 3) {
                                count = 0;
                                (void) snprintf(msg, 256, "%-30s %-25s %s\n",
                                    clnt_apid, clnt_name, alt_hca);
                                cfga_msg(msgp, msg);
                        }
                } /* end of while */

                S_FREE(info);
                S_FREE(msg);
                nvlist_free(nvl);

        /* -x unconfig_clients */
        } else if (strcmp(func, IB_UNCONFIG_HCA_CLIENTS) == 0) {
                /*
                 * -x unconfig_clients changes state by calling into RCM.
                 * It needs root privileges.
                 */
                if (geteuid() != 0) {
                        return (ib_err_msg(errstring, CFGA_IB_PRIV_ERR, ap_id,
                            errno));
                }

                /* only supported on HCA ap_ids */
                if (fab_apid != NULL) {
                        DPRINTF("cfga_private_func: fabric apid supplied\n");
                        return (ib_err_msg(errstring, CFGA_IB_INVALID_OP_ERR,
                            ap_id, errno));
                }

                /*
                 * Check w/ user if it is ok to do this operation
                 * If the user fails to confirm, bailout
                 */
                if (!ib_confirm(confp, IB_CONFIRM3))
                        return (CFGA_NACK);

                /* Get device-paths of all the IOC/Port/Pseudo devices */
                rv = ib_do_control_ioctl((char *)ap_id, IBNEX_UNCFG_CLNTS_SZ,
                    IBNEX_UNCFG_CLNTS_INFO, 0, (void **)&info, &info_len);
                if (rv != 0) {
                        DPRINTF("cfga_private_func: ib_do_control_ioctl "
                            "failed :%d\n", rv);
                        return (ib_err_msg(errstring, CFGA_IB_HCA_UNCONFIG_ERR,
                            ap_id, errno));
                }

                if (nvlist_unpack((char *)info, info_len, &nvl, 0)) {
                        DPRINTF("cfga_private_func: nvlist_unpack failed %p\n",
                            info);
                        S_FREE(info);
                        return (ib_err_msg(errstring, CFGA_IB_NVLIST_ERR, ap_id,
                            errno));
                }

                ret = 0;

                /* Call RCM Offline on all device paths */
                while (nvp = nvlist_next_nvpair(nvl, nvp)) {
                        name = nvpair_name(nvp);
                        if (strcmp(name, "devpath") == 0) {
                                (void) nvpair_value_string(nvp, &clnt_devpath);
                                ++count;
                        } else if (strcmp(name, "ApID") == 0) {
                                (void) nvpair_value_string(nvp, &clnt_apid);
                                ++count;
                        }

                        /* handle the client unconfigure now */
                        if (count == 2) {
                                count = 0;      /* reset count */

                                DPRINTF("cfga_private_func: client apid = %s, "
                                    "DevPath = %s\n", clnt_apid, clnt_devpath);
                                if ((rv = ib_setup_for_devctl_cmd(clnt_apid,
                                    B_TRUE, &hdl, &nvl)) != CFGA_IB_OK) {
                                        ib_cleanup_after_devctl_cmd(hdl, nvl);
                                        return (ib_err_msg(errstring, rv,
                                            clnt_apid, errno));
                                }

                                if ((rv = ib_device_configured(hdl, nvl,
                                    &rstate)) == CFGA_IB_NOT_CONFIGURED)
                                        continue;

                                if ((rv = ib_rcm_offline(clnt_apid, errstring,
                                    clnt_devpath, flags)) != CFGA_IB_OK) {
                                        DPRINTF("cfga_private_func: client rcm "
                                            "offline failed for %s, with %d\n",
                                            clnt_devpath, rv);
                                        ret = rv;
                                        continue;
                                }

                                if (devctl_ap_unconfigure(hdl, nvl) != 0) {
                                        DPRINTF("cfga_private_func: client "
                                            "unconfigure failed: errno %d\n",
                                            errno);
                                        ret = CFGA_IB_UNCONFIG_OP_ERR;
                                        if (errno == EBUSY)
                                                ret = CFGA_IB_BUSY_ERR;
                                        (void) ib_rcm_online(clnt_apid,
                                            errstring, clnt_devpath, flags);
                                        continue;
                                } else {
                                        (void) ib_rcm_remove(clnt_apid,
                                            errstring, clnt_devpath, flags);
                                }
                                ib_cleanup_after_devctl_cmd(hdl, nvl);

                        } /* end of if count == 2 */

                } /* end of while */

                S_FREE(info);
                nvlist_free(nvl);
                if (ret) {
                        DPRINTF("cfga_private_func: unconfig_clients of %s "
                            "failed with %d\n", ap_id, ret);
                        return (ib_err_msg(errstring, CFGA_IB_UCFG_CLNTS_ERR,
                            ap_id, errno));
                }

        /* -x update_pkey_tbls */
        } else if (strcmp(func, IB_UPDATE_PKEY_TBLS) == 0) {
                /*
                 * Check for root privileges.
                 */
                if (geteuid() != 0) {
                        return (ib_err_msg(errstring, CFGA_IB_PRIV_ERR, ap_id,
                            errno));
                }

                /* CHECK: Only supported on fabric ap_ids */
                if (fab_apid == NULL || strcmp(fab_apid, IBNEX_FABRIC) != 0) {
                        DPRINTF("cfga_private_func: fabric apid needed\n");
                        return (ib_err_msg(errstring, CFGA_IB_INVALID_OP_ERR,
                            ap_id, errno));
                }

                /* Check w/ user if it is ok to do this operation */
                len = strlen(IB_CONFIRM4) + 10;
                if ((msg = (char *)calloc(len, 1)) != NULL) {
                        (void) snprintf(msg, len, "%s\nContinue", IB_CONFIRM4);
                }

                /* If the user fails to confirm, return */
                if (!ib_confirm(confp, msg)) {
                        free(msg);
                        return (CFGA_NACK);
                }
                free(msg);

                /* Update P_Key tables for all ports of all HCAs */
                rv = ib_do_control_ioctl((char *)ap_id, IBNEX_UPDATE_PKEY_TBLS,
                    0, 0, 0, &info_len);

                if (rv != 0) {
                        DPRINTF("cfga_private_func: ib_do_control_ioctl "
                            "failed :%d\n", rv);
                        return (ib_err_msg(errstring, CFGA_IB_UPD_PKEY_TBLS_ERR,
                            ap_id, errno));
                }

        /* -x [add_service|delete_service] */
        } else if ((strncmp(func, IB_ADD_SERVICE, 12) == 0) ||
            (strncmp(func, IB_DELETE_SERVICE, 15) == 0)) {
                char                    *subopts, *val;
                uint8_t                 cmd;

                /* check: Only supported on fabric ap_ids */
                if (fab_apid == NULL || strcmp(fab_apid, IBNEX_FABRIC) != 0) {
                        DPRINTF("cfga_private_func: fabric apid needed\n");
                        return (ib_err_msg(errstring, CFGA_IB_INVALID_OP_ERR,
                            ap_id, errno));
                }

                /* Check for root privileges. */
                if (geteuid() != 0) {
                        return (ib_err_msg(errstring, CFGA_IB_PRIV_ERR, ap_id,
                            errno));
                }

                /* return error if no options are specified */
                subopts = (char *)options;
                if (subopts == (char *)NULL) {
                        DPRINTF("cfga_private_func: no sub-options\n");
                        (void) cfga_help(msgp, options, flags);
                        return (ib_err_msg(errstring, CFGA_IB_INVAL_ARG_ERR,
                            ap_id, errno));
                }

                /* parse options specified */
                while (*subopts != '\0') {
                        switch (getsubopt(&subopts, ib_service_subopts, &val)) {
                        case 0: /* comm */
                                if (val == NULL) {
                                        (void) cfga_help(msgp, options, flags);
                                        S_FREE(service_name);
                                        return (ib_err_msg(errstring,
                                            CFGA_IB_INVAL_ARG_ERR,
                                            ap_id, errno));
                                } else {
                                        comm_name = strdup(val);
                                        if (comm_name == NULL) {
                                                DPRINTF("comm sub-opt invalid "
                                                    "arg\n");
                                                S_FREE(service_name);
                                                return (ib_err_msg(errstring,
                                                    CFGA_IB_COMM_INVAL_ERR,
                                                    ap_id, errno));
                                        }
                                }
                                break;

                        case 1: /* service */
                                if (val == NULL) {
                                        (void) cfga_help(msgp, options, flags);
                                        S_FREE(comm_name);
                                        return (ib_err_msg(errstring,
                                            CFGA_IB_INVAL_ARG_ERR,
                                            ap_id, errno));
                                } else {
                                        /* service can be upto 4 long */
                                        if (strlen(val) == 0 ||
                                            strlen(val) > 4) {
                                                DPRINTF("comm sub-opt invalid "
                                                    "service passed\n");
                                                S_FREE(comm_name);
                                                return (ib_err_msg(errstring,
                                                    CFGA_IB_SVC_LEN_ERR,
                                                    ap_id, errno));
                                        }
                                        service_name = strdup(val);
                                        if (service_name == NULL) {
                                                DPRINTF("comm sub-opt "
                                                    "internal error\n");
                                                S_FREE(comm_name);
                                                return (ib_err_msg(errstring,
                                                    CFGA_IB_SVC_INVAL_ERR,
                                                    ap_id, errno));
                                        }
                                }
                                break;

                        default:
                                (void) cfga_help(msgp, options, flags);
                                S_FREE(comm_name);
                                S_FREE(service_name);
                                return (ib_err_msg(errstring,
                                    CFGA_IB_INVAL_ARG_ERR, ap_id, errno));
                        }
                }

                /* figure out the "operation" */
                if (strncasecmp(func, IB_ADD_SERVICE, 11) == 0)
                        cmd = IBCONF_ADD_ENTRY;
                else if (strncasecmp(func, IB_DELETE_SERVICE, 14) == 0)
                        cmd = IBCONF_DELETE_ENTRY;
                DPRINTF("Service = %s, Comm = %s, Operation = %s\n",
                    service_name, comm_name, func);

                if (strncasecmp(comm_name, IBNEX_PORT_STR, 4) == 0)
                        service_type = IB_PORT_SERVICE;
                else if (strncasecmp(comm_name, IBNEX_VPPA_STR, 4) == 0)
                        service_type = IB_VPPA_SERVICE;
                else if (strncasecmp(comm_name, IBNEX_HCASVC_STR, 4) == 0)
                        service_type = IB_HCASVC_SERVICE;
                else {
                        (void) cfga_help(msgp, options, flags);
                        S_FREE(comm_name);
                        S_FREE(service_name);
                        return (ib_err_msg(errstring, CFGA_IB_INVAL_ARG_ERR,
                            ap_id, errno));
                }

                /* do the add/delete entry to the service */
                if (cmd == IBCONF_ADD_ENTRY) {
                        if ((rv = ib_add_service(errstring)) != CFGA_IB_OK)
                                DPRINTF("cfga_private_func: add failed\n");
                } else if (cmd == IBCONF_DELETE_ENTRY) {
                        if ((rv = ib_delete_service(errstring)) != CFGA_IB_OK)
                                DPRINTF("cfga_private_func: delete failed\n");
                }

                S_FREE(comm_name);
                S_FREE(service_name);
                return (ib_err_msg(errstring, rv, ap_id, errno));

        } else if (strncmp(func, IB_LIST_SERVICES, 13) == 0) {

                /* check: Only supported on fabric ap_ids */
                if (fab_apid == NULL || strcmp(fab_apid, IBNEX_FABRIC) != 0) {
                        DPRINTF("cfga_private_func: fabric apid needed\n");
                        return (ib_err_msg(errstring, CFGA_IB_INVALID_OP_ERR,
                            ap_id, errno));
                }

                /* do the list services */
                rv = ib_list_services(msgp, errstring);
                if (rv != CFGA_IB_OK) {
                        DPRINTF("cfga_private_func: ib_list_services failed\n");
                        return (ib_err_msg(errstring, rv, ap_id, errno));
                }

        /* -x update_ioc_conf */
        } else if (strncmp(func, IB_UPDATE_IOC_CONF, 17) == 0) {
                uint_t misc_arg;

                /* Supported only with root privilege */
                if (geteuid() != 0) {
                        return (ib_err_msg(errstring, CFGA_IB_PRIV_ERR, ap_id,
                            errno));
                }

                /*
                 * check: Only supported on fabric ap_id or IOC APID
                 * IOC APID does not have any commas in it.
                 */
                if (fab_apid == NULL ||
                    (fab_apid != NULL && strstr(fab_apid, ",") != NULL)) {
                        DPRINTF("cfga_private_func: fabric/IOC apid needed\n");
                        return (ib_err_msg(errstring, CFGA_IB_INVALID_OP_ERR,
                            ap_id, errno));
                }

                /* Check w/ user if it is ok to do this operation */
                len = strlen(IB_CONFIRM5) + 10;
                if ((msg = (char *)calloc(len, 1)) != NULL) {
                        (void) snprintf(msg, len, "%s\nContinue", IB_CONFIRM5);
                }

                /* If the user fails to confirm, return */
                if (!ib_confirm(confp, msg)) {
                        free(msg);
                        return (CFGA_NACK);
                }
                free(msg);

                misc_arg = (strcmp(fab_apid, IBNEX_FABRIC) == 0) ?
                    IBNEX_BASE_APID : IBNEX_DYN_APID;

                /* Reprobe and update IOC(s) configuration */
                rv = ib_do_control_ioctl((char *)ap_id, IBNEX_UPDATE_IOC_CONF,
                    0, misc_arg, 0, &info_len);

                if (rv != 0) {
                        DPRINTF("cfga_private_func: ib_do_control_ioctl "
                            "failed :%d\n", rv);
                        return (ib_err_msg(errstring, CFGA_IB_DEVCTL_ERR,
                            ap_id, errno));
                }
        } else {
                DPRINTF("cfga_private_func: unrecognized command.\n");
                (void) cfga_help(msgp, options, flags);
                errno = EINVAL;
                return (CFGA_INVAL);
        }

        return (ib_err_msg(errstring, rv, ap_id, errno));
}


/*
 * Function:
 *      cfga_test
 * Input:
 *      ap_id           - The attachment point of an IB fabric
 *      options         - Test command options passed by the cfgadm(8)
 *      msgp            - cfgadm error message for this plugin
 *      errstring       - This contains error msg if command fails
 *      flags           - cfgadm(8) flags
 * Output:
 *      NONE
 * Returns:
 *      CFGA_OPNOTSUPP
 * Description:
 *      Do "cfgadm -t"
 */
/*ARGSUSED*/
cfga_err_t
cfga_test(const char *ap_id, const char *options, struct cfga_msg *msgp,
    char **errstring, cfga_flags_t flags)
{
        (void) cfga_help(msgp, options, flags);
        return (CFGA_OPNOTSUPP);
}


/*
 * Function:
 *      ib_fill_static_apids
 * Input:
 *      ap_id           - The static attachment point of an IB device
 *      clp             - The returned "list" information array
 * Output:
 *      NONE
 * Returns:
 *      Fills up the "list" information array for the static attachment point
 * Description:
 *      IB fabric supports two types of static attachment points.
 *      One is fabric and other is for the HCAs. This fills up
 *      "cfga_list_data_t" for static attachment points.
 */
static cfga_ib_ret_t
ib_fill_static_apids(char *ap_id, cfga_list_data_t *clp)
{
        int     rv, l_err;
        char    *ap_id_log = NULL;

        /* Get /dev/cfg path to corresponding to the physical ap_id */
        /* Remember ap_id_log must be freed */
        if (ib_physpath_to_devlink(ap_id, &ap_id_log,
            &l_err) != ICFGA_OK) {
                DPRINTF("ib_fill_static_apids: "
                    "ib_physpath_to_devlink failed\n");
                return (CFGA_IB_DEVLINK_ERR);
        }
        assert(ap_id_log != NULL);

        /* Get logical ap-id corresponding to the physical */
        if (strstr(ap_id_log, CFGA_DEV_DIR) == NULL) {
                DPRINTF("ib_fill_static_apids: devlink doesn't contain "
                    "/dev/cfg\n");
                free(ap_id_log);
                return (CFGA_IB_DEVLINK_ERR);
        }

        clp->ap_cond = CFGA_COND_OK;
        clp->ap_r_state = CFGA_STAT_CONNECTED;
        clp->ap_o_state = CFGA_STAT_CONFIGURED;
        clp->ap_class[0] = '\0';        /* Filled by libcfgadm */
        clp->ap_busy = 0;
        clp->ap_status_time = (time_t)-1;
        (void) snprintf(clp->ap_log_id, sizeof (clp->ap_log_id), "%s",
            /* Strip off /dev/cfg/ */ ap_id_log + strlen(CFGA_DEV_DIR) + 1);
        (void) strlcpy(clp->ap_phys_id, ap_id, sizeof (clp->ap_phys_id));

        /* Static IB apid */
        if (strstr((char *)ap_id, IB_FABRIC_APID_STR) != NULL)  {
                (void) strlcpy(clp->ap_type, IB_FABRIC_TYPE,
                    sizeof (clp->ap_type));     /* Fill in type */
                (void) strlcpy(clp->ap_info, IB_FABRIC_INFO,
                    sizeof (clp->ap_info));

        } else {        /* Static HCA apid */
                size_t  size = 0;
                uchar_t *data = NULL;

                (void) strlcpy(clp->ap_type, IB_HCA_TYPE,
                    sizeof (clp->ap_type));     /* Fill in type */

                rv = ib_do_control_ioctl(ap_id, IBNEX_HCA_VERBOSE_SZ,
                    IBNEX_HCA_VERBOSE_INFO, 0, (void **)&data, &size);
                if (rv != 0) {
                        DPRINTF("ib_fill_static_apids: ib_do_control_ioctl "
                            "failed :%d\n", rv);
                        free(ap_id_log);
                        S_FREE(data);
                        return (CFGA_IB_IOCTL_ERR);
                }

                (void) strlcpy(clp->ap_info, (char *)data,
                    sizeof (clp->ap_info));
                S_FREE(data);
        }
        free(ap_id_log);
        return (CFGA_IB_OK);
}


/*
 * Function:
 *      cfga_list_ext
 * Input:
 *      ap_id           - The attachment point of an IB fabric
 *      ap_id_list      - The returned "list" information array
 *      nlistp          - Number of elements in the "list" information array
 *      options         - List command options passed by the cfgadm(8)
 *      listopts        - "-s" specific options
 *      errstring       - This contains error msg if command fails
 *      flags           - cfgadm(8) flags
 * Output:
 *      NONE
 * Returns:
 *      If the command succeeded, cfgadm -l output otherwise an error
 * Description:
 *      Do cfgadm -l
 */
/*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                     expand = 0;
        int                     i, index, count;
        int                     show_dynamic = 0;
        size_t                  num_devices = 0;
        size_t                  num_hcas = 0;
        size_t                  snap_size = 0;
        uchar_t                 *snap_data = NULL;
        nvpair_t                *nvp = NULL;    /* for lint purposes */
        nvlist_t                *nvl = NULL;
        boolean_t               apid_matched = B_FALSE; /* for valid ap_id */
        cfga_ib_ret_t           rv = CFGA_IB_OK;
        cfga_list_data_t        *clp = NULL;

        if ((rv = ib_verify_params(ap_id, options, errstring)) != CFGA_IB_OK) {
                return (ib_err_msg(errstring, rv, ap_id, errno));
        }

        /* make sure we have a valid ap_id_list */
        if (ap_id_list == NULL || nlistp == NULL) {
                DPRINTF("cfga_list_ext: list = NULL or nlistp = NULL\n");
                return (ib_err_msg(errstring, CFGA_IB_INVAL_ARG_ERR,
                    ap_id, errno));
        }

        DPRINTF("cfga_list_ext: ap_id = %s\n", ap_id);

        if ((flags & CFGA_FLAG_LIST_ALL) == CFGA_FLAG_LIST_ALL) {
                expand = 1;             /* -a flag passed */
        }

        if (GET_DYN(ap_id) != NULL) {
                show_dynamic = 1;
        }

        if ((expand == 1) &&    /* -a option passed */
            (strstr((char *)ap_id, IB_FABRIC_APID_STR) != NULL)) {
                /*
                 * Figure out how many IOC/Port/Pseudo
                 * devices exist in the system?
                 */
                if ((rv = ib_do_control_ioctl((char *)ap_id,
                    IBNEX_NUM_DEVICE_NODES, 0, 0, 0, &num_devices)) !=
                    CFGA_IB_OK) {
                        DPRINTF("cfga_list_ext: ib_do_control_ioctl "
                            "IBNEX_NUM_DEVICE_NODES failed :%d\n", rv);
                        if (errno == ENOENT)
                                return (CFGA_APID_NOEXIST);
                        return (ib_err_msg(errstring, rv, ap_id, errno));
                }

                DPRINTF("cfga_list_ext: num_devices = %d\n", num_devices);
        }

        /* Figure out how many HCA nodes exist in the system. */
        if ((rv = ib_do_control_ioctl((char *)ap_id, IBNEX_NUM_HCA_NODES, 0, 0,
            0, &num_hcas)) != CFGA_IB_OK) {
                DPRINTF("cfga_list_ext: ib_do_control_ioctl "
                    "IBNEX_NUM_HCA_NODES failed :%d\n", rv);
                if (errno == ENOENT)
                        return (CFGA_APID_NOEXIST);
                return (ib_err_msg(errstring, rv, ap_id, errno));
        }
        DPRINTF("cfga_list_ext: num_hcas = %d\n", num_hcas);

        /*
         * No HCAs or IOC/VPPA/Port/HCA_SVC/Pseudo devices seen (non-IB system)
         */
        if (!(num_hcas || num_devices)) {
                DPRINTF("cfga_list_ext: no IB devices found\n");
                return (CFGA_APID_NOEXIST);
        }

        /*
         * *nlistp contains to how many APIDs to show w/ cfgadm -l.
         * If ap_id is "fabric" then
         *      *nlistp is all Dynamic Apids + One more for "fabric"
         * If ap_id is "HCA" ap_id then
         *      *nlistp is 1
         * Note that each HCA is a static APID, so nlistp will be 1 always
         * and this function will be called N times for each of the N HCAs
         * in the host.
         */
        if (strstr((char *)ap_id, IB_FABRIC_APID_STR) != NULL) {
                *nlistp = num_devices + 1;

        } else {
                /* Assume it as a HCA ap_id */
                *nlistp = 1;
        }

        /* Allocate storage for passing "list" info back */
        if ((*ap_id_list = (cfga_list_data_t *)calloc(*nlistp,
            sizeof (cfga_list_data_t))) == NULL) {
                DPRINTF("cfga_list_ext: malloc for cfga_list_data_t failed. "
                    "errno: %d\n", errno);
                return (ib_err_msg(errstring, CFGA_IB_ALLOC_FAIL,
                    ap_id, errno));
        }

        /*
         * Only static ap_id is ib_fabric:
         * If -a options isn't specified then only show the static ap_id.
         */
        if (!show_dynamic) {
                clp = &(*ap_id_list[0]);

                if ((rv = ib_fill_static_apids((char *)ap_id, clp)) !=
                    CFGA_IB_OK) {
                        S_FREE(*ap_id_list);
                        return (ib_err_msg(errstring, rv, ap_id, errno));
                }
                apid_matched = B_TRUE;
        }

        /*
         * No -a specified
         * No HCAs or IOC/VPPA/HCA_SVC/Port/Pseudo devices seen (non-IB system)
         */
        if (!expand || (!num_hcas && !num_devices)) {
                if (!show_dynamic)
                        return (CFGA_OK);
        }

        if (strstr((char *)ap_id, IB_FABRIC_APID_STR) != NULL) {
                rv = ib_do_control_ioctl((char *)ap_id, IBNEX_SNAPSHOT_SIZE,
                    IBNEX_GET_SNAPSHOT, IBNEX_DONOT_PROBE_FLAG,
                    (void **)&snap_data, &snap_size);
                if (rv != 0) {
                        DPRINTF("cfga_list_ext: ib_do_control_ioctl "
                            "failed :%d\n", rv);
                        S_FREE(*ap_id_list);
                        S_FREE(snap_data);
                        return (ib_err_msg(errstring, rv, ap_id, errno));
                }

                if (nvlist_unpack((char *)snap_data, snap_size, &nvl, 0)) {
                        DPRINTF("cfga_list_ext: nvlist_unpack 1 failed %p\n",
                            snap_data);
                        S_FREE(*ap_id_list);
                        S_FREE(snap_data);
                        return (ib_err_msg(errstring, CFGA_IB_NVLIST_ERR,
                            ap_id, errno));
                }

                /*
                 * In kernel a nvlist is build per ap_id which contains
                 * information that is displayed using cfgadm -l.
                 * For IB devices only these 6 items are shown:
                 *      ap_id, type, occupant, receptacle, condition and info
                 *
                 * In addition, one could specify a dynamic ap_id from
                 * command-line. Then cfgadm -l should show only that
                 * ap_id and skip rest.
                 */
                index = 1; count = 0;
                while (nvp = nvlist_next_nvpair(nvl, nvp)) {
                        int32_t intval = 0;
                        int32_t node_type;
                        char    *info;
                        char    *nv_apid;
                        char    *name = nvpair_name(nvp);

                        /* start of with next device */
                        if (count == IB_NUM_NVPAIRS) {
                                count = 0;
                                ++index;
                        }

                        /*
                         * Check if the index doesn't go beyond the
                         * device number. If it goes, stop the loop
                         * here not to cause the heap corruption.
                         */
                        if (show_dynamic == 0 && index > num_devices)
                                break;

                        /* fill up data into "clp" */
                        clp =  (show_dynamic != 0) ? &(*ap_id_list[0]) :
                            &(ap_id_list[0][index]);

                        /* First nvlist entry is "ap_id" always */
                        if (strcmp(name, IBNEX_NODE_APID_NVL) == 0) {
                                (void) nvpair_value_string(nvp, &nv_apid);
                                DPRINTF("cfga_list_ext: Name = %s, apid = %s\n",
                                    name, nv_apid);

                                /*
                                 * If a dynamic ap_id is specified in the
                                 * command-line, skip all entries until
                                 * the one needed matches.
                                 */
                                if (show_dynamic &&
                                    strstr(ap_id, nv_apid) == NULL) {
                                        DPRINTF("cfga_list_ext: NO MATCH\n");

                                        /*
                                         * skip rest of the entries of this
                                         * device.
                                         */
                                        for (i = 0; i < IB_NUM_NVPAIRS - 1; i++)
                                                nvp = nvlist_next_nvpair(nvl,
                                                    nvp);
                                        count = 0;      /* reset it */
                                        continue;
                                }

                                apid_matched = B_TRUE;

                                /* build the physical ap_id */
                                if (strstr(ap_id, DYN_SEP) == NULL) {
                                        (void) snprintf(clp->ap_phys_id,
                                            sizeof (clp->ap_phys_id), "%s%s%s",
                                            ap_id, DYN_SEP, nv_apid);
                                } else {
                                        (void) snprintf(clp->ap_phys_id,
                                            sizeof (clp->ap_phys_id), "%s",
                                            ap_id);
                                }

                                /* ensure that this is a valid apid */
                                if (ib_verify_valid_apid(clp->ap_phys_id) !=
                                    0) {
                                        DPRINTF("cfga_list_ext: "
                                            "not a valid IB ap_id\n");
                                        S_FREE(*ap_id_list);
                                        S_FREE(snap_data);
                                        nvlist_free(nvl);
                                        return (ib_err_msg(errstring,
                                            CFGA_IB_AP_ERR, ap_id, errno));
                                }

                                /* build the logical ap_id */
                                (void) snprintf(clp->ap_log_id,
                                    sizeof (clp->ap_log_id), "ib%s%s",
                                    DYN_SEP, nv_apid);
                                DPRINTF("cfga_list_ext: ap_pi = %s, ap_li = %s,"
                                    "\nap_info = %s\n", clp->ap_phys_id,
                                    clp->ap_log_id, clp->ap_info);
                                ++count;

                        } else if (strcmp(name, IBNEX_NODE_INFO_NVL) == 0) {
                                (void) nvpair_value_string(nvp, &info);
                                DPRINTF("cfga_list_ext: Name = %s, info = %s\n",
                                    name, info);
                                (void) snprintf(clp->ap_info,
                                    sizeof (clp->ap_info), "%s", info);
                                ++count;

                        } else if (strcmp(name, IBNEX_NODE_TYPE_NVL) == 0) {
                                (void) nvpair_value_int32(nvp, &node_type);
                                if (node_type == IBNEX_PORT_NODE_TYPE) {
                                        (void) snprintf(clp->ap_type,
                                            sizeof (clp->ap_type), "%s",
                                            IB_PORT_TYPE);
                                } else if (node_type == IBNEX_VPPA_NODE_TYPE) {
                                        (void) snprintf(clp->ap_type,
                                            sizeof (clp->ap_type), "%s",
                                            IB_VPPA_TYPE);
                                } else if (node_type ==
                                    IBNEX_HCASVC_NODE_TYPE) {
                                        (void) snprintf(clp->ap_type,
                                            sizeof (clp->ap_type), "%s",
                                            IB_HCASVC_TYPE);
                                } else if (node_type == IBNEX_IOC_NODE_TYPE) {
                                        (void) snprintf(clp->ap_type,
                                            sizeof (clp->ap_type), "%s",
                                            IB_IOC_TYPE);
                                } else if (node_type ==
                                    IBNEX_PSEUDO_NODE_TYPE) {
                                        (void) snprintf(clp->ap_type,
                                            sizeof (clp->ap_type), "%s",
                                            IB_PSEUDO_TYPE);
                                }
                                DPRINTF("cfga_list_ext: Name = %s, type = %x\n",
                                    name, intval);
                                ++count;

                        } else if (strcmp(name, IBNEX_NODE_RSTATE_NVL) == 0) {
                                (void) nvpair_value_int32(nvp, &intval);

                                if (intval == AP_RSTATE_EMPTY)
                                        clp->ap_r_state = CFGA_STAT_EMPTY;
                                else if (intval == AP_RSTATE_DISCONNECTED)
                                        clp->ap_r_state =
                                            CFGA_STAT_DISCONNECTED;
                                else if (intval == AP_RSTATE_CONNECTED)
                                        clp->ap_r_state = CFGA_STAT_CONNECTED;
                                DPRINTF("cfga_list_ext: Name = %s, "
                                    "rstate = %x\n", name, intval);
                                ++count;

                        } else if (strcmp(name, IBNEX_NODE_OSTATE_NVL) == 0) {
                                (void) nvpair_value_int32(nvp, &intval);

                                if (intval == AP_OSTATE_CONFIGURED)
                                        clp->ap_o_state = CFGA_STAT_CONFIGURED;
                                else if (intval == AP_OSTATE_UNCONFIGURED)
                                        clp->ap_o_state =
                                            CFGA_STAT_UNCONFIGURED;
                                DPRINTF("cfga_list_ext: Name = %s, "
                                    "ostate = %x\n", name, intval);
                                ++count;

                        } else if (strcmp(name, IBNEX_NODE_COND_NVL) == 0) {
                                (void) nvpair_value_int32(nvp, &intval);

                                if (intval == AP_COND_OK)
                                        clp->ap_cond = CFGA_COND_OK;
                                else if (intval == AP_COND_FAILING)
                                        clp->ap_cond = CFGA_COND_FAILING;
                                else if (intval == AP_COND_FAILED)
                                        clp->ap_cond = CFGA_COND_FAILED;
                                else if (intval == AP_COND_UNUSABLE)
                                        clp->ap_cond = CFGA_COND_UNUSABLE;
                                else if (intval == AP_COND_UNKNOWN)
                                        clp->ap_cond = CFGA_COND_UNKNOWN;
                                DPRINTF("cfga_list_ext: Name = %s, "
                                    "condition = %x\n", name, intval);
                                ++count;
                        }

                        clp->ap_class[0] = '\0'; /* Filled by libcfgadm */
                        clp->ap_busy = 0;
                        clp->ap_status_time = (time_t)-1;
                } /* end of while */
        }

        S_FREE(snap_data);
        nvlist_free(nvl);

        /*
         * if a cmdline specified ap_id doesn't match the known list of ap_ids
         * then report an error right away
         */
        rv = (apid_matched ==  B_TRUE) ? CFGA_IB_OK : CFGA_IB_AP_ERR;
        return (ib_err_msg(errstring, rv, ap_id, errno));
}


/*
 * Function:
 *      cfga_msg
 * Input:
 *      msgp            - cfgadm error message for this plugin
 *      str             - string to be passed on to the message
 * Output:
 *      NONE
 * Returns:
 *      NONE
 * Description:
 *      This routine accepts a variable number of message IDs and
 *      constructs a corresponding error string which is printed
 *      via the message print routine argument.
 */
void
cfga_msg(struct cfga_msg *msgp, const char *str)
{
        int len;
        char *q;

        if (msgp == NULL || msgp->message_routine == NULL) {
                DPRINTF("cfga_msg: msg\n");
                return;
        }

        if ((len = strlen(str)) == 0) {
                DPRINTF("cfga_msg: null str\n");
                return;
        }

        if ((q = (char *)calloc(len + 1, 1)) == NULL) {
                DPRINTF("cfga_msg: null q\n");
                return;
        }

        (void) strlcpy(q, str, len + 1);
        (*msgp->message_routine)(msgp->appdata_ptr, q);

        free(q);
}


/*
 * Function:
 *      cfga_help
 * Input:
 *      msgp            - Help message passed on to cfgadm(8)
 *      options         - Help message options passed on to cfgadm(8)
 *      flags           - cfgadm(8) flags
 * Output:
 *      NONE
 * Returns:
 *      Were we able to print cfgadm help or not for this plugin
 * Description:
 *      Print cfgadm help for this plugin
 */
/* ARGSUSED */
cfga_err_t
cfga_help(struct cfga_msg *msgp, const char *options, cfga_flags_t flags)
{
        DPRINTF("cfga_help:\n");

        if (options) {
                cfga_msg(msgp, dgettext(TEXT_DOMAIN, ib_help[
                    CFGA_IB_HELP_UNKNOWN]));
                cfga_msg(msgp, options);
        }

        /* Print messages array */
        cfga_msg(msgp, dgettext(TEXT_DOMAIN, ib_help[CFGA_IB_HELP_HEADER]));
        cfga_msg(msgp, ib_help[CFGA_IB_HELP_CONFIG]);
        cfga_msg(msgp, ib_help[CFGA_IB_HELP_LIST]);
        cfga_msg(msgp, ib_help[CFGA_IB_HELP_UPD_PKEY]);
        cfga_msg(msgp, ib_help[CFGA_IB_HELP_CONF_FILE1]);
        cfga_msg(msgp, ib_help[CFGA_IB_HELP_CONF_FILE2]);
        cfga_msg(msgp, ib_help[CFGA_IB_HELP_UPD_IOC_CONF]);
        cfga_msg(msgp, ib_help[CFGA_IB_HELP_UNCFG_CLNTS]);

        return (CFGA_OK);
}


/*
 * Function:
 *      ib_confirm
 * Input:
 *      confp           - The "cfga" structure that confirms a cfgadm query
 *      msg             - The message that needs confirmation
 * Output:
 *      None
 * Returns:
 *      If a user entered YES or NO
 * Description:
 *      Queries a user if it is ok to proceed with an operation or not.
 *      Returns user's response.
 */
static int
ib_confirm(struct cfga_confirm *confp, char *msg)
{
        int rval;

        /* check that "confirm" function exists */
        if (confp == NULL || confp->confirm == NULL) {
                return (0);
        }

        /* Call cfgadm provided "confirm" function */
        rval = (*confp->confirm)(confp->appdata_ptr, msg);
        DPRINTF("ib_confirm: %d\n", rval);

        return (rval);
}


/*
 * Function:
 *      ib_get_devicepath
 * Input:
 *      ap_id           - The dynamic attachment point of an IB device
 * Output:
 *      None
 * Returns:
 *      devpath if it exists; otherwise NULL
 * Description:
 *      Returns the devicepath for a dynamic attachment point of an IB device
 */
static char *
ib_get_devicepath(const char *ap_id)
{
        char            *devpath = NULL;
        size_t          size;

        /* Get device path sizes */
        if (ib_do_control_ioctl((char *)ap_id, IBNEX_DEVICE_PATH_SZ,
            IBNEX_GET_DEVICE_PATH, 0, (void **)&devpath, &size) == CFGA_IB_OK) {
                DPRINTF("ib_get_devicepath: get device path ioctl ok\n");
                return (devpath);

        } else {
                DPRINTF("ib_get_devicepath: get device path ioctl failed\n");
                return ((char *)NULL);
        }
}