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#include <sys/fibre-channel/impl/fc_error.h>
2N/A
2N/A/* Structure for walking the tree */
2N/Atypedef struct {
2N/A apid_t *apidp;
2N/A char *xport_logp;
2N/A ldata_list_t *listp;
2N/A fpcfga_cmd_t cmd;
2N/A cfga_stat_t chld_config;
2N/A cfga_type_t xport_type;
2N/A cfga_stat_t xport_rstate;
2N/A fpcfga_ret_t ret;
2N/A int l_errno;
2N/A} fpcfga_list_t;
2N/A
2N/Atypedef struct {
2N/A uint_t itype;
2N/A const char *ntype;
2N/A const char *name;
2N/A} fpcfga_devtype_t;
2N/A
2N/A#define ERR_INQ_DTYPE 0xff
2N/A
2N/A/* The TYPE field is parseable and should not contain spaces */
2N/A#define FP_FC_PORT_TYPE "fc"
2N/A#define FP_FC_PORT_ERROR "fc-error"
2N/A#define FP_FC_FABRIC_PORT_TYPE "fc-fabric"
2N/A#define FP_FC_PUBLIC_PORT_TYPE "fc-public"
2N/A#define FP_FC_PRIVATE_PORT_TYPE "fc-private"
2N/A#define FP_FC_PT_TO_PT_PORT_TYPE "fc-pt_to_pt"
2N/A
2N/A/* Indicates no plag passing */
2N/A#define NO_FLAG 0
2N/A
2N/A/* defines for retry algorithm */
2N/A#define OPEN_RETRY_COUNT 5
2N/A#define OPEN_RETRY_INTERVAL 10000 /* 1/100 of a sec. */
2N/A#define IOCTL_RETRY_COUNT 5
2N/A#define IOCTL_RETRY_INTERVAL 5000000 /* 5 sec */
2N/A
2N/A/* define for fcp scsi passthru wait */
2N/A#define FCP_SCSI_CMD_TIMEOUT 10
2N/A
2N/A/* define for fcp pseudo node */
2N/A#define FCP_PATH "/devices/pseudo/fcp@0:fcp"
2N/A
2N/A/* Function prototypes */
2N/Astatic fpcfga_ret_t postprocess_list_data(const ldata_list_t *listp,
2N/A fpcfga_cmd_t cmd, cfga_stat_t chld_config, int *np, uint_t flags);
2N/Astatic int stat_fc_dev(di_node_t node, void *arg);
2N/Astatic int stat_FCP_dev(di_node_t node, void *arg);
2N/Astatic fpcfga_ret_t do_stat_fca_xport(fpcfga_list_t *lap, int limited_stat,
2N/A HBA_PORTATTRIBUTES portAttrs);
2N/Astatic int get_xport_state(di_node_t node, void *arg);
2N/A
2N/Astatic fpcfga_ret_t do_stat_fc_dev(const di_node_t node, const char *nodepath,
2N/A fpcfga_list_t *lap, int limited_stat);
2N/Astatic fpcfga_ret_t do_stat_FCP_dev(const di_node_t node, const char *nodepath,
2N/A fpcfga_list_t *lap, int limited_stat);
2N/Astatic cfga_stat_t xport_devinfo_to_recep_state(uint_t xport_di_state);
2N/Astatic cfga_stat_t dev_devinfo_to_occupant_state(uint_t dev_di_state);
2N/Astatic void get_hw_info(di_node_t node, cfga_list_data_t *clp);
2N/Astatic const char *get_device_type(di_node_t);
2N/Astatic fpcfga_ret_t init_ldata_for_accessible_dev(const char *dyncomp,
2N/A uchar_t inq_type, fpcfga_list_t *lap);
2N/Astatic fpcfga_ret_t init_ldata_for_accessible_FCP_dev(const char *port_wwn,
2N/A int num_luns, struct report_lun_resp *resp_buf,
2N/A fpcfga_list_t *larg, int *l_errnop);
2N/Astatic fpcfga_ret_t is_dyn_ap_on_ldata_list(const char *port_wwn,
2N/A const ldata_list_t *listp, ldata_list_t **matchldpp, int *l_errno);
2N/Astatic fpcfga_ret_t is_FCP_dev_ap_on_ldata_list(const char *port_wwn,
2N/A const int lun_num, ldata_list_t *ldatap, ldata_list_t **matchldpp);
2N/A
2N/Astatic fpcfga_ret_t init_ldata_for_mpath_dev(di_path_t path, char *port_wwn,
2N/A int *l_errnop, fpcfga_list_t *lap);
2N/Astatic fpcfga_ret_t insert_ldata_to_ldatalist(const char *port_wwn,
2N/A int *lun_nump, ldata_list_t *listp, ldata_list_t **ldatapp);
2N/Astatic fpcfga_ret_t insert_fc_dev_ldata(const char *port_wwn,
2N/A ldata_list_t *listp, ldata_list_t **ldatapp);
2N/Astatic fpcfga_ret_t insert_FCP_dev_ldata(const char *port_wwn, int lun_num,
2N/A ldata_list_t *listp, ldata_list_t **ldatapp);
2N/Astatic int stat_path_info_fc_dev(di_node_t root, fpcfga_list_t *lap,
2N/A int *l_errnop);
2N/Astatic int stat_path_info_FCP_dev(di_node_t root, fpcfga_list_t *lap,
2N/A int *l_errnop);
2N/Astatic fpcfga_ret_t get_accessible_FCP_dev_ldata(const char *dyncomp,
2N/A fpcfga_list_t *lap, int *l_errnop);
2N/Astatic fpcfga_ret_t get_standard_inq_data(const char *xport_phys,
2N/A const char *dyncomp, uchar_t *lun_num, struct scsi_inquiry **inq_buf,
2N/A int *l_errnop);
2N/Astatic void init_fcp_scsi_cmd(struct fcp_scsi_cmd *fscsi, uchar_t *lun_num,
2N/A la_wwn_t *pwwn, void *scmdbuf, size_t scmdbuf_len, void *respbuf,
2N/A size_t respbuf_len, void *sensebuf, size_t sensebuf_len);
2N/Astatic fpcfga_ret_t issue_fcp_scsi_cmd(const char *xport_phys,
2N/A struct fcp_scsi_cmd *fscsi, int *l_errnop);
2N/Astatic uchar_t get_inq_dtype(char *xport_phys, char *dyncomp, HBA_HANDLE handle,
2N/A HBA_PORTATTRIBUTES *portAttrs, HBA_PORTATTRIBUTES *discPortAttrs);
2N/A
2N/Astatic fpcfga_devtype_t device_list[] = {
2N/A { DTYPE_DIRECT, DDI_NT_BLOCK_CHAN, "disk"},
2N/A { DTYPE_DIRECT, DDI_NT_BLOCK, "disk"},
2N/A { DTYPE_DIRECT, DDI_NT_BLOCK_WWN, "disk"},
2N/A { DTYPE_DIRECT, DDI_NT_BLOCK_FABRIC, "disk"},
2N/A { DTYPE_SEQUENTIAL, DDI_NT_TAPE, "tape"},
2N/A { DTYPE_PRINTER, NULL, "printer"},
2N/A { DTYPE_PROCESSOR, NULL, "processor"},
2N/A { DTYPE_WORM, NULL, "WORM"},
2N/A { DTYPE_RODIRECT, DDI_NT_CD_CHAN, "CD-ROM"},
2N/A { DTYPE_RODIRECT, DDI_NT_CD, "CD-ROM"},
2N/A { DTYPE_SCANNER, NULL, "scanner"},
2N/A { DTYPE_OPTICAL, NULL, "optical"},
2N/A { DTYPE_CHANGER, NULL, "med-changer"},
2N/A { DTYPE_COMM, NULL, "comm-device"},
2N/A { DTYPE_ARRAY_CTRL, NULL, "array-ctrl"},
2N/A { DTYPE_ESI, NULL, "ESI"},
2N/A /*
2N/A * This has to be the LAST entry for DTYPE_UNKNOWN_INDEX.
2N/A * Add entries before this.
2N/A */
2N/A { DTYPE_UNKNOWN, NULL, "unknown"}
2N/A};
2N/A
2N/A#define N_DEVICE_TYPES (sizeof (device_list) / sizeof (device_list[0]))
2N/A
2N/A#define DTYPE_UNKNOWN_INDEX (N_DEVICE_TYPES - 1)
2N/A
2N/A/*
2N/A * Main routine for list operation.
2N/A * It calls various routines to consturct ldata list and
2N/A * postprocess the list data.
2N/A *
2N/A * Overall algorithm:
2N/A * Get the device list on input hba port and construct ldata list for
2N/A * accesible devices.
2N/A * Stat hba port and devices through walking the device tree.
2N/A * Verify the validity of the list data.
2N/A */
2N/Afpcfga_ret_t
2N/Ado_list(
2N/A apid_t *apidp,
2N/A fpcfga_cmd_t cmd,
2N/A ldata_list_t **llpp,
2N/A int *nelemp,
2N/A char **errstring)
2N/A{
2N/A int n = -1, l_errno = 0, limited_stat;
2N/A walkarg_t walkarg;
2N/A fpcfga_list_t larg = {NULL};
2N/A fpcfga_ret_t ret;
2N/A la_wwn_t pwwn;
2N/A char *dyncomp = NULL;
2N/A HBA_HANDLE handle;
2N/A HBA_PORTATTRIBUTES portAttrs;
2N/A HBA_PORTATTRIBUTES discPortAttrs;
2N/A HBA_STATUS status;
2N/A int portIndex, discIndex;
2N/A int retry;
2N/A uchar_t inq_dtype;
2N/A
2N/A if (*llpp != NULL || *nelemp != 0) {
2N/A return (FPCFGA_ERR);
2N/A }
2N/A
2N/A /* Create the hba logid (also base component of logical ap_id) */
2N/A ret = make_xport_logid(apidp->xport_phys, &larg.xport_logp, &l_errno);
2N/A if (ret != FPCFGA_OK) {
2N/A cfga_err(errstring, l_errno, ERR_LIST, 0);
2N/A return (FPCFGA_ERR);
2N/A }
2N/A
2N/A assert(larg.xport_logp != NULL);
2N/A
2N/A larg.cmd = cmd;
2N/A larg.apidp = apidp;
2N/A larg.xport_rstate = CFGA_STAT_NONE;
2N/A
2N/A if ((ret = findMatchingAdapterPort(larg.apidp->xport_phys, &handle,
2N/A &portIndex, &portAttrs, errstring)) != FPCFGA_OK) {
2N/A S_FREE(larg.xport_logp);
2N/A return (ret);
2N/A }
2N/A
2N/A /*
2N/A * If stating a specific device, we will do limited stat on fca port.
2N/A * otherwise full stat on fca part is required.
2N/A * If stating a specific device we don't know if it exists or is
2N/A * configured yet. larg.ret is set to apid noexist for do_stat_dev.
2N/A * otherwise larg.ret is set to ok initially.
2N/A */
2N/A if (larg.cmd == FPCFGA_STAT_FC_DEV) {
2N/A limited_stat = 1; /* for do_stat_fca_xport */
2N/A larg.ret = FPCFGA_APID_NOEXIST; /* for stat_fc_dev */
2N/A } else {
2N/A limited_stat = 0; /* for do_stat_fca_xport */
2N/A larg.ret = FPCFGA_OK; /* for stat_fc_dev */
2N/A }
2N/A
2N/A /* For all list commands, the fca port needs to be stat'ed */
2N/A if ((ret = do_stat_fca_xport(&larg, limited_stat,
2N/A portAttrs)) != FPCFGA_OK) {
2N/A cfga_err(errstring, larg.l_errno, ERR_LIST, 0);
2N/A list_free(&larg.listp);
2N/A S_FREE(larg.xport_logp);
2N/A HBA_CloseAdapter(handle);
2N/A HBA_FreeLibrary();
2N/A return (ret);
2N/A }
2N/A
2N/A#ifdef DEBUG
2N/A if (limited_stat) {
2N/A assert(larg.listp == NULL);
2N/A } else {
2N/A assert(larg.listp != NULL);
2N/A }
2N/A#endif
2N/A /*
2N/A * If stat'ing a FCA port or ALL, we have the bus stat data at
2N/A * this point.
2N/A * Assume that the bus has no configured children.
2N/A */
2N/A larg.chld_config = CFGA_STAT_UNCONFIGURED;
2N/A
2N/A switch (larg.cmd) {
2N/A case FPCFGA_STAT_FC_DEV:
2N/A /* la_wwn_t has uchar_t raw_wwn[8] thus no need to free. */
2N/A if (cvt_dyncomp_to_lawwn(apidp->dyncomp, &pwwn) != 0) {
2N/A cfga_err(errstring, 0, ERR_LIST, 0);
2N/A list_free(&larg.listp);
2N/A S_FREE(larg.xport_logp);
2N/A HBA_CloseAdapter(handle);
2N/A HBA_FreeLibrary();
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A /*
2N/A * if the dyncomp exists on disco ports construct list_data
2N/A * otherwise return FPCFGA_APID_NOEXIST.
2N/A */
2N/A retry = 0;
2N/A do {
2N/A status = getPortAttrsByWWN(handle,
2N/A *((HBA_WWN *)(&pwwn)), &discPortAttrs);
2N/A if (status == HBA_STATUS_ERROR_STALE_DATA) {
2N/A /* get Port Attributes again after refresh. */
2N/A HBA_RefreshInformation(handle);
2N/A } else {
2N/A break; /* either okay or some other error */
2N/A }
2N/A } while (retry++ < HBA_MAX_RETRIES);
2N/A
2N/A if (status == HBA_STATUS_OK) {
2N/A /*
2N/A * if dyncomp found in disco ports
2N/A * construct ldata_list and return.
2N/A * otherwise continue to stat on dev tree with
2N/A * larg.ret set to access_ok which informs stat_fc_dev
2N/A * the existence of device on disco ports.
2N/A *
2N/A * if path is null that guatantees the node is not
2N/A * configured. if node is detached the path
2N/A * is incomplete and not usable for further
2N/A * operations like uscsi_inq so take care of it here.
2N/A */
2N/A inq_dtype = get_inq_dtype(apidp->xport_phys,
2N/A apidp->dyncomp, handle, &portAttrs, &discPortAttrs);
2N/A
2N/A if (init_ldata_for_accessible_dev(apidp->dyncomp,
2N/A inq_dtype, &larg) != FPCFGA_OK) {
2N/A cfga_err(errstring, larg.l_errno,
2N/A ERR_LIST, 0);
2N/A list_free(&larg.listp);
2N/A S_FREE(larg.xport_logp);
2N/A HBA_CloseAdapter(handle);
2N/A HBA_FreeLibrary();
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A if (apidp->lunlist == NULL) {
2N/A n = 0;
2N/A if (postprocess_list_data(
2N/A larg.listp, cmd,
2N/A larg.chld_config, &n, NO_FLAG) !=
2N/A FPCFGA_OK) {
2N/A cfga_err(errstring,
2N/A larg.l_errno, ERR_LIST, 0);
2N/A list_free(&larg.listp);
2N/A S_FREE(larg.xport_logp);
2N/A HBA_CloseAdapter(handle);
2N/A HBA_FreeLibrary();
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A *nelemp = n;
2N/A *llpp = larg.listp;
2N/A S_FREE(larg.xport_logp);
2N/A HBA_CloseAdapter(handle);
2N/A HBA_FreeLibrary();
2N/A return (FPCFGA_OK);
2N/A }
2N/A larg.ret = FPCFGA_ACCESS_OK;
2N/A } else if (status == HBA_STATUS_ERROR_ILLEGAL_WWN) {
2N/A /*
2N/A * path indicates if the node exists in dev tree.
2N/A * if not found in dev tree return apid no exist.
2N/A * otherwise continue to stat with larg.ret set to
2N/A * apid_noexist.
2N/A */
2N/A if (apidp->lunlist == NULL) {
2N/A list_free(&larg.listp);
2N/A S_FREE(larg.xport_logp);
2N/A HBA_CloseAdapter(handle);
2N/A HBA_FreeLibrary();
2N/A return (FPCFGA_APID_NOEXIST);
2N/A }
2N/A } else { /* any error */
2N/A /*
2N/A * path indicates if the node exists in dev tree.
2N/A * if not found in dev tree return lib error.
2N/A * otherwise continue to stat with larg.ret set to
2N/A * apid_noexist.
2N/A */
2N/A if (apidp->lunlist == NULL) {
2N/A cfga_err(errstring, 0, ERR_FC_GET_DEVLIST, 0);
2N/A list_free(&larg.listp);
2N/A S_FREE(larg.xport_logp);
2N/A HBA_CloseAdapter(handle);
2N/A HBA_FreeLibrary();
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A }
2N/A break;
2N/A case FPCFGA_STAT_ALL:
2N/A /*
2N/A * for each dev in disco ports, create a ldata_list element.
2N/A * if if no disco ports found, continue to stat on devinfo tree
2N/A * to see if any node exist on the fca port.
2N/A */
2N/A for (discIndex = 0;
2N/A discIndex < portAttrs.NumberofDiscoveredPorts;
2N/A discIndex++) {
2N/A if (getDiscPortAttrs(handle, portIndex,
2N/A discIndex, &discPortAttrs)) {
2N/A /* Move on to the next target */
2N/A continue;
2N/A }
2N/A memcpy(&pwwn, &discPortAttrs.PortWWN, sizeof (la_wwn_t));
2N/A cvt_lawwn_to_dyncomp(&pwwn, &dyncomp, &l_errno);
2N/A if (dyncomp == NULL) {
2N/A cfga_err(errstring, l_errno, ERR_LIST, 0);
2N/A list_free(&larg.listp);
2N/A S_FREE(larg.xport_logp);
2N/A HBA_CloseAdapter(handle);
2N/A HBA_FreeLibrary();
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A inq_dtype = get_inq_dtype(apidp->xport_phys, dyncomp,
2N/A handle, &portAttrs, &discPortAttrs);
2N/A
2N/A if ((ret = init_ldata_for_accessible_dev(
2N/A dyncomp, inq_dtype, &larg)) != FPCFGA_OK) {
2N/A S_FREE(dyncomp);
2N/A cfga_err(errstring, larg.l_errno, ERR_LIST, 0);
2N/A list_free(&larg.listp);
2N/A S_FREE(larg.xport_logp);
2N/A HBA_CloseAdapter(handle);
2N/A HBA_FreeLibrary();
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A S_FREE(dyncomp);
2N/A }
2N/A break;
2N/A default:
2N/A break;
2N/A }
2N/A
2N/A /* we need to stat at least 1 device for all commands */
2N/A if (apidp->flags == FLAG_DEVINFO_FORCE) {
2N/A walkarg.flags = FLAG_DEVINFO_FORCE;
2N/A } else {
2N/A walkarg.flags = 0;
2N/A }
2N/A
2N/A walkarg.flags |= FLAG_PATH_INFO_WALK;
2N/A walkarg.walkmode.node_args.flags = DI_WALK_CLDFIRST;
2N/A walkarg.walkmode.node_args.fcn = stat_fc_dev;
2N/A
2N/A /*
2N/A * Subtree is ALWAYS rooted at the HBA (not at the device) as
2N/A * otherwise deadlock may occur if bus is disconnected.
2N/A *
2N/A * DINFOPROP was sufficient on apidp->xport_phys prior to the support
2N/A * on scsi_vhci child node. In order to get the link between
2N/A * scsi_vhci node and path info node the snap shot of the
2N/A * the whole device tree is required with DINFOCPYALL | DINFOPATH flag.
2N/A */
2N/A ret = walk_tree(apidp->xport_phys, &larg, DINFOCPYALL | DINFOPATH,
2N/A &walkarg, FPCFGA_WALK_NODE, &larg.l_errno);
2N/A
2N/A /*
2N/A * ret from walk_tree is either FPCFGA_OK or FPCFGA_ERR.
2N/A * larg.ret is used to detect other errors. Make sure larg.ret
2N/A * is set to a correct error.
2N/A */
2N/A if (ret != FPCFGA_OK || (ret = larg.ret) != FPCFGA_OK) {
2N/A if (ret != FPCFGA_APID_NOEXIST) {
2N/A cfga_err(errstring, larg.l_errno, ERR_LIST, 0);
2N/A }
2N/A /* if larg.ret = FPCFGA_APID_NOEXIST; */
2N/A goto out;
2N/A }
2N/A
2N/A assert(larg.listp != NULL);
2N/A
2N/A n = 0;
2N/A ret = postprocess_list_data(larg.listp, cmd, larg.chld_config, &n,
2N/A NO_FLAG);
2N/A if (ret != FPCFGA_OK) {
2N/A cfga_err(errstring, 0, ERR_LIST, 0);
2N/A ret = FPCFGA_LIB_ERR;
2N/A goto out;
2N/A }
2N/A
2N/A *nelemp = n;
2N/A *llpp = larg.listp;
2N/A ret = FPCFGA_OK;
2N/A /* FALLTHROUGH */
2N/Aout:
2N/A if (ret != FPCFGA_OK) list_free(&larg.listp);
2N/A S_FREE(larg.xport_logp);
2N/A HBA_CloseAdapter(handle);
2N/A HBA_FreeLibrary();
2N/A return (ret);
2N/A}
2N/A
2N/A/*
2N/A * Main routine for list operation when show_FCP_dev option is given.
2N/A * It calls various routines to consturct ldata list and
2N/A * postprocess the list data.
2N/A *
2N/A * The difference between do_list() and do_list_FCP_dev() is to
2N/A * process FCP SCSI LUN data list via uscsi report lun operation and
2N/A * stat lun level instead of port WWN based target level.
2N/A * The rest of logic is same.
2N/A *
2N/A * Overall algorithm:
2N/A * Get the device list on input hba port and construct ldata list for
2N/A * accesible devices.
2N/A * For each configured device, USCSI report lun is issued and ldata list
2N/A * with FCP device level(LUN) information is created.
2N/A * Stat hba port and LUN devices through walking the device tree.
2N/A * Verify the validity of the list data.
2N/A */
2N/Afpcfga_ret_t
2N/Ado_list_FCP_dev(
2N/A const char *ap_id,
2N/A uint_t flags,
2N/A fpcfga_cmd_t cmd,
2N/A ldata_list_t **llpp,
2N/A int *nelemp,
2N/A char **errstring)
2N/A{
2N/A int n = -1, l_errno = 0, limited_stat, len;
2N/A walkarg_t walkarg;
2N/A fpcfga_list_t larg = {NULL};
2N/A fpcfga_ret_t ret;
2N/A la_wwn_t pwwn;
2N/A char *xport_phys = NULL, *dyn = NULL, *dyncomp = NULL,
2N/A *lun_dyn = NULL;
2N/A apid_t apid_con = {NULL};
2N/A HBA_HANDLE handle;
2N/A HBA_PORTATTRIBUTES portAttrs;
2N/A HBA_PORTATTRIBUTES discPortAttrs;
2N/A HBA_STATUS status;
2N/A int portIndex, discIndex;
2N/A int retry;
2N/A uint64_t lun = 0;
2N/A struct scsi_inquiry inq;
2N/A struct scsi_extended_sense sense;
2N/A HBA_UINT8 scsiStatus;
2N/A uint32_t inquirySize = sizeof (inq),
2N/A senseSize = sizeof (sense);
2N/A
2N/A if (*llpp != NULL || *nelemp != 0) {
2N/A return (FPCFGA_ERR);
2N/A }
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 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 S_FREE(xport_phys);
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A
2N/A (void) strcpy(dyncomp, DYN_TO_DYNCOMP(dyn));
2N/A /* Remove the dynamic component from the base. */
2N/A *dyn = '\0';
2N/A /* if lun dyncomp exists delete it */
2N/A if ((lun_dyn = GET_LUN_DYN(dyncomp)) != NULL) {
2N/A *lun_dyn = '\0';
2N/A }
2N/A }
2N/A
2N/A apid_con.xport_phys = xport_phys;
2N/A apid_con.dyncomp = dyncomp;
2N/A apid_con.flags = flags;
2N/A
2N/A larg.apidp = &apid_con;
2N/A
2N/A /* Create the hba logid (also base component of logical ap_id) */
2N/A ret = make_xport_logid(larg.apidp->xport_phys, &larg.xport_logp,
2N/A &l_errno);
2N/A if (ret != FPCFGA_OK) {
2N/A cfga_err(errstring, l_errno, ERR_LIST, 0);
2N/A S_FREE(larg.apidp->xport_phys);
2N/A S_FREE(larg.apidp->dyncomp);
2N/A return (FPCFGA_ERR);
2N/A }
2N/A
2N/A assert(larg.xport_logp != NULL);
2N/A
2N/A larg.cmd = cmd;
2N/A larg.xport_rstate = CFGA_STAT_NONE;
2N/A
2N/A if ((ret = findMatchingAdapterPort(larg.apidp->xport_phys, &handle,
2N/A &portIndex, &portAttrs, errstring)) != FPCFGA_OK) {
2N/A S_FREE(larg.xport_logp);
2N/A S_FREE(larg.apidp->dyncomp);
2N/A return (ret);
2N/A }
2N/A
2N/A /*
2N/A * If stating a specific device, we will do limited stat on fca port.
2N/A * otherwise full stat on fca part is required.
2N/A * If stating a specific device we don't know if it exists or is
2N/A * configured yet. larg.ret is set to apid noexist for do_stat_dev.
2N/A * otherwise larg.ret is set to ok initially.
2N/A */
2N/A if (larg.cmd == FPCFGA_STAT_FC_DEV) {
2N/A limited_stat = 1; /* for do_stat_fca_xport */
2N/A larg.ret = FPCFGA_APID_NOEXIST; /* for stat_fc_dev */
2N/A } else {
2N/A limited_stat = 0; /* for do_stat_fca_xport */
2N/A larg.ret = FPCFGA_OK; /* for stat_fc_dev */
2N/A }
2N/A
2N/A /* For all list commands, the fca port needs to be stat'ed */
2N/A if ((ret = do_stat_fca_xport(&larg, limited_stat,
2N/A portAttrs)) != FPCFGA_OK) {
2N/A cfga_err(errstring, larg.l_errno, ERR_LIST, 0);
2N/A list_free(&larg.listp);
2N/A S_FREE(larg.xport_logp);
2N/A S_FREE(larg.apidp->xport_phys);
2N/A S_FREE(larg.apidp->dyncomp);
2N/A HBA_CloseAdapter(handle);
2N/A HBA_FreeLibrary();
2N/A return (ret);
2N/A }
2N/A
2N/A /*
2N/A * If stat'ing a FCA port or ALL, we have the bus stat data at
2N/A * this point.
2N/A * Assume that the bus has no configured children.
2N/A */
2N/A larg.chld_config = CFGA_STAT_UNCONFIGURED;
2N/A
2N/A switch (larg.cmd) {
2N/A case FPCFGA_STAT_FC_DEV:
2N/A /* la_wwn_t has uchar_t raw_wwn[8] thus no need to free. */
2N/A if (cvt_dyncomp_to_lawwn(larg.apidp->dyncomp, &pwwn) != 0) {
2N/A cfga_err(errstring, 0, ERR_LIST, 0);
2N/A list_free(&larg.listp);
2N/A S_FREE(larg.xport_logp);
2N/A S_FREE(larg.apidp->xport_phys);
2N/A S_FREE(larg.apidp->dyncomp);
2N/A HBA_CloseAdapter(handle);
2N/A HBA_FreeLibrary();
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A /*
2N/A * if the dyncomp exists on disco ports construct list_data
2N/A * otherwise return FPCFGA_APID_NOEXIST.
2N/A */
2N/A retry = 0;
2N/A do {
2N/A status = getPortAttrsByWWN(handle,
2N/A *((HBA_WWN *)(&pwwn)), &discPortAttrs);
2N/A if (status == HBA_STATUS_ERROR_STALE_DATA) {
2N/A /* get Port Attributes again after refresh. */
2N/A HBA_RefreshInformation(handle);
2N/A } else {
2N/A break; /* either okay or some other error */
2N/A }
2N/A } while (retry++ < HBA_MAX_RETRIES);
2N/A
2N/A if (status == HBA_STATUS_OK) {
2N/A /*
2N/A * if dyncomp exists only in dev list
2N/A * construct ldata_list and return.
2N/A * otherwise continue to stat on dev tree with
2N/A * larg.ret set to access_ok which informs stat_fc_dev
2N/A * the existence of device on dev_list.
2N/A *
2N/A * if path is null that guatantees the node is not
2N/A * configured. if node is detached the path
2N/A * is incomplete and not usable for further
2N/A * operations like uscsi_inq so take care of it here.
2N/A */
2N/A status = HBA_ScsiInquiryV2(handle, portAttrs.PortWWN,
2N/A discPortAttrs.PortWWN, lun, 0, 0,
2N/A &inq, &inquirySize, &scsiStatus,
2N/A &sense, &senseSize);
2N/A if (status == HBA_STATUS_OK) {
2N/A inq.inq_dtype = inq.inq_dtype & DTYPE_MASK;
2N/A } else if (status == HBA_STATUS_ERROR_NOT_A_TARGET) {
2N/A inq.inq_dtype = DTYPE_UNKNOWN;
2N/A } else {
2N/A inq.inq_dtype = ERR_INQ_DTYPE;
2N/A }
2N/A
2N/A if (init_ldata_for_accessible_dev(larg.apidp->dyncomp,
2N/A inq.inq_dtype, &larg) != FPCFGA_OK) {
2N/A cfga_err(errstring, larg.l_errno,
2N/A ERR_LIST, 0);
2N/A list_free(&larg.listp);
2N/A S_FREE(larg.xport_logp);
2N/A S_FREE(larg.apidp->xport_phys);
2N/A S_FREE(larg.apidp->dyncomp);
2N/A HBA_CloseAdapter(handle);
2N/A HBA_FreeLibrary();
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A if ((ret = get_accessible_FCP_dev_ldata(
2N/A larg.apidp->dyncomp, &larg, &l_errno))
2N/A != FPCFGA_OK) {
2N/A cfga_err(errstring, larg.l_errno, ERR_LIST, 0);
2N/A list_free(&larg.listp);
2N/A S_FREE(larg.xport_logp);
2N/A S_FREE(larg.apidp->xport_phys);
2N/A S_FREE(larg.apidp->dyncomp);
2N/A HBA_CloseAdapter(handle);
2N/A HBA_FreeLibrary();
2N/A return (FPCFGA_LIB_ERR);
2N/A } else {
2N/A /* continue to stat dev with access okay. */
2N/A larg.ret = FPCFGA_ACCESS_OK;
2N/A }
2N/A } else if (status == HBA_STATUS_ERROR_ILLEGAL_WWN) {
2N/A /*
2N/A * path indicates if the node exists in dev tree.
2N/A * if not found in dev tree return apid no exist.
2N/A * otherwise continue to stat with larg.ret set to
2N/A * apid_noexist.
2N/A */
2N/A if (larg.apidp->lunlist == NULL) {
2N/A list_free(&larg.listp);
2N/A S_FREE(larg.xport_logp);
2N/A HBA_CloseAdapter(handle);
2N/A HBA_FreeLibrary();
2N/A return (FPCFGA_APID_NOEXIST);
2N/A }
2N/A } else { /* not found or any error */
2N/A /*
2N/A * continue to stat dev with larg.ret set to
2N/A * apid_noexist.
2N/A */
2N/A larg.ret = FPCFGA_APID_NOEXIST;
2N/A }
2N/A break;
2N/A case FPCFGA_STAT_ALL:
2N/A /*
2N/A * for each dev in disco ports, create a ldata_list element.
2N/A * if if no disco ports found, continue to stat on devinfo tree
2N/A * to see if any node exist on the fca port.
2N/A */
2N/A for (discIndex = 0;
2N/A discIndex < portAttrs.NumberofDiscoveredPorts;
2N/A discIndex++) {
2N/A if (getDiscPortAttrs(handle, portIndex,
2N/A discIndex, &discPortAttrs)) {
2N/A /* Move on to the next target */
2N/A continue;
2N/A }
2N/A memcpy(&pwwn, &discPortAttrs.PortWWN, sizeof (la_wwn_t));
2N/A cvt_lawwn_to_dyncomp(&pwwn, &dyncomp, &l_errno);
2N/A if (dyncomp == NULL) {
2N/A cfga_err(errstring, l_errno, ERR_LIST, 0);
2N/A list_free(&larg.listp);
2N/A S_FREE(larg.xport_logp);
2N/A S_FREE(larg.apidp->xport_phys);
2N/A S_FREE(larg.apidp->dyncomp);
2N/A HBA_CloseAdapter(handle);
2N/A HBA_FreeLibrary();
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A status = HBA_ScsiInquiryV2(handle, portAttrs.PortWWN,
2N/A discPortAttrs.PortWWN, lun, 0, 0,
2N/A &inq, &inquirySize, &scsiStatus,
2N/A &sense, &senseSize);
2N/A if (status == HBA_STATUS_OK) {
2N/A inq.inq_dtype = inq.inq_dtype & DTYPE_MASK;
2N/A } else if (status == HBA_STATUS_ERROR_NOT_A_TARGET) {
2N/A inq.inq_dtype = DTYPE_UNKNOWN;
2N/A } else {
2N/A inq.inq_dtype = ERR_INQ_DTYPE;
2N/A }
2N/A if ((ret = init_ldata_for_accessible_dev(
2N/A dyncomp, inq.inq_dtype, &larg)) != FPCFGA_OK) {
2N/A S_FREE(dyncomp);
2N/A cfga_err(errstring, larg.l_errno, ERR_LIST, 0);
2N/A list_free(&larg.listp);
2N/A S_FREE(larg.xport_logp);
2N/A S_FREE(larg.apidp->xport_phys);
2N/A S_FREE(larg.apidp->dyncomp);
2N/A HBA_CloseAdapter(handle);
2N/A HBA_FreeLibrary();
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A if ((ret = get_accessible_FCP_dev_ldata(
2N/A dyncomp, &larg, &l_errno)) != FPCFGA_OK) {
2N/A S_FREE(dyncomp);
2N/A cfga_err(errstring, larg.l_errno, ERR_LIST, 0);
2N/A list_free(&larg.listp);
2N/A S_FREE(larg.xport_logp);
2N/A S_FREE(larg.apidp->xport_phys);
2N/A S_FREE(larg.apidp->dyncomp);
2N/A HBA_CloseAdapter(handle);
2N/A HBA_FreeLibrary();
2N/A return (ret);
2N/A }
2N/A S_FREE(dyncomp);
2N/A }
2N/A break;
2N/A /* default: continue */
2N/A }
2N/A
2N/A /* we need to stat at least 1 device for all commands */
2N/A if ((larg.apidp->flags & FLAG_DEVINFO_FORCE) == FLAG_DEVINFO_FORCE) {
2N/A walkarg.flags = FLAG_DEVINFO_FORCE;
2N/A } else {
2N/A walkarg.flags = 0;
2N/A }
2N/A
2N/A walkarg.flags |= FLAG_PATH_INFO_WALK;
2N/A walkarg.walkmode.node_args.flags = DI_WALK_CLDFIRST;
2N/A walkarg.walkmode.node_args.fcn = stat_FCP_dev;
2N/A
2N/A /*
2N/A * Subtree is ALWAYS rooted at the HBA (not at the device) as
2N/A * otherwise deadlock may occur if bus is disconnected.
2N/A *
2N/A * DINFOPROP was sufficient on apidp->xport_phys prior to the support
2N/A * on scsi_vhci child node. In order to get the link between
2N/A * scsi_vhci node and path info node the snap shot of the
2N/A * the whole device tree is required with DINFOCPYALL | DINFOPATH flag.
2N/A */
2N/A ret = walk_tree(larg.apidp->xport_phys, &larg, DINFOCPYALL | DINFOPATH,
2N/A &walkarg, FPCFGA_WALK_NODE, &larg.l_errno);
2N/A
2N/A /*
2N/A * ret from walk_tree is either FPCFGA_OK or FPCFGA_ERR.
2N/A * larg.ret is used to detect other errors. Make sure larg.ret
2N/A * is set to a correct error.
2N/A */
2N/A if (ret != FPCFGA_OK || (ret = larg.ret) != FPCFGA_OK) {
2N/A if (ret != FPCFGA_APID_NOEXIST) {
2N/A cfga_err(errstring, larg.l_errno, ERR_LIST, 0);
2N/A }
2N/A /* if larg.ret = FPCFGA_APID_NOEXIST return. */
2N/A list_free(&larg.listp);
2N/A S_FREE(larg.xport_logp);
2N/A S_FREE(larg.apidp->xport_phys);
2N/A S_FREE(larg.apidp->dyncomp);
2N/A HBA_CloseAdapter(handle);
2N/A HBA_FreeLibrary();
2N/A return (ret);
2N/A }
2N/A
2N/A assert(larg.listp != NULL);
2N/A
2N/A n = 0;
2N/A ret = postprocess_list_data(larg.listp, cmd, larg.chld_config, &n,
2N/A flags);
2N/A if (ret != FPCFGA_OK) {
2N/A cfga_err(errstring, 0, ERR_LIST, 0);
2N/A list_free(&larg.listp);
2N/A S_FREE(larg.xport_logp);
2N/A S_FREE(larg.apidp->xport_phys);
2N/A S_FREE(larg.apidp->dyncomp);
2N/A HBA_CloseAdapter(handle);
2N/A HBA_FreeLibrary();
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A
2N/A *nelemp = n;
2N/A *llpp = larg.listp;
2N/A ret = FPCFGA_OK;
2N/A S_FREE(larg.xport_logp);
2N/A S_FREE(larg.apidp->xport_phys);
2N/A S_FREE(larg.apidp->dyncomp);
2N/A HBA_CloseAdapter(handle);
2N/A HBA_FreeLibrary();
2N/A return (FPCFGA_OK);
2N/A}
2N/A
2N/A/*
2N/A * This routine returns initialize struct fcp_ioctl.
2N/A */
2N/Astatic void
2N/Ainit_fcp_scsi_cmd(
2N/A struct fcp_scsi_cmd *fscsi,
2N/A uchar_t *lun_num,
2N/A la_wwn_t *pwwn,
2N/A void *scmdbuf,
2N/A size_t scmdbuf_len,
2N/A void *respbuf,
2N/A size_t respbuf_len,
2N/A void *sensebuf,
2N/A size_t sensebuf_len)
2N/A{
2N/A memset(fscsi, 0, sizeof (struct fcp_scsi_cmd));
2N/A memset(scmdbuf, 0, scmdbuf_len);
2N/A memcpy(fscsi->scsi_fc_pwwn.raw_wwn, pwwn, sizeof (u_longlong_t));
2N/A fscsi->scsi_fc_rspcode = 0;
2N/A fscsi->scsi_flags = FCP_SCSI_READ;
2N/A fscsi->scsi_timeout = FCP_SCSI_CMD_TIMEOUT; /* second */
2N/A fscsi->scsi_cdbbufaddr = (caddr_t)scmdbuf;
2N/A fscsi->scsi_cdblen = scmdbuf_len;
2N/A fscsi->scsi_bufaddr = (caddr_t)respbuf;
2N/A fscsi->scsi_buflen = respbuf_len;
2N/A fscsi->scsi_bufresid = 0;
2N/A fscsi->scsi_bufstatus = 0;
2N/A fscsi->scsi_rqbufaddr = (caddr_t)sensebuf;
2N/A fscsi->scsi_rqlen = sensebuf_len;
2N/A fscsi->scsi_rqresid = 0;
2N/A memcpy(&fscsi->scsi_lun, lun_num, sizeof (fscsi->scsi_lun));
2N/A}
2N/A
2N/A/*
2N/A * This routine returns issues FCP_TGT_SEND_SCSI
2N/A */
2N/Astatic fpcfga_ret_t
2N/Aissue_fcp_scsi_cmd(
2N/A const char *xport_phys,
2N/A struct fcp_scsi_cmd *fscsi,
2N/A int *l_errnop)
2N/A{
2N/A struct stat stbuf;
2N/A int fcp_fd, retry, rv;
2N/A
2N/A if (stat(xport_phys, &stbuf) < 0) {
2N/A *l_errnop = errno;
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A
2N/A fscsi->scsi_fc_port_num = (uint32_t)minor(stbuf.st_rdev);
2N/A fcp_fd = open(FCP_PATH, O_RDONLY | O_NDELAY);
2N/A retry = 0;
2N/A while (fcp_fd < 0 && retry++ < OPEN_RETRY_COUNT && (
2N/A errno == EBUSY || errno == EAGAIN)) {
2N/A (void) usleep(OPEN_RETRY_INTERVAL);
2N/A fcp_fd = open(FCP_PATH, O_RDONLY|O_NDELAY);
2N/A }
2N/A if (fcp_fd < 0) {
2N/A *l_errnop = errno;
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A
2N/A rv = ioctl(fcp_fd, FCP_TGT_SEND_SCSI, fscsi);
2N/A retry = 0;
2N/A while ((rv != 0 && retry++ < IOCTL_RETRY_COUNT &&
2N/A (errno == EBUSY || errno == EAGAIN)) ||
2N/A (retry++ < IOCTL_RETRY_COUNT &&
2N/A ((uchar_t)fscsi->scsi_bufstatus & STATUS_MASK)
2N/A == STATUS_BUSY)) {
2N/A (void) usleep(IOCTL_RETRY_INTERVAL);
2N/A rv = ioctl(fcp_fd, FCP_TGT_SEND_SCSI, fscsi);
2N/A }
2N/A close(fcp_fd);
2N/A
2N/A if (fscsi->scsi_fc_status == FC_DEVICE_NOT_TGT) {
2N/A return (FPCFGA_FCP_SEND_SCSI_DEV_NOT_TGT);
2N/A } else if (rv != 0 || fscsi->scsi_bufstatus != 0) {
2N/A *l_errnop = errno;
2N/A return (FPCFGA_FCP_TGT_SEND_SCSI_FAILED);
2N/A }
2N/A return (FPCFGA_OK);
2N/A}
2N/A
2N/A/*
2N/A * This routine returns standard inq data for
2N/A * a target represented by dyncomp.
2N/A *
2N/A * Calls FCP passthru ioctl FCP_TGT_SEND_SCSI to get inquiry data.
2N/A *
2N/A * Caller should free the *inq_buf.
2N/A */
2N/Astatic fpcfga_ret_t
2N/Aget_standard_inq_data(
2N/A const char *xport_phys,
2N/A const char *dyncomp,
2N/A uchar_t *lun_num,
2N/A struct scsi_inquiry **inq_buf,
2N/A int *l_errnop)
2N/A{
2N/A struct fcp_scsi_cmd fscsi;
2N/A struct scsi_extended_sense sensebuf;
2N/A union scsi_cdb scsi_inq_req;
2N/A la_wwn_t pwwn;
2N/A int alloc_len;
2N/A fpcfga_ret_t ret;
2N/A
2N/A
2N/A alloc_len = sizeof (struct scsi_inquiry);
2N/A if ((*inq_buf = (struct scsi_inquiry *)calloc(1, alloc_len)) == NULL) {
2N/A *l_errnop = errno;
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A
2N/A if (cvt_dyncomp_to_lawwn(dyncomp, &pwwn) != 0) {
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A
2N/A init_fcp_scsi_cmd(&fscsi, lun_num, &pwwn, &scsi_inq_req,
2N/A sizeof (scsi_inq_req), *inq_buf, alloc_len, &sensebuf,
2N/A sizeof (struct scsi_extended_sense));
2N/A scsi_inq_req.scc_cmd = SCMD_INQUIRY;
2N/A scsi_inq_req.g0_count0 = sizeof (struct scsi_inquiry);
2N/A
2N/A if ((ret = issue_fcp_scsi_cmd(xport_phys, &fscsi, l_errnop))
2N/A != FPCFGA_OK) {
2N/A S_FREE(*inq_buf);
2N/A return (ret);
2N/A }
2N/A
2N/A return (FPCFGA_OK);
2N/A}
2N/A
2N/A/*
2N/A * This routine returns report lun data and number of luns found
2N/A * on a target represented by dyncomp.
2N/A *
2N/A * Calls FCP passthru ioctl FCP_TGT_SEND_SCSI to get report lun data.
2N/A *
2N/A * Caller should free the *resp_buf when FPCFGA_OK is returned.
2N/A */
2N/Afpcfga_ret_t
2N/Aget_report_lun_data(
2N/A const char *xport_phys,
2N/A const char *dyncomp,
2N/A int *num_luns,
2N/A report_lun_resp_t **resp_buf,
2N/A struct scsi_extended_sense *sensebuf,
2N/A int *l_errnop)
2N/A{
2N/A struct fcp_scsi_cmd fscsi;
2N/A union scsi_cdb scsi_rl_req;
2N/A la_wwn_t pwwn;
2N/A int alloc_len;
2N/A fpcfga_ret_t ret;
2N/A uchar_t lun_data[SAM_LUN_SIZE];
2N/A
2N/A alloc_len = sizeof (struct report_lun_resp);
2N/A if ((*resp_buf = (report_lun_resp_t *)calloc(1, alloc_len)) == NULL) {
2N/A *l_errnop = errno;
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A
2N/A if (cvt_dyncomp_to_lawwn(dyncomp, &pwwn) != 0) {
2N/A S_FREE(*resp_buf);
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A
2N/A /* sending to LUN 0 so initializing lun_data buffer to be 0 */
2N/A memset(lun_data, 0, sizeof (lun_data));
2N/A init_fcp_scsi_cmd(&fscsi, lun_data, &pwwn, &scsi_rl_req,
2N/A sizeof (scsi_rl_req), *resp_buf, alloc_len, sensebuf,
2N/A sizeof (struct scsi_extended_sense));
2N/A scsi_rl_req.scc_cmd = FP_SCMD_REPORT_LUN;
2N/A FORMG5COUNT(&scsi_rl_req, alloc_len);
2N/A
2N/A if ((ret = issue_fcp_scsi_cmd(xport_phys, &fscsi, l_errnop))
2N/A != FPCFGA_OK) {
2N/A S_FREE(*resp_buf);
2N/A return (ret);
2N/A }
2N/A
2N/A if (ntohl((*resp_buf)->num_lun) >
2N/A (sizeof (struct report_lun_resp) - REPORT_LUN_HDR_SIZE)) {
2N/A alloc_len = (*resp_buf)->num_lun + REPORT_LUN_HDR_SIZE;
2N/A S_FREE(*resp_buf);
2N/A if ((*resp_buf = (report_lun_resp_t *)calloc(1, alloc_len))
2N/A == NULL) {
2N/A *l_errnop = errno;
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A (void) memset((char *)*resp_buf, 0, alloc_len);
2N/A FORMG5COUNT(&scsi_rl_req, alloc_len);
2N/A
2N/A fscsi.scsi_bufaddr = (caddr_t)*resp_buf;
2N/A fscsi.scsi_buflen = alloc_len;
2N/A
2N/A if ((ret = issue_fcp_scsi_cmd(xport_phys, &fscsi, l_errnop))
2N/A != FPCFGA_OK) {
2N/A S_FREE(*resp_buf);
2N/A return (ret);
2N/A }
2N/A }
2N/A
2N/A /* num_lun represent number of luns * 8. */
2N/A *num_luns = ntohl((*resp_buf)->num_lun) >> 3;
2N/A
2N/A return (FPCFGA_OK);
2N/A}
2N/A
2N/A/*
2N/A * Routine for consturct ldata list for each FCP SCSI LUN device
2N/A * for a discovered target device.
2N/A * It calls get_report_lun_data to get report lun data and
2N/A * construct ldata list per each lun.
2N/A *
2N/A * It is called only when show_FCP_dev option is given.
2N/A *
2N/A * Overall algorithm:
2N/A * Get the report lun data thru FCP passthru ioctl.
2N/A * Call init_ldata_for_accessible_FCP_dev to process the report LUN data.
2N/A * For each LUN found standard inquiry is issued to get device type.
2N/A */
2N/Astatic fpcfga_ret_t
2N/Aget_accessible_FCP_dev_ldata(
2N/A const char *dyncomp,
2N/A fpcfga_list_t *lap,
2N/A int *l_errnop)
2N/A{
2N/A report_lun_resp_t *resp_buf;
2N/A struct scsi_extended_sense sense;
2N/A int num_luns;
2N/A fpcfga_ret_t ret;
2N/A
2N/A memset(&sense, 0, sizeof (sense));
2N/A if ((ret = get_report_lun_data(lap->apidp->xport_phys, dyncomp,
2N/A &num_luns, &resp_buf, &sense, l_errnop)) != FPCFGA_OK) {
2N/A /*
2N/A * when report lun data fails then return FPCFGA_OK thus
2N/A * keep the ldata for the target which is acquired previously.
2N/A * For remote hba node this will be normal.
2N/A * For a target error may already be detected through
2N/A * FCP_TGT_INQ.
2N/A */
2N/A if ((ret == FPCFGA_FCP_TGT_SEND_SCSI_FAILED) ||
2N/A (ret == FPCFGA_FCP_SEND_SCSI_DEV_NOT_TGT)) {
2N/A ret = FPCFGA_OK;
2N/A }
2N/A return (ret);
2N/A }
2N/A
2N/A if (num_luns > 0) {
2N/A ret = init_ldata_for_accessible_FCP_dev(
2N/A dyncomp, num_luns, resp_buf, lap, l_errnop);
2N/A } else {
2N/A /*
2N/A * proceed with to stat if no lun found.
2N/A * This will make the target apid will be kept.
2N/A */
2N/A ret = FPCFGA_OK;
2N/A }
2N/A
2N/A S_FREE(resp_buf);
2N/A return (ret);
2N/A}
2N/A
2N/A/*
2N/A * Routine for checking validity of ldata list based on input argumemnt.
2N/A * Set the occupant state of hba port if the list is valid.
2N/A */
2N/Astatic fpcfga_ret_t
2N/Apostprocess_list_data(
2N/A const ldata_list_t *listp,
2N/A fpcfga_cmd_t cmd,
2N/A cfga_stat_t chld_config,
2N/A int *np,
2N/A uint_t flags)
2N/A{
2N/A ldata_list_t *tmplp = NULL;
2N/A cfga_list_data_t *xport_ldatap = NULL;
2N/A int i;
2N/A
2N/A
2N/A *np = 0;
2N/A
2N/A if (listp == NULL) {
2N/A return (FPCFGA_ERR);
2N/A }
2N/A
2N/A tmplp = (ldata_list_t *)listp;
2N/A for (i = 0; tmplp != NULL; tmplp = tmplp->next) {
2N/A i++;
2N/A if (GET_DYN(tmplp->ldata.ap_phys_id) == NULL) {
2N/A /* A bus stat data */
2N/A assert(GET_DYN(tmplp->ldata.ap_log_id) == NULL);
2N/A xport_ldatap = &tmplp->ldata;
2N/A#ifdef DEBUG
2N/A } else {
2N/A assert(GET_DYN(tmplp->ldata.ap_log_id) != NULL);
2N/A#endif
2N/A }
2N/A }
2N/A
2N/A switch (cmd) {
2N/A case FPCFGA_STAT_FC_DEV:
2N/A if ((flags & FLAG_FCP_DEV) == FLAG_FCP_DEV) {
2N/A if (i < 1 || xport_ldatap != NULL) {
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A } else {
2N/A if (i != 1 || xport_ldatap != NULL) {
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A }
2N/A break;
2N/A case FPCFGA_STAT_FCA_PORT:
2N/A if (i != 1 || xport_ldatap == NULL) {
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A break;
2N/A case FPCFGA_STAT_ALL:
2N/A if (i < 1 || xport_ldatap == NULL) {
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A break;
2N/A default:
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A
2N/A *np = i;
2N/A
2N/A /* Fill in the occupant (child) state. */
2N/A if (xport_ldatap != NULL) {
2N/A xport_ldatap->ap_o_state = chld_config;
2N/A }
2N/A return (FPCFGA_OK);
2N/A}
2N/A
2N/A/*
2N/A * Routine for checking each target device found in device tree.
2N/A * When the matching port WWN dev is found from the accessble ldata list
2N/A * the target device is updated with configured ostate.
2N/A *
2N/A * Overall algorithm:
2N/A * Parse the device tree to find configured devices which matches with
2N/A * list argument. If cmd is stat on a specific target device it
2N/A * matches port WWN and continues to further processing. If cmd is
2N/A * stat on hba port all the device target under the hba are processed.
2N/A */
2N/Astatic int
2N/Astat_fc_dev(di_node_t node, void *arg)
2N/A{
2N/A fpcfga_list_t *lap = NULL;
2N/A char *devfsp = NULL, *nodepath = NULL;
2N/A size_t len = 0;
2N/A int limited_stat = 0, match_minor, rv;
2N/A fpcfga_ret_t ret;
2N/A di_prop_t prop = DI_PROP_NIL;
2N/A uchar_t *port_wwn_data;
2N/A char port_wwn[WWN_SIZE*2+1];
2N/A int count;
2N/A
2N/A lap = (fpcfga_list_t *)arg;
2N/A
2N/A /*
2N/A * Skip partial nodes
2N/A *
2N/A * This checking is from the scsi plug-in and will be deleted for
2N/A * fp plug-in. The node will be processed for fp even if it is
2N/A * in driver detached state. From fp perspective the node is configured
2N/A * as long as the node is not in offline or down state.
2N/A * scsi plug-in considers the known state when it is offlined
2N/A * regradless of driver detached state or when it is not in driver
2N/A * detached state like normal state.
2N/A * If the node is only in driver detached state it is considered as
2N/A * unknown state.
2N/A *
2N/A * if (!known_state(node) && (lap->cmd != FPCFGA_STAT_FC_DEV)) {
2N/A * return (DI_WALK_CONTINUE);
2N/A *
2N/A */
2N/A
2N/A devfsp = di_devfs_path(node);
2N/A if (devfsp == NULL) {
2N/A rv = DI_WALK_CONTINUE;
2N/A goto out;
2N/A }
2N/A
2N/A len = strlen(DEVICES_DIR) + strlen(devfsp) + 1;
2N/A
2N/A nodepath = calloc(1, len);
2N/A if (nodepath == NULL) {
2N/A lap->l_errno = errno;
2N/A lap->ret = FPCFGA_LIB_ERR;
2N/A rv = DI_WALK_TERMINATE;
2N/A goto out;
2N/A }
2N/A
2N/A (void) snprintf(nodepath, len, "%s%s", DEVICES_DIR, devfsp);
2N/A
2N/A /* Skip node if it is HBA */
2N/A match_minor = 0;
2N/A if (!dev_cmp(lap->apidp->xport_phys, nodepath, match_minor)) {
2N/A rv = DI_WALK_CONTINUE;
2N/A goto out;
2N/A }
2N/A
2N/A /* If stat'ing a specific device, is this node that device */
2N/A if (lap->cmd == FPCFGA_STAT_FC_DEV) {
2N/A /* checks port wwn property to find a match */
2N/A while ((prop = di_prop_next(node, prop))
2N/A != DI_PROP_NIL) {
2N/A if ((strcmp(PORT_WWN_PROP,
2N/A di_prop_name(prop)) == 0) &&
2N/A (di_prop_type(prop) ==
2N/A DI_PROP_TYPE_BYTE)) {
2N/A break;
2N/A }
2N/A }
2N/A
2N/A if (prop != DI_PROP_NIL) {
2N/A count = di_prop_bytes(prop, &port_wwn_data);
2N/A if (count != WWN_SIZE) {
2N/A lap->ret = FPCFGA_LIB_ERR;
2N/A rv = DI_WALK_TERMINATE;
2N/A goto out;
2N/A }
2N/A (void) sprintf(port_wwn, "%016llx",
2N/A (wwnConversion(port_wwn_data)));
2N/A /*
2N/A * port wwn doesn't match contine to walk
2N/A * if match call do_stat_fc_dev.
2N/A */
2N/A if (strncmp(port_wwn, lap->apidp->dyncomp,
2N/A WWN_SIZE*2)) {
2N/A rv = DI_WALK_CONTINUE;
2N/A goto out;
2N/A }
2N/A } else {
2N/A rv = DI_WALK_CONTINUE;
2N/A goto out;
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * If stat'ing a xport only, we look at device nodes only to get
2N/A * xport configuration status. So a limited stat will suffice.
2N/A */
2N/A if (lap->cmd == FPCFGA_STAT_FCA_PORT) {
2N/A limited_stat = 1;
2N/A } else {
2N/A limited_stat = 0;
2N/A }
2N/A
2N/A /*
2N/A * Ignore errors if stat'ing a bus or listing all
2N/A */
2N/A ret = do_stat_fc_dev(node, nodepath, lap, limited_stat);
2N/A if (ret != FPCFGA_OK) {
2N/A if (lap->cmd == FPCFGA_STAT_FC_DEV) {
2N/A lap->ret = ret;
2N/A rv = DI_WALK_TERMINATE;
2N/A } else {
2N/A rv = DI_WALK_CONTINUE;
2N/A }
2N/A goto out;
2N/A }
2N/A
2N/A /* Are we done ? */
2N/A rv = DI_WALK_CONTINUE;
2N/A if (lap->cmd == FPCFGA_STAT_FCA_PORT &&
2N/A lap->chld_config == CFGA_STAT_CONFIGURED) {
2N/A rv = DI_WALK_TERMINATE;
2N/A } else if (lap->cmd == FPCFGA_STAT_FC_DEV) {
2N/A /*
2N/A * If stat'ing a specific device, we are done at this point.
2N/A */
2N/A rv = DI_WALK_TERMINATE;
2N/A }
2N/A
2N/A /*FALLTHRU*/
2N/Aout:
2N/A S_FREE(nodepath);
2N/A if (devfsp != NULL) di_devfs_path_free(devfsp);
2N/A return (rv);
2N/A}
2N/A
2N/A/*
2N/A * Routine for checking each FCP SCSI LUN device found in device tree.
2N/A * When the matching port WWN and LUN are found from the accessble ldata list
2N/A * the FCP SCSI LUN is updated with configured ostate.
2N/A *
2N/A * Overall algorithm:
2N/A * Parse the device tree to find configured devices which matches with
2N/A * list argument. If cmd is stat on a specific target device it
2N/A * matches port WWN and continues to further processing. If cmd is
2N/A * stat on hba port all the FCP SCSI LUN under the hba are processed.
2N/A */
2N/Astatic int
2N/Astat_FCP_dev(di_node_t node, void *arg)
2N/A{
2N/A fpcfga_list_t *lap = NULL;
2N/A char *devfsp = NULL, *nodepath = NULL;
2N/A size_t len = 0;
2N/A int limited_stat = 0, match_minor, rv, di_ret;
2N/A fpcfga_ret_t ret;
2N/A uchar_t *port_wwn_data;
2N/A char port_wwn[WWN_SIZE*2+1];
2N/A
2N/A lap = (fpcfga_list_t *)arg;
2N/A
2N/A devfsp = di_devfs_path(node);
2N/A if (devfsp == NULL) {
2N/A rv = DI_WALK_CONTINUE;
2N/A goto out;
2N/A }
2N/A
2N/A len = strlen(DEVICES_DIR) + strlen(devfsp) + 1;
2N/A
2N/A nodepath = calloc(1, len);
2N/A if (nodepath == NULL) {
2N/A lap->l_errno = errno;
2N/A lap->ret = FPCFGA_LIB_ERR;
2N/A rv = DI_WALK_TERMINATE;
2N/A goto out;
2N/A }
2N/A
2N/A (void) snprintf(nodepath, len, "%s%s", DEVICES_DIR, devfsp);
2N/A
2N/A /* Skip node if it is HBA */
2N/A match_minor = 0;
2N/A if (!dev_cmp(lap->apidp->xport_phys, nodepath, match_minor)) {
2N/A rv = DI_WALK_CONTINUE;
2N/A goto out;
2N/A }
2N/A
2N/A /* If stat'ing a specific device, is this node that device */
2N/A if (lap->cmd == FPCFGA_STAT_FC_DEV) {
2N/A /* checks port wwn property to find a match */
2N/A di_ret = di_prop_lookup_bytes(DDI_DEV_T_ANY, node,
2N/A PORT_WWN_PROP, &port_wwn_data);
2N/A if (di_ret == -1) {
2N/A rv = DI_WALK_CONTINUE;
2N/A goto out;
2N/A } else {
2N/A (void) sprintf(port_wwn, "%016llx",
2N/A (wwnConversion(port_wwn_data)));
2N/A /*
2N/A * port wwn doesn't match contine to walk
2N/A * if match call do_stat_FCP_dev.
2N/A */
2N/A if (strncmp(port_wwn, lap->apidp->dyncomp,
2N/A WWN_SIZE*2)) {
2N/A rv = DI_WALK_CONTINUE;
2N/A goto out;
2N/A }
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * If stat'ing a xport only, we look at device nodes only to get
2N/A * xport configuration status. So a limited stat will suffice.
2N/A */
2N/A if (lap->cmd == FPCFGA_STAT_FCA_PORT) {
2N/A limited_stat = 1;
2N/A } else {
2N/A limited_stat = 0;
2N/A }
2N/A
2N/A /*
2N/A * Ignore errors if stat'ing a bus or listing all
2N/A */
2N/A ret = do_stat_FCP_dev(node, nodepath, lap, limited_stat);
2N/A if (ret != FPCFGA_OK) {
2N/A rv = DI_WALK_CONTINUE;
2N/A goto out;
2N/A }
2N/A
2N/A /* Are we done ? */
2N/A rv = DI_WALK_CONTINUE;
2N/A if (lap->cmd == FPCFGA_STAT_FCA_PORT &&
2N/A lap->chld_config == CFGA_STAT_CONFIGURED) {
2N/A rv = DI_WALK_TERMINATE;
2N/A }
2N/A
2N/A /*FALLTHRU*/
2N/Aout:
2N/A S_FREE(nodepath);
2N/A if (devfsp != NULL) di_devfs_path_free(devfsp);
2N/A return (rv);
2N/A}
2N/A
2N/Astatic fpcfga_ret_t
2N/Ado_stat_fca_xport(fpcfga_list_t *lap, int limited_stat,
2N/A HBA_PORTATTRIBUTES portAttrs)
2N/A{
2N/A cfga_list_data_t *clp = NULL;
2N/A ldata_list_t *listp = NULL;
2N/A int l_errno = 0;
2N/A uint_t devinfo_state = 0;
2N/A walkarg_t walkarg;
2N/A fpcfga_ret_t ret;
2N/A cfga_cond_t cond = CFGA_COND_UNKNOWN;
2N/A
2N/A assert(lap->xport_logp != NULL);
2N/A
2N/A /* Get xport state */
2N/A if (lap->apidp->flags == FLAG_DEVINFO_FORCE) {
2N/A walkarg.flags = FLAG_DEVINFO_FORCE;
2N/A } else {
2N/A walkarg.flags = 0;
2N/A }
2N/A walkarg.walkmode.node_args.flags = 0;
2N/A walkarg.walkmode.node_args.fcn = get_xport_state;
2N/A
2N/A ret = walk_tree(lap->apidp->xport_phys, &devinfo_state,
2N/A DINFOCPYALL | DINFOPATH, &walkarg, FPCFGA_WALK_NODE, &l_errno);
2N/A if (ret == FPCFGA_OK) {
2N/A lap->xport_rstate = xport_devinfo_to_recep_state(devinfo_state);
2N/A } else {
2N/A lap->xport_rstate = CFGA_STAT_NONE;
2N/A }
2N/A
2N/A /*
2N/A * Get topology works okay even if the fp port is connected
2N/A * to a switch and no devices connected to the switch.
2N/A * In this case the list will only shows fp port info without
2N/A * any device listed.
2N/A */
2N/A switch (portAttrs.PortType) {
2N/A case HBA_PORTTYPE_NLPORT:
2N/A (void) snprintf(lap->xport_type,
2N/A sizeof (lap->xport_type), "%s",
2N/A FP_FC_PUBLIC_PORT_TYPE);
2N/A break;
2N/A case HBA_PORTTYPE_NPORT:
2N/A (void) snprintf(lap->xport_type,
2N/A sizeof (lap->xport_type), "%s",
2N/A FP_FC_FABRIC_PORT_TYPE);
2N/A break;
2N/A case HBA_PORTTYPE_LPORT:
2N/A (void) snprintf(lap->xport_type,
2N/A sizeof (lap->xport_type), "%s",
2N/A FP_FC_PRIVATE_PORT_TYPE);
2N/A break;
2N/A case HBA_PORTTYPE_PTP:
2N/A (void) snprintf(lap->xport_type,
2N/A sizeof (lap->xport_type), "%s",
2N/A FP_FC_PT_TO_PT_PORT_TYPE);
2N/A break;
2N/A /*
2N/A * HBA_PORTTYPE_UNKNOWN means nothing is connected
2N/A */
2N/A case HBA_PORTTYPE_UNKNOWN:
2N/A (void) snprintf(lap->xport_type,
2N/A sizeof (lap->xport_type), "%s",
2N/A FP_FC_PORT_TYPE);
2N/A break;
2N/A /* NOT_PRESENT, OTHER, FPORT, FLPORT */
2N/A default:
2N/A (void) snprintf(lap->xport_type,
2N/A sizeof (lap->xport_type), "%s",
2N/A FP_FC_PORT_TYPE);
2N/A cond = CFGA_COND_FAILED;
2N/A break;
2N/A }
2N/A
2N/A if (limited_stat) {
2N/A /* We only want to know bus(receptacle) connect status */
2N/A return (FPCFGA_OK);
2N/A }
2N/A
2N/A listp = calloc(1, sizeof (ldata_list_t));
2N/A if (listp == NULL) {
2N/A lap->l_errno = errno;
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A
2N/A clp = &listp->ldata;
2N/A
2N/A (void) snprintf(clp->ap_log_id, sizeof (clp->ap_log_id), "%s",
2N/A lap->xport_logp);
2N/A (void) snprintf(clp->ap_phys_id, sizeof (clp->ap_phys_id), "%s",
2N/A lap->apidp->xport_phys);
2N/A
2N/A clp->ap_class[0] = '\0'; /* Filled by libcfgadm */
2N/A clp->ap_r_state = lap->xport_rstate;
2N/A clp->ap_o_state = lap->chld_config;
2N/A clp->ap_cond = cond;
2N/A clp->ap_busy = 0;
2N/A clp->ap_status_time = (time_t)-1;
2N/A clp->ap_info[0] = '\0';
2N/A (void) strncpy(clp->ap_type, lap->xport_type, sizeof (clp->ap_type));
2N/A
2N/A /* Link it in. lap->listp is NULL originally. */
2N/A listp->next = lap->listp;
2N/A /* lap->listp now gets cfga_list_data for the fca port. */
2N/A lap->listp = listp;
2N/A
2N/A return (FPCFGA_OK);
2N/A}
2N/A
2N/A
2N/Astatic int
2N/Aget_xport_state(di_node_t node, void *arg)
2N/A{
2N/A uint_t *di_statep = (uint_t *)arg;
2N/A
2N/A *di_statep = di_state(node);
2N/A
2N/A return (DI_WALK_TERMINATE);
2N/A}
2N/A
2N/A/*
2N/A * Routine for updating ldata list based on the state of device node.
2N/A * When no matching accessible ldata is found a new ldata is created
2N/A * with proper state information.
2N/A *
2N/A * Overall algorithm:
2N/A * If the device node is online and the matching ldata is found
2N/A * the target device is updated with configued and unknown condition.
2N/A * If the device node is offline or down and the matching ldata is found
2N/A * the target device is updated with configued and unusable condition.
2N/A * If the device node is online but the matching ldata is not found
2N/A * the target device is created with configued and failing condition.
2N/A * If the device node is offline or down and the matching ldata is not found
2N/A * the target device is created with configued and unusable condition.
2N/A */
2N/Astatic fpcfga_ret_t
2N/Ado_stat_fc_dev(
2N/A const di_node_t node,
2N/A const char *nodepath,
2N/A fpcfga_list_t *lap,
2N/A int limited_stat)
2N/A{
2N/A uint_t dctl_state = 0, devinfo_state = 0;
2N/A char *dyncomp = NULL;
2N/A cfga_list_data_t *clp = NULL;
2N/A cfga_busy_t busy;
2N/A ldata_list_t *listp = NULL;
2N/A ldata_list_t *matchldp = NULL;
2N/A int l_errno = 0;
2N/A cfga_stat_t ostate;
2N/A cfga_cond_t cond;
2N/A fpcfga_ret_t ret;
2N/A
2N/A assert(lap->apidp->xport_phys != NULL);
2N/A assert(lap->xport_logp != NULL);
2N/A
2N/A cond = CFGA_COND_UNKNOWN;
2N/A
2N/A devinfo_state = di_state(node);
2N/A ostate = dev_devinfo_to_occupant_state(devinfo_state);
2N/A
2N/A /*
2N/A * NOTE: The framework cannot currently detect layered driver
2N/A * opens, so the busy indicator is not very reliable. Also,
2N/A * non-root users will not be able to determine busy
2N/A * status (libdevice needs root permissions).
2N/A * This should probably be fixed by adding a DI_BUSY to the di_state()
2N/A * routine in libdevinfo.
2N/A */
2N/A if (devctl_cmd(nodepath, FPCFGA_DEV_GETSTATE, &dctl_state,
2N/A &l_errno) == FPCFGA_OK) {
2N/A busy = ((dctl_state & DEVICE_BUSY) == DEVICE_BUSY) ? 1 : 0;
2N/A } else {
2N/A busy = 0;
2N/A }
2N/A
2N/A /* We only want to know device config state */
2N/A if (limited_stat) {
2N/A if (((strcmp(lap->xport_type, FP_FC_FABRIC_PORT_TYPE) == 0) ||
2N/A strcmp(lap->xport_type, FP_FC_PUBLIC_PORT_TYPE) == 0)) {
2N/A lap->chld_config = CFGA_STAT_CONFIGURED;
2N/A } else {
2N/A if (ostate != CFGA_STAT_UNCONFIGURED) {
2N/A lap->chld_config = CFGA_STAT_CONFIGURED;
2N/A }
2N/A }
2N/A return (FPCFGA_OK);
2N/A }
2N/A
2N/A /*
2N/A * If child device is configured, see if it is accessible also
2N/A * for FPCFGA_STAT_FC_DEV cmd.
2N/A */
2N/A if (lap->cmd == FPCFGA_STAT_FC_DEV) {
2N/A switch (ostate) {
2N/A case CFGA_STAT_CONFIGURED:
2N/A /*
2N/A * if configured and not accessble, the device is
2N/A * till be displayed with failing condition.
2N/A * return code should be FPCFGA_OK to display it.
2N/A */
2N/A case CFGA_STAT_NONE:
2N/A /*
2N/A * If not unconfigured and not attached
2N/A * the state is set to CFGA_STAT_NONE currently.
2N/A * This is okay for the detached node due to
2N/A * the driver being unloaded.
2N/A * May need to define another state to
2N/A * isolate the detached only state.
2N/A *
2N/A * handle the same way as configured.
2N/A */
2N/A if (lap->ret != FPCFGA_ACCESS_OK) {
2N/A cond = CFGA_COND_FAILING;
2N/A }
2N/A lap->chld_config = CFGA_STAT_CONFIGURED;
2N/A break;
2N/A case CFGA_STAT_UNCONFIGURED:
2N/A /*
2N/A * if unconfigured - offline or down,
2N/A * set to cond to unusable regardless of accessibility.
2N/A * This behavior needs to be examined further.
2N/A * When the device is not accessible the node
2N/A * may get offline or down. In that case failing
2N/A * cond may make more sense.
2N/A * In anycase the ostate will be set to configured
2N/A * configured.
2N/A */
2N/A cond = CFGA_COND_UNUSABLE;
2N/A /*
2N/A * For fabric port the fca port is considered as
2N/A * configured since user configured previously
2N/A * for any existing node. Otherwise when the
2N/A * device was accessible, the hba is considered as
2N/A * configured.
2N/A */
2N/A if (((strcmp(lap->xport_type,
2N/A FP_FC_PUBLIC_PORT_TYPE) == 0) ||
2N/A (strcmp(lap->xport_type,
2N/A FP_FC_FABRIC_PORT_TYPE) == 0)) ||
2N/A (lap->ret == FPCFGA_ACCESS_OK)) {
2N/A lap->chld_config = CFGA_STAT_CONFIGURED;
2N/A } else {
2N/A lap->ret = FPCFGA_APID_NOEXIST;
2N/A return (FPCFGA_OK);
2N/A }
2N/A break;
2N/A default:
2N/A break;
2N/A }
2N/A
2N/A /* if device found in disco ports, ldata already created. */
2N/A if (lap->ret == FPCFGA_ACCESS_OK) {
2N/A /*
2N/A * if cond is not changed then don't update
2N/A * condition to keep the previous condition.
2N/A */
2N/A if (cond != CFGA_COND_UNKNOWN) {
2N/A lap->listp->ldata.ap_cond = cond;
2N/A }
2N/A lap->listp->ldata.ap_o_state = CFGA_STAT_CONFIGURED;
2N/A lap->listp->ldata.ap_busy = busy;
2N/A lap->ret = FPCFGA_OK;
2N/A return (FPCFGA_OK);
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * if cmd is stat all check ldata list
2N/A * to see if the node exist on the dev list. Otherwise create
2N/A * the list element.
2N/A */
2N/A if (lap->cmd == FPCFGA_STAT_ALL) {
2N/A if (lap->listp != NULL) {
2N/A if ((ret = make_dyncomp_from_dinode(node,
2N/A &dyncomp, &l_errno)) != FPCFGA_OK) {
2N/A return (ret);
2N/A }
2N/A ret = is_dyn_ap_on_ldata_list(dyncomp, lap->listp,
2N/A &matchldp, &l_errno);
2N/A switch (ret) {
2N/A case FPCFGA_ACCESS_OK:
2N/A /* node exists so set ostate to configured. */
2N/A lap->chld_config = CFGA_STAT_CONFIGURED;
2N/A matchldp->ldata.ap_o_state =
2N/A CFGA_STAT_CONFIGURED;
2N/A matchldp->ldata.ap_busy = busy;
2N/A clp = &matchldp->ldata;
2N/A switch (ostate) {
2N/A case CFGA_STAT_CONFIGURED:
2N/A /*
2N/A * If not unconfigured and not attached
2N/A * the state is set to CFGA_STAT_NONE currently.
2N/A * This is okay for the detached node due to
2N/A * the driver being unloaded.
2N/A * May need to define another state to
2N/A * isolate the detached only state.
2N/A */
2N/A case CFGA_STAT_NONE:
2N/A /* update ap_type and ap_info */
2N/A get_hw_info(node, clp);
2N/A break;
2N/A /*
2N/A * node is offline or down.
2N/A * set cond to unusable.
2N/A */
2N/A case CFGA_STAT_UNCONFIGURED:
2N/A /*
2N/A * if cond is not unknown
2N/A * we already set the cond from
2N/A * a different node with the same
2N/A * port WWN or initial probing
2N/A * was failed so don't update again.
2N/A */
2N/A if (matchldp->ldata.ap_cond ==
2N/A CFGA_COND_UNKNOWN) {
2N/A matchldp->ldata.ap_cond =
2N/A CFGA_COND_UNUSABLE;
2N/A }
2N/A break;
2N/A default:
2N/A break;
2N/A }
2N/A /* node found in ldata list so just return. */
2N/A lap->ret = FPCFGA_OK;
2N/A S_FREE(dyncomp);
2N/A return (FPCFGA_OK);
2N/A case FPCFGA_LIB_ERR:
2N/A lap->l_errno = l_errno;
2N/A S_FREE(dyncomp);
2N/A return (ret);
2N/A case FPCFGA_APID_NOACCESS:
2N/A switch (ostate) {
2N/A /* node is attached but not in dev list */
2N/A case CFGA_STAT_CONFIGURED:
2N/A case CFGA_STAT_NONE:
2N/A lap->chld_config = CFGA_STAT_CONFIGURED;
2N/A cond = CFGA_COND_FAILING;
2N/A break;
2N/A /*
2N/A * node is offline or down.
2N/A * set cond to unusable.
2N/A */
2N/A case CFGA_STAT_UNCONFIGURED:
2N/A /*
2N/A * For fabric port the fca port is
2N/A * considered as configured since user
2N/A * configured previously for any
2N/A * existing node.
2N/A */
2N/A cond = CFGA_COND_UNUSABLE;
2N/A if ((strcmp(lap->xport_type,
2N/A FP_FC_PUBLIC_PORT_TYPE) == 0) ||
2N/A (strcmp(lap->xport_type,
2N/A FP_FC_FABRIC_PORT_TYPE) == 0)) {
2N/A lap->chld_config =
2N/A CFGA_STAT_CONFIGURED;
2N/A } else {
2N/A lap->ret = FPCFGA_OK;
2N/A S_FREE(dyncomp);
2N/A return (FPCFGA_OK);
2N/A }
2N/A break;
2N/A default:
2N/A /*
2N/A * continue to create ldata_list struct for
2N/A * this node
2N/A */
2N/A break;
2N/A }
2N/A default:
2N/A break;
2N/A }
2N/A } else {
2N/A /*
2N/A * dev_list is null so there is no accessible dev.
2N/A * set the cond and continue to create ldata.
2N/A */
2N/A switch (ostate) {
2N/A case CFGA_STAT_CONFIGURED:
2N/A case CFGA_STAT_NONE:
2N/A cond = CFGA_COND_FAILING;
2N/A lap->chld_config = CFGA_STAT_CONFIGURED;
2N/A break;
2N/A /*
2N/A * node is offline or down.
2N/A * set cond to unusable.
2N/A */
2N/A case CFGA_STAT_UNCONFIGURED:
2N/A cond = CFGA_COND_UNUSABLE;
2N/A /*
2N/A * For fabric port the fca port is
2N/A * considered as configured since user
2N/A * configured previously for any
2N/A * existing node.
2N/A */
2N/A if ((strcmp(lap->xport_type,
2N/A FP_FC_PUBLIC_PORT_TYPE) == 0) ||
2N/A (strcmp(lap->xport_type,
2N/A FP_FC_FABRIC_PORT_TYPE) == 0)) {
2N/A lap->chld_config =
2N/A CFGA_STAT_CONFIGURED;
2N/A } else {
2N/A lap->ret = FPCFGA_OK;
2N/A S_FREE(dyncomp);
2N/A return (FPCFGA_OK);
2N/A }
2N/A break;
2N/A default:
2N/A break;
2N/A }
2N/A }
2N/A }
2N/A
2N/A listp = calloc(1, sizeof (ldata_list_t));
2N/A if (listp == NULL) {
2N/A lap->l_errno = errno;
2N/A S_FREE(dyncomp);
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A
2N/A clp = &listp->ldata;
2N/A
2N/A /* Create the dynamic component. */
2N/A if (dyncomp == NULL) {
2N/A ret = make_dyncomp_from_dinode(node, &dyncomp, &l_errno);
2N/A if (ret != FPCFGA_OK) {
2N/A S_FREE(listp);
2N/A return (ret);
2N/A }
2N/A }
2N/A
2N/A /* Create logical and physical ap_id */
2N/A (void) snprintf(clp->ap_log_id, sizeof (clp->ap_log_id), "%s%s%s",
2N/A lap->xport_logp, DYN_SEP, dyncomp);
2N/A
2N/A (void) snprintf(clp->ap_phys_id, sizeof (clp->ap_phys_id), "%s%s%s",
2N/A lap->apidp->xport_phys, DYN_SEP, dyncomp);
2N/A
2N/A S_FREE(dyncomp);
2N/A
2N/A clp->ap_class[0] = '\0'; /* Filled in by libcfgadm */
2N/A clp->ap_r_state = lap->xport_rstate;
2N/A /* set to ostate to configured and set cond with info. */
2N/A clp->ap_o_state = CFGA_STAT_CONFIGURED;
2N/A clp->ap_cond = cond;
2N/A clp->ap_busy = busy;
2N/A clp->ap_status_time = (time_t)-1;
2N/A
2N/A /* get ap_type and ap_info. */
2N/A get_hw_info(node, clp);
2N/A
2N/A /* Link it in */
2N/A listp->next = lap->listp;
2N/A lap->listp = listp;
2N/A
2N/A lap->ret = FPCFGA_OK;
2N/A return (FPCFGA_OK);
2N/A}
2N/A
2N/A/*
2N/A * Wrapper routine for handling path info.
2N/A *
2N/A * When show_FCP_dev option is given stat_path_info_FCP_dev() is called.
2N/A * Otherwise stat_path_info_fc_dev() is called.
2N/A */
2N/Aint
2N/Astat_path_info_node(
2N/A di_node_t root,
2N/A void *arg,
2N/A int *l_errnop)
2N/A{
2N/A fpcfga_list_t *lap = NULL;
2N/A
2N/A lap = (fpcfga_list_t *)arg;
2N/A if ((lap->apidp->flags & (FLAG_FCP_DEV)) == FLAG_FCP_DEV) {
2N/A return (stat_path_info_FCP_dev(root, lap, l_errnop));
2N/A } else {
2N/A return (stat_path_info_fc_dev(root, lap, l_errnop));
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Routine for updating ldata list based on the state of path info node.
2N/A * When no matching accessible ldata is found a new ldata is created
2N/A * with proper state information.
2N/A *
2N/A * Overall algorithm:
2N/A * If the path info node is not offline and the matching ldata is found
2N/A * the target device is updated with configued and unknown condition.
2N/A * If the path info node is offline or failed and the matching ldata is found
2N/A * the target device is updated with configued and unusable condition.
2N/A * If the path info node is online but the matching ldata is not found
2N/A * the target device is created with configued and failing condition.
2N/A * If the path info is offline or failed and the matching ldata is not found
2N/A * the target device is created with configued and unusable condition.
2N/A */
2N/Astatic int
2N/Astat_path_info_fc_dev(
2N/A di_node_t root,
2N/A fpcfga_list_t *lap,
2N/A int *l_errnop)
2N/A{
2N/A ldata_list_t *matchldp = NULL;
2N/A di_path_t path = DI_PATH_NIL;
2N/A uchar_t *port_wwn_data;
2N/A char port_wwn[WWN_SIZE*2+1];
2N/A int count;
2N/A fpcfga_ret_t ret;
2N/A di_path_state_t pstate;
2N/A
2N/A if (root == DI_NODE_NIL) {
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A
2N/A /*
2N/A * if stat on a specific dev and walk_node found it okay
2N/A * then just return ok.
2N/A */
2N/A if ((lap->cmd == FPCFGA_STAT_FC_DEV) && (lap->ret == FPCFGA_OK)) {
2N/A return (FPCFGA_OK);
2N/A }
2N/A
2N/A /*
2N/A * if stat on a fca xport and chld_config is set
2N/A * then just return ok.
2N/A */
2N/A if ((lap->cmd == FPCFGA_STAT_FCA_PORT) &&
2N/A (lap->chld_config == CFGA_STAT_CONFIGURED)) {
2N/A return (FPCFGA_OK);
2N/A }
2N/A
2N/A /*
2N/A * when there is no path_info node return FPCFGA_OK.
2N/A * That way the result from walk_node shall be maintained.
2N/A */
2N/A if ((path = di_path_next_client(root, path)) == DI_PATH_NIL) {
2N/A /*
2N/A * if the dev was in dev list but not found
2N/A * return OK to indicate is not configured.
2N/A */
2N/A if (lap->ret == FPCFGA_ACCESS_OK) {
2N/A lap->ret = FPCFGA_OK;
2N/A }
2N/A return (FPCFGA_OK);
2N/A }
2N/A
2N/A /* if stat on fca port return. */
2N/A if (lap->cmd == FPCFGA_STAT_FCA_PORT) {
2N/A if (((strcmp(lap->xport_type, FP_FC_FABRIC_PORT_TYPE) == 0) ||
2N/A strcmp(lap->xport_type, FP_FC_PUBLIC_PORT_TYPE) == 0)) {
2N/A lap->chld_config = CFGA_STAT_CONFIGURED;
2N/A return (FPCFGA_OK);
2N/A } else {
2N/A if ((pstate = di_path_state(path)) !=
2N/A DI_PATH_STATE_OFFLINE) {
2N/A lap->chld_config = CFGA_STAT_CONFIGURED;
2N/A return (FPCFGA_OK);
2N/A }
2N/A }
2N/A }
2N/A /*
2N/A * now parse the path info node.
2N/A */
2N/A do {
2N/A count = di_path_prop_lookup_bytes(path, PORT_WWN_PROP,
2N/A &port_wwn_data);
2N/A if (count != WWN_SIZE) {
2N/A ret = FPCFGA_LIB_ERR;
2N/A break;
2N/A }
2N/A
2N/A (void) sprintf(port_wwn, "%016llx",
2N/A (wwnConversion(port_wwn_data)));
2N/A switch (lap->cmd) {
2N/A case FPCFGA_STAT_FC_DEV:
2N/A /* if no match contine to the next path info node. */
2N/A if (strncmp(port_wwn, lap->apidp->dyncomp,
2N/A WWN_SIZE*2)) {
2N/A break;
2N/A }
2N/A /* if device in dev_list, ldata already created. */
2N/A if (lap->ret == FPCFGA_ACCESS_OK) {
2N/A lap->listp->ldata.ap_o_state =
2N/A CFGA_STAT_CONFIGURED;
2N/A if (((pstate = di_path_state(path)) ==
2N/A DI_PATH_STATE_OFFLINE) ||
2N/A (pstate == DI_PATH_STATE_FAULT)) {
2N/A lap->listp->ldata.ap_cond =
2N/A CFGA_COND_UNUSABLE;
2N/A }
2N/A lap->ret = FPCFGA_OK;
2N/A return (FPCFGA_OK);
2N/A } else {
2N/A if ((strcmp(lap->xport_type,
2N/A FP_FC_PUBLIC_PORT_TYPE) == 0) ||
2N/A (strcmp(lap->xport_type,
2N/A FP_FC_FABRIC_PORT_TYPE) == 0)) {
2N/A lap->chld_config = CFGA_STAT_CONFIGURED;
2N/A return (init_ldata_for_mpath_dev(
2N/A path, port_wwn, l_errnop, lap));
2N/A } else {
2N/A if ((di_path_state(path)) !=
2N/A DI_PATH_STATE_OFFLINE) {
2N/A return (init_ldata_for_mpath_dev(
2N/A path, port_wwn, l_errnop, lap));
2N/A } else {
2N/A lap->ret = FPCFGA_APID_NOEXIST;
2N/A return (FPCFGA_OK);
2N/A }
2N/A }
2N/A }
2N/A case FPCFGA_STAT_ALL:
2N/A /* check if there is list data. */
2N/A if (lap->listp != NULL) {
2N/A ret = is_dyn_ap_on_ldata_list(port_wwn,
2N/A lap->listp, &matchldp, l_errnop);
2N/A if (ret == FPCFGA_ACCESS_OK) {
2N/A lap->chld_config = CFGA_STAT_CONFIGURED;
2N/A matchldp->ldata.ap_o_state =
2N/A CFGA_STAT_CONFIGURED;
2N/A /*
2N/A * Update the condition as unusable
2N/A * if the pathinfo state is failed
2N/A * or offline.
2N/A */
2N/A if (((pstate = di_path_state(path)) ==
2N/A DI_PATH_STATE_OFFLINE) ||
2N/A (pstate ==
2N/A DI_PATH_STATE_FAULT)) {
2N/A matchldp->ldata.ap_cond =
2N/A CFGA_COND_UNUSABLE;
2N/A }
2N/A break;
2N/A } else if (ret == FPCFGA_LIB_ERR) {
2N/A lap->l_errno = *l_errnop;
2N/A return (ret);
2N/A }
2N/A }
2N/A /*
2N/A * now create ldata for this particular path info node.
2N/A * if port top is private loop and pathinfo is in
2N/A * in offline state don't include to ldata list.
2N/A */
2N/A if (((strcmp(lap->xport_type,
2N/A FP_FC_PUBLIC_PORT_TYPE) == 0) ||
2N/A (strcmp(lap->xport_type,
2N/A FP_FC_FABRIC_PORT_TYPE) == 0)) ||
2N/A (di_path_state(path) !=
2N/A DI_PATH_STATE_OFFLINE)) {
2N/A lap->chld_config = CFGA_STAT_CONFIGURED;
2N/A ret = init_ldata_for_mpath_dev(
2N/A path, port_wwn, l_errnop, lap);
2N/A if (ret != FPCFGA_OK) {
2N/A return (ret);
2N/A }
2N/A }
2N/A break;
2N/A case FPCFGA_STAT_FCA_PORT:
2N/A if (di_path_state(path) != DI_PATH_STATE_OFFLINE) {
2N/A lap->chld_config = CFGA_STAT_CONFIGURED;
2N/A return (FPCFGA_OK);
2N/A }
2N/A }
2N/A path = di_path_next_client(root, path);
2N/A } while (path != DI_PATH_NIL);
2N/A
2N/A return (FPCFGA_OK);
2N/A
2N/A}
2N/A
2N/A/*
2N/A * Routine for updating ldata list based on the state of path info node.
2N/A * When no matching accessible ldata is found a new ldata is created
2N/A * with proper state information.
2N/A *
2N/A * The difference from stat_path_info_fc_dev() is
2N/A * to handle FCP SCSI LUN information. Otherwise overall algorithm is
2N/A * same.
2N/A *
2N/A * Overall algorithm:
2N/A * If the path info node is not offline and the matching ldata is found
2N/A * the target device is updated with configued and unknown condition.
2N/A * If the path info node is offline or failed and the matching ldata is found
2N/A * the target device is updated with configued and unusable condition.
2N/A * If the path info node is online but the matching ldata is not found
2N/A * the target device is created with configued and failing condition.
2N/A * If the path info is offline or failed and the matching ldata is not found
2N/A * the target device is created with configued and unusable condition.
2N/A */
2N/Astatic int
2N/Astat_path_info_FCP_dev(
2N/A di_node_t root,
2N/A fpcfga_list_t *lap,
2N/A int *l_errnop)
2N/A{
2N/A ldata_list_t *matchldp = NULL, *listp = NULL;
2N/A cfga_list_data_t *clp;
2N/A di_path_t path = DI_PATH_NIL;
2N/A di_node_t client_node = DI_NODE_NIL;
2N/A char *port_wwn = NULL, *nodepath = NULL;
2N/A int *lun_nump;
2N/A fpcfga_ret_t ldata_ret;
2N/A di_path_state_t pstate;
2N/A cfga_busy_t busy;
2N/A uint_t dctl_state = 0;
2N/A
2N/A if (root == DI_NODE_NIL) {
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A
2N/A /*
2N/A * if stat on a fca xport and chld_config is set
2N/A * then just return ok.
2N/A */
2N/A if ((lap->cmd == FPCFGA_STAT_FCA_PORT) &&
2N/A (lap->chld_config == CFGA_STAT_CONFIGURED)) {
2N/A return (FPCFGA_OK);
2N/A }
2N/A /*
2N/A * when there is no path_info node return FPCFGA_OK.
2N/A * That way the result from walk_node shall be maintained.
2N/A */
2N/A if ((path = di_path_next_client(root, path)) == DI_PATH_NIL) {
2N/A /*
2N/A * if the dev was in dev list but not found
2N/A * return ok.
2N/A */
2N/A if (lap->ret == FPCFGA_ACCESS_OK) {
2N/A lap->ret = FPCFGA_OK;
2N/A }
2N/A return (FPCFGA_OK);
2N/A }
2N/A /*
2N/A * If stat on fca port and port topology is fabric return here.
2N/A * If not fabric return only when path state is not offfline.
2N/A * The other cases are handbled below.
2N/A */
2N/A if (lap->cmd == FPCFGA_STAT_FCA_PORT) {
2N/A if (((strcmp(lap->xport_type, FP_FC_FABRIC_PORT_TYPE) == 0) ||
2N/A strcmp(lap->xport_type, FP_FC_PUBLIC_PORT_TYPE) == 0)) {
2N/A lap->chld_config = CFGA_STAT_CONFIGURED;
2N/A return (FPCFGA_OK);
2N/A } else {
2N/A if ((pstate = di_path_state(path)) !=
2N/A DI_PATH_STATE_OFFLINE) {
2N/A lap->chld_config = CFGA_STAT_CONFIGURED;
2N/A return (FPCFGA_OK);
2N/A }
2N/A }
2N/A }
2N/A /*
2N/A * now parse the path info node.
2N/A */
2N/A do {
2N/A switch (lap->cmd) {
2N/A case FPCFGA_STAT_FC_DEV:
2N/A if ((make_portwwn_luncomp_from_pinode(path, &port_wwn,
2N/A &lun_nump, l_errnop)) != FPCFGA_OK) {
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A
2N/A if ((ldata_ret = is_FCP_dev_ap_on_ldata_list(port_wwn,
2N/A *lun_nump, lap->listp, &matchldp))
2N/A == FPCFGA_LIB_ERR) {
2N/A S_FREE(port_wwn);
2N/A return (ldata_ret);
2N/A }
2N/A
2N/A if (ldata_ret == FPCFGA_ACCESS_OK) {
2N/A lap->chld_config = CFGA_STAT_CONFIGURED;
2N/A matchldp->ldata.ap_o_state =
2N/A CFGA_STAT_CONFIGURED;
2N/A /*
2N/A * Update the condition as unusable
2N/A * if the pathinfo state is failed
2N/A * or offline.
2N/A */
2N/A if (((pstate = di_path_state(path)) ==
2N/A DI_PATH_STATE_OFFLINE) ||
2N/A (pstate == DI_PATH_STATE_FAULT)) {
2N/A matchldp->ldata.ap_cond =
2N/A CFGA_COND_UNUSABLE;
2N/A }
2N/A lap->ret = FPCFGA_OK;
2N/A break;
2N/A }
2N/A
2N/A if (strncmp(port_wwn, lap->apidp->dyncomp, WWN_SIZE*2)
2N/A != 0) {
2N/A break;
2N/A }
2N/A /*
2N/A * now create ldata for this particular path info node.
2N/A * if port top is private loop and pathinfo is in
2N/A * in offline state don't include to ldata list.
2N/A */
2N/A if (((strcmp(lap->xport_type,
2N/A FP_FC_PUBLIC_PORT_TYPE) == 0) ||
2N/A (strcmp(lap->xport_type,
2N/A FP_FC_FABRIC_PORT_TYPE) == 0)) ||
2N/A (di_path_state(path) !=
2N/A DI_PATH_STATE_OFFLINE)) {
2N/A lap->chld_config = CFGA_STAT_CONFIGURED;
2N/A /* create ldata for this pi node. */
2N/A client_node = di_path_client_node(path);
2N/A if (client_node == DI_NODE_NIL) {
2N/A *l_errnop = errno;
2N/A S_FREE(port_wwn);
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A if ((construct_nodepath_from_dinode(
2N/A client_node, &nodepath, l_errnop))
2N/A != FPCFGA_OK) {
2N/A S_FREE(port_wwn);
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A
2N/A listp = calloc(1, sizeof (ldata_list_t));
2N/A if (listp == NULL) {
2N/A S_FREE(port_wwn);
2N/A S_FREE(nodepath);
2N/A lap->l_errno = errno;
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A
2N/A clp = &listp->ldata;
2N/A
2N/A /* Create logical and physical ap_id */
2N/A (void) snprintf(clp->ap_log_id,
2N/A sizeof (clp->ap_log_id), "%s%s%s%s%d",
2N/A lap->xport_logp, DYN_SEP, port_wwn,
2N/A LUN_COMP_SEP, *lun_nump);
2N/A (void) snprintf(clp->ap_phys_id,
2N/A sizeof (clp->ap_phys_id), "%s%s%s%s%d",
2N/A lap->apidp->xport_phys, DYN_SEP, port_wwn,
2N/A LUN_COMP_SEP, *lun_nump);
2N/A /*
2N/A * We reached here since FCP dev is not found
2N/A * in ldata list but path info node exists.
2N/A *
2N/A * Update the condition as failing
2N/A * if the pathinfo state was normal.
2N/A * Update the condition as unusable
2N/A * if the pathinfo state is failed
2N/A * or offline.
2N/A */
2N/A clp->ap_class[0] = '\0'; /* Filled by libcfgadm */
2N/A clp->ap_o_state = CFGA_STAT_CONFIGURED;
2N/A if (((pstate = di_path_state(path))
2N/A == DI_PATH_STATE_OFFLINE) ||
2N/A (pstate == DI_PATH_STATE_FAULT)) {
2N/A clp->ap_cond = CFGA_COND_UNUSABLE;
2N/A } else {
2N/A clp->ap_cond = CFGA_COND_FAILING;
2N/A }
2N/A clp->ap_r_state = lap->xport_rstate;
2N/A clp->ap_info[0] = '\0';
2N/A /* update ap_type and ap_info */
2N/A get_hw_info(client_node, clp);
2N/A if (devctl_cmd(nodepath, FPCFGA_DEV_GETSTATE,
2N/A &dctl_state, l_errnop) == FPCFGA_OK) {
2N/A busy = ((dctl_state & DEVICE_BUSY)
2N/A == DEVICE_BUSY) ? 1 : 0;
2N/A } else {
2N/A busy = 0;
2N/A }
2N/A clp->ap_busy = busy;
2N/A clp->ap_status_time = (time_t)-1;
2N/A
2N/A (void) insert_ldata_to_ldatalist(port_wwn,
2N/A lun_nump, listp, &(lap->listp));
2N/A }
2N/A break;
2N/A case FPCFGA_STAT_ALL:
2N/A if ((make_portwwn_luncomp_from_pinode(path, &port_wwn,
2N/A &lun_nump, l_errnop)) != FPCFGA_OK) {
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A
2N/A if ((ldata_ret = is_FCP_dev_ap_on_ldata_list(port_wwn,
2N/A *lun_nump, lap->listp, &matchldp))
2N/A == FPCFGA_LIB_ERR) {
2N/A S_FREE(port_wwn);
2N/A return (ldata_ret);
2N/A }
2N/A
2N/A if (ldata_ret == FPCFGA_ACCESS_OK) {
2N/A lap->chld_config = CFGA_STAT_CONFIGURED;
2N/A matchldp->ldata.ap_o_state =
2N/A CFGA_STAT_CONFIGURED;
2N/A /*
2N/A * Update the condition as unusable
2N/A * if the pathinfo state is failed
2N/A * or offline.
2N/A */
2N/A if (((pstate = di_path_state(path)) ==
2N/A DI_PATH_STATE_OFFLINE) ||
2N/A (pstate == DI_PATH_STATE_FAULT)) {
2N/A matchldp->ldata.ap_cond =
2N/A CFGA_COND_UNUSABLE;
2N/A }
2N/A break;
2N/A }
2N/A /*
2N/A * now create ldata for this particular path info node.
2N/A * if port top is private loop and pathinfo is in
2N/A * in offline state don't include to ldata list.
2N/A */
2N/A if (((strcmp(lap->xport_type,
2N/A FP_FC_PUBLIC_PORT_TYPE) == 0) ||
2N/A (strcmp(lap->xport_type,
2N/A FP_FC_FABRIC_PORT_TYPE) == 0)) ||
2N/A (di_path_state(path) !=
2N/A DI_PATH_STATE_OFFLINE)) {
2N/A lap->chld_config = CFGA_STAT_CONFIGURED;
2N/A /* create ldata for this pi node. */
2N/A client_node = di_path_client_node(path);
2N/A if (client_node == DI_NODE_NIL) {
2N/A *l_errnop = errno;
2N/A S_FREE(port_wwn);
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A if ((construct_nodepath_from_dinode(
2N/A client_node, &nodepath, l_errnop))
2N/A != FPCFGA_OK) {
2N/A S_FREE(port_wwn);
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A
2N/A listp = calloc(1, sizeof (ldata_list_t));
2N/A if (listp == NULL) {
2N/A S_FREE(port_wwn);
2N/A S_FREE(nodepath);
2N/A lap->l_errno = errno;
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A
2N/A clp = &listp->ldata;
2N/A
2N/A /* Create logical and physical ap_id */
2N/A (void) snprintf(clp->ap_log_id,
2N/A sizeof (clp->ap_log_id), "%s%s%s%s%d",
2N/A lap->xport_logp, DYN_SEP, port_wwn,
2N/A LUN_COMP_SEP, *lun_nump);
2N/A (void) snprintf(clp->ap_phys_id,
2N/A sizeof (clp->ap_phys_id), "%s%s%s%s%d",
2N/A lap->apidp->xport_phys, DYN_SEP, port_wwn,
2N/A LUN_COMP_SEP, *lun_nump);
2N/A /*
2N/A * We reached here since FCP dev is not found
2N/A * in ldata list but path info node exists.
2N/A *
2N/A * Update the condition as failing
2N/A * if the pathinfo state was normal.
2N/A * Update the condition as unusable
2N/A * if the pathinfo state is failed
2N/A * or offline.
2N/A */
2N/A clp->ap_class[0] = '\0'; /* Filled by libcfgadm */
2N/A clp->ap_o_state = CFGA_STAT_CONFIGURED;
2N/A if (((pstate = di_path_state(path))
2N/A == DI_PATH_STATE_OFFLINE) ||
2N/A (pstate == DI_PATH_STATE_FAULT)) {
2N/A clp->ap_cond = CFGA_COND_UNUSABLE;
2N/A } else {
2N/A clp->ap_cond = CFGA_COND_FAILING;
2N/A }
2N/A clp->ap_r_state = lap->xport_rstate;
2N/A clp->ap_info[0] = '\0';
2N/A /* update ap_type and ap_info */
2N/A get_hw_info(client_node, clp);
2N/A if (devctl_cmd(nodepath, FPCFGA_DEV_GETSTATE,
2N/A &dctl_state, l_errnop) == FPCFGA_OK) {
2N/A busy = ((dctl_state & DEVICE_BUSY)
2N/A == DEVICE_BUSY) ? 1 : 0;
2N/A } else {
2N/A busy = 0;
2N/A }
2N/A clp->ap_busy = busy;
2N/A clp->ap_status_time = (time_t)-1;
2N/A
2N/A (void) insert_ldata_to_ldatalist(port_wwn,
2N/A lun_nump, listp, &(lap->listp));
2N/A }
2N/A break;
2N/A case FPCFGA_STAT_FCA_PORT:
2N/A if (di_path_state(path) != DI_PATH_STATE_OFFLINE) {
2N/A lap->chld_config = CFGA_STAT_CONFIGURED;
2N/A lap->ret = FPCFGA_OK;
2N/A return (FPCFGA_OK);
2N/A }
2N/A }
2N/A path = di_path_next_client(root, path);
2N/A } while (path != DI_PATH_NIL);
2N/A
2N/A lap->ret = FPCFGA_OK;
2N/A S_FREE(port_wwn);
2N/A S_FREE(nodepath);
2N/A return (FPCFGA_OK);
2N/A
2N/A}
2N/A
2N/A/*
2N/A * Routine for updating ldata list based on the state of device node.
2N/A * When no matching accessible ldata is found a new ldata is created
2N/A * with proper state information.
2N/A *
2N/A * The difference from do_stat_fc_dev() is
2N/A * to handle FCP SCSI LUN information. Otherwise overall algorithm is
2N/A * same.
2N/A *
2N/A * Overall algorithm:
2N/A * If the device node is online and the matching ldata is found
2N/A * the target device is updated with configued and unknown condition.
2N/A * If the device node is offline or down and the matching ldata is found
2N/A * the target device is updated with configued and unusable condition.
2N/A * If the device node is online but the matching ldata is not found
2N/A * the target device is created with configued and failing condition.
2N/A * If the device node is offline or down and the matching ldata is not found
2N/A * the target device is created with configued and unusable condition.
2N/A */
2N/Astatic fpcfga_ret_t
2N/Ado_stat_FCP_dev(
2N/A const di_node_t node,
2N/A const char *nodepath,
2N/A fpcfga_list_t *lap,
2N/A int limited_stat)
2N/A{
2N/A uint_t dctl_state = 0, devinfo_state = 0;
2N/A char *port_wwn = NULL;
2N/A cfga_list_data_t *clp = NULL;
2N/A cfga_busy_t busy;
2N/A ldata_list_t *listp = NULL;
2N/A ldata_list_t *matchldp = NULL;
2N/A int l_errno = 0, *lun_nump;
2N/A cfga_stat_t ostate;
2N/A cfga_cond_t cond;
2N/A fpcfga_ret_t ldata_ret;
2N/A
2N/A assert(lap->apidp->xport_phys != NULL);
2N/A assert(lap->xport_logp != NULL);
2N/A
2N/A cond = CFGA_COND_UNKNOWN;
2N/A
2N/A devinfo_state = di_state(node);
2N/A ostate = dev_devinfo_to_occupant_state(devinfo_state);
2N/A
2N/A /*
2N/A * NOTE: The devctl framework cannot currently detect layered driver
2N/A * opens, so the busy indicator is not very reliable. Also,
2N/A * non-root users will not be able to determine busy
2N/A * status (libdevice needs root permissions).
2N/A * This should probably be fixed by adding a DI_BUSY to the di_state()
2N/A * routine in libdevinfo.
2N/A */
2N/A if (devctl_cmd(nodepath, FPCFGA_DEV_GETSTATE, &dctl_state,
2N/A &l_errno) == FPCFGA_OK) {
2N/A busy = ((dctl_state & DEVICE_BUSY) == DEVICE_BUSY) ? 1 : 0;
2N/A } else {
2N/A busy = 0;
2N/A }
2N/A
2N/A /* We only want to know device config state */
2N/A if (limited_stat) {
2N/A if (((strcmp(lap->xport_type, FP_FC_FABRIC_PORT_TYPE) == 0) ||
2N/A strcmp(lap->xport_type, FP_FC_PUBLIC_PORT_TYPE) == 0)) {
2N/A lap->chld_config = CFGA_STAT_CONFIGURED;
2N/A } else {
2N/A if (ostate != CFGA_STAT_UNCONFIGURED) {
2N/A lap->chld_config = CFGA_STAT_CONFIGURED;
2N/A }
2N/A }
2N/A return (FPCFGA_OK);
2N/A }
2N/A
2N/A /*
2N/A * If child device is configured, see if it is accessible also
2N/A * for FPCFGA_STAT_FC_DEV cmd.
2N/A */
2N/A if ((make_portwwn_luncomp_from_dinode(node, &port_wwn, &lun_nump,
2N/A &l_errno)) != FPCFGA_OK) {
2N/A lap->l_errno = l_errno;
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A
2N/A if ((ldata_ret = is_FCP_dev_ap_on_ldata_list(port_wwn, *lun_nump,
2N/A lap->listp, &matchldp)) == FPCFGA_LIB_ERR) {
2N/A lap->l_errno = l_errno;
2N/A S_FREE(port_wwn);
2N/A return (ldata_ret);
2N/A }
2N/A
2N/A if (lap->cmd == FPCFGA_STAT_FC_DEV) {
2N/A switch (ostate) {
2N/A case CFGA_STAT_CONFIGURED:
2N/A /*
2N/A * if configured and not accessble, the device is
2N/A * till be displayed with failing condition.
2N/A * return code should be FPCFGA_OK to display it.
2N/A */
2N/A case CFGA_STAT_NONE:
2N/A /*
2N/A * If not unconfigured and not attached
2N/A * the state is set to CFGA_STAT_NONE currently.
2N/A * This is okay for the detached node due to
2N/A * the driver being unloaded.
2N/A * May need to define another state to
2N/A * isolate the detached only state.
2N/A *
2N/A * handle the same way as configured.
2N/A */
2N/A if (ldata_ret != FPCFGA_ACCESS_OK) {
2N/A cond = CFGA_COND_FAILING;
2N/A }
2N/A lap->chld_config = CFGA_STAT_CONFIGURED;
2N/A break;
2N/A case CFGA_STAT_UNCONFIGURED:
2N/A /*
2N/A * if unconfigured - offline or down,
2N/A * set to cond to unusable regardless of accessibility.
2N/A * This behavior needs to be examined further.
2N/A * When the device is not accessible the node
2N/A * may get offline or down. In that case failing
2N/A * cond may make more sense.
2N/A * In anycase the ostate will be set to configured
2N/A * configured.
2N/A */
2N/A cond = CFGA_COND_UNUSABLE;
2N/A /*
2N/A * For fabric port the fca port is considered as
2N/A * configured since user configured previously
2N/A * for any existing node. Otherwise when the
2N/A * device was accessible, the hba is considered as
2N/A * configured.
2N/A */
2N/A if (((strcmp(lap->xport_type,
2N/A FP_FC_PUBLIC_PORT_TYPE) == 0) ||
2N/A (strcmp(lap->xport_type,
2N/A FP_FC_FABRIC_PORT_TYPE) == 0)) ||
2N/A (lap->ret == FPCFGA_ACCESS_OK)) {
2N/A lap->chld_config = CFGA_STAT_CONFIGURED;
2N/A } else {
2N/A /*
2N/A * if lap->ret is okay there is at least
2N/A * one matching ldata exist. Need to keep
2N/A * okay ret to display the matching ones.
2N/A */
2N/A if (lap->ret != FPCFGA_OK) {
2N/A lap->ret = FPCFGA_APID_NOEXIST;
2N/A }
2N/A S_FREE(port_wwn);
2N/A return (FPCFGA_OK);
2N/A }
2N/A break;
2N/A default:
2N/A break;
2N/A }
2N/A
2N/A /* if device found in dev_list, ldata already created. */
2N/A if (ldata_ret == FPCFGA_ACCESS_OK) {
2N/A /*
2N/A * if cond is not changed then don't update
2N/A * condition to keep any condtion
2N/A * from initial discovery. If the initial
2N/A * cond was failed the same condition will be kept.
2N/A */
2N/A if (cond != CFGA_COND_UNKNOWN) {
2N/A matchldp->ldata.ap_cond = cond;
2N/A }
2N/A matchldp->ldata.ap_o_state = CFGA_STAT_CONFIGURED;
2N/A matchldp->ldata.ap_busy = busy;
2N/A /* update ap_info via inquiry */
2N/A clp = &matchldp->ldata;
2N/A /* update ap_type and ap_info */
2N/A get_hw_info(node, clp);
2N/A lap->ret = FPCFGA_OK;
2N/A S_FREE(port_wwn);
2N/A return (FPCFGA_OK);
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * if cmd is stat all check ldata list
2N/A * to see if the node exist on the dev list. Otherwise create
2N/A * the list element.
2N/A */
2N/A if (lap->cmd == FPCFGA_STAT_ALL) {
2N/A switch (ldata_ret) {
2N/A case FPCFGA_ACCESS_OK:
2N/A /* node exists so set ostate to configured. */
2N/A lap->chld_config = CFGA_STAT_CONFIGURED;
2N/A matchldp->ldata.ap_o_state =
2N/A CFGA_STAT_CONFIGURED;
2N/A matchldp->ldata.ap_busy = busy;
2N/A clp = &matchldp->ldata;
2N/A switch (ostate) {
2N/A case CFGA_STAT_CONFIGURED:
2N/A /*
2N/A * If not unconfigured and not attached
2N/A * the state is set to CFGA_STAT_NONE currently.
2N/A * This is okay for the detached node due to
2N/A * the driver being unloaded.
2N/A * May need to define another state to
2N/A * isolate the detached only state.
2N/A */
2N/A case CFGA_STAT_NONE:
2N/A /* update ap_type and ap_info */
2N/A get_hw_info(node, clp);
2N/A break;
2N/A /*
2N/A * node is offline or down.
2N/A * set cond to unusable.
2N/A */
2N/A case CFGA_STAT_UNCONFIGURED:
2N/A /*
2N/A * if cond is not unknown
2N/A * initial probing was failed
2N/A * so don't update again.
2N/A */
2N/A if (matchldp->ldata.ap_cond ==
2N/A CFGA_COND_UNKNOWN) {
2N/A matchldp->ldata.ap_cond =
2N/A CFGA_COND_UNUSABLE;
2N/A }
2N/A break;
2N/A default:
2N/A break;
2N/A }
2N/A /* node found in ldata list so just return. */
2N/A lap->ret = FPCFGA_OK;
2N/A S_FREE(port_wwn);
2N/A return (FPCFGA_OK);
2N/A case FPCFGA_APID_NOACCESS:
2N/A switch (ostate) {
2N/A /* node is attached but not in dev list */
2N/A case CFGA_STAT_CONFIGURED:
2N/A case CFGA_STAT_NONE:
2N/A lap->chld_config = CFGA_STAT_CONFIGURED;
2N/A cond = CFGA_COND_FAILING;
2N/A break;
2N/A /*
2N/A * node is offline or down.
2N/A * set cond to unusable.
2N/A */
2N/A case CFGA_STAT_UNCONFIGURED:
2N/A /*
2N/A * For fabric port the fca port is
2N/A * considered as configured since user
2N/A * configured previously for any
2N/A * existing node.
2N/A */
2N/A cond = CFGA_COND_UNUSABLE;
2N/A if ((strcmp(lap->xport_type,
2N/A FP_FC_PUBLIC_PORT_TYPE) == 0) ||
2N/A (strcmp(lap->xport_type,
2N/A FP_FC_FABRIC_PORT_TYPE) == 0)) {
2N/A lap->chld_config =
2N/A CFGA_STAT_CONFIGURED;
2N/A } else {
2N/A lap->ret = FPCFGA_OK;
2N/A S_FREE(port_wwn);
2N/A return (FPCFGA_OK);
2N/A }
2N/A break;
2N/A default:
2N/A /*
2N/A * continue to create ldata_list struct for
2N/A * this node
2N/A */
2N/A break;
2N/A }
2N/A default:
2N/A break;
2N/A }
2N/A }
2N/A
2N/A listp = calloc(1, sizeof (ldata_list_t));
2N/A if (listp == NULL) {
2N/A lap->l_errno = errno;
2N/A S_FREE(port_wwn);
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A
2N/A clp = &listp->ldata;
2N/A
2N/A /* Create logical and physical ap_id */
2N/A (void) snprintf(clp->ap_log_id, sizeof (clp->ap_log_id),
2N/A "%s%s%s%s%d", lap->xport_logp, DYN_SEP, port_wwn,
2N/A LUN_COMP_SEP, *lun_nump);
2N/A (void) snprintf(clp->ap_phys_id, sizeof (clp->ap_phys_id),
2N/A "%s%s%s%s%d", lap->apidp->xport_phys, DYN_SEP, port_wwn,
2N/A LUN_COMP_SEP, *lun_nump);
2N/A clp->ap_class[0] = '\0'; /* Filled in by libcfgadm */
2N/A clp->ap_r_state = lap->xport_rstate;
2N/A clp->ap_o_state = CFGA_STAT_CONFIGURED;
2N/A clp->ap_cond = cond;
2N/A clp->ap_busy = busy;
2N/A clp->ap_status_time = (time_t)-1;
2N/A clp->ap_info[0] = '\0';
2N/A
2N/A get_hw_info(node, clp);
2N/A
2N/A (void) insert_ldata_to_ldatalist(port_wwn, lun_nump, listp,
2N/A &(lap->listp));
2N/A
2N/A lap->ret = FPCFGA_OK;
2N/A S_FREE(port_wwn);
2N/A return (FPCFGA_OK);
2N/A}
2N/A
2N/A/*
2N/A * Searches the ldata_list to find if the the input port_wwn exist.
2N/A *
2N/A * Input: port_wwn, ldata_list.
2N/A * Return value: FPCFGA_APID_NOACCESS if not found on ldata_list.
2N/A * FPCFGA_ACCESS_OK if found on ldata_list.
2N/A */
2N/Astatic fpcfga_ret_t
2N/Ais_dyn_ap_on_ldata_list(const char *port_wwn, const ldata_list_t *listp,
2N/A ldata_list_t **matchldpp, int *l_errnop)
2N/A{
2N/A char *dyn = NULL, *dyncomp = NULL;
2N/A int len;
2N/A ldata_list_t *tmplp;
2N/A fpcfga_ret_t ret;
2N/A
2N/A
2N/A ret = FPCFGA_APID_NOACCESS;
2N/A
2N/A tmplp = (ldata_list_t *)listp;
2N/A while (tmplp != NULL) {
2N/A if ((dyn = GET_DYN(tmplp->ldata.ap_phys_id)) != NULL) {
2N/A len = strlen(DYN_TO_DYNCOMP(dyn)) + 1;
2N/A dyncomp = calloc(1, len);
2N/A if (dyncomp == NULL) {
2N/A *l_errnop = errno;
2N/A ret = FPCFGA_LIB_ERR;
2N/A break;
2N/A }
2N/A (void) strcpy(dyncomp, DYN_TO_DYNCOMP(dyn));
2N/A if (!(strncmp(port_wwn, dyncomp, WWN_SIZE*2))) {
2N/A *matchldpp = tmplp;
2N/A S_FREE(dyncomp);
2N/A ret = FPCFGA_ACCESS_OK;
2N/A break;
2N/A }
2N/A S_FREE(dyncomp);
2N/A }
2N/A tmplp = tmplp->next;
2N/A }
2N/A
2N/A return (ret);
2N/A}
2N/A
2N/A/*
2N/A * Searches the ldata_list to find if the the input port_wwn and lun exist.
2N/A *
2N/A * Input: port_wwn, ldata_list.
2N/A * Return value: FPCFGA_APID_NOACCESS if not found on ldata_list.
2N/A * FPCFGA_ACCESS_OK if found on ldata_list.
2N/A */
2N/Astatic fpcfga_ret_t
2N/Ais_FCP_dev_ap_on_ldata_list(const char *port_wwn, const int lun_num,
2N/A ldata_list_t *ldatap,
2N/A ldata_list_t **matchldpp)
2N/A{
2N/A ldata_list_t *curlp = NULL;
2N/A char *dyn = NULL, *dyncomp = NULL;
2N/A char *lun_dyn = NULL, *lunp = NULL;
2N/A int ldata_lun;
2N/A fpcfga_ret_t ret;
2N/A
2N/A /*
2N/A * if there is no list data just return the FCP dev list.
2N/A * Normally this should not occur since list data should
2N/A * be created through discoveredPort list.
2N/A */
2N/A ret = FPCFGA_APID_NOACCESS;
2N/A if (ldatap == NULL) {
2N/A return (ret);
2N/A }
2N/A
2N/A dyn = GET_DYN(ldatap->ldata.ap_phys_id);
2N/A if (dyn != NULL) dyncomp = DYN_TO_DYNCOMP(dyn);
2N/A if ((dyncomp != NULL) &&
2N/A (strncmp(dyncomp, port_wwn, WWN_SIZE*2) == 0)) {
2N/A lun_dyn = GET_LUN_DYN(dyncomp);
2N/A if (lun_dyn != NULL) {
2N/A lunp = LUN_DYN_TO_LUNCOMP(lun_dyn);
2N/A if ((ldata_lun = atoi(lunp)) == lun_num) {
2N/A *matchldpp = ldatap;
2N/A return (FPCFGA_ACCESS_OK);
2N/A } else if (ldata_lun > lun_num) {
2N/A return (ret);
2N/A }
2N/A /* else continue */
2N/A } else {
2N/A /* we have match without lun comp. */
2N/A *matchldpp = ldatap;
2N/A return (FPCFGA_ACCESS_OK);
2N/A }
2N/A }
2N/A
2N/A curlp = ldatap->next;
2N/A
2N/A dyn = dyncomp = NULL;
2N/A lun_dyn = lunp = NULL;
2N/A while (curlp != NULL) {
2N/A dyn = GET_DYN(curlp->ldata.ap_phys_id);
2N/A if (dyn != NULL) dyncomp = DYN_TO_DYNCOMP(dyn);
2N/A if ((dyncomp != NULL) &&
2N/A (strncmp(dyncomp, port_wwn, WWN_SIZE*2) == 0)) {
2N/A lun_dyn = GET_LUN_DYN(dyncomp);
2N/A if (lun_dyn != NULL) {
2N/A lunp = LUN_DYN_TO_LUNCOMP(lun_dyn);
2N/A if ((ldata_lun = atoi(lunp)) == lun_num) {
2N/A *matchldpp = curlp;
2N/A return (FPCFGA_ACCESS_OK);
2N/A } else if (ldata_lun > lun_num) {
2N/A return (ret);
2N/A }
2N/A /* else continue */
2N/A } else {
2N/A /* we have match without lun comp. */
2N/A *matchldpp = curlp;
2N/A return (FPCFGA_ACCESS_OK);
2N/A }
2N/A }
2N/A dyn = dyncomp = NULL;
2N/A lun_dyn = lunp = NULL;
2N/A curlp = curlp->next;
2N/A }
2N/A
2N/A return (ret);
2N/A
2N/A}
2N/A
2N/A/*
2N/A * This routine is called when a pathinfo without matching pwwn in dev_list
2N/A * is found.
2N/A */
2N/Astatic fpcfga_ret_t
2N/Ainit_ldata_for_mpath_dev(di_path_t path, char *pwwn, int *l_errnop,
2N/A fpcfga_list_t *lap)
2N/A{
2N/A ldata_list_t *listp = NULL;
2N/A cfga_list_data_t *clp = NULL;
2N/A size_t devlen;
2N/A char *devpath;
2N/A di_node_t client_node = DI_NODE_NIL;
2N/A uint_t dctl_state = 0;
2N/A cfga_busy_t busy;
2N/A char *client_path;
2N/A di_path_state_t pstate;
2N/A
2N/A /* get the client node path */
2N/A if (path == DI_PATH_NIL) {
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A client_node = di_path_client_node(path);
2N/A if (client_node == DI_NODE_NIL) {
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A if ((client_path = di_devfs_path(client_node)) == NULL) {
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A devlen = strlen(DEVICES_DIR) + strlen(client_path) + 1;
2N/A devpath = calloc(1, devlen);
2N/A if (devpath == NULL) {
2N/A di_devfs_path_free(client_path);
2N/A *l_errnop = errno;
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A (void) snprintf(devpath, devlen, "%s%s", DEVICES_DIR, client_path);
2N/A
2N/A /* now need to create ldata for this dev */
2N/A listp = calloc(1, sizeof (ldata_list_t));
2N/A if (listp == NULL) {
2N/A di_devfs_path_free(client_path);
2N/A S_FREE(devpath);
2N/A *l_errnop = errno;
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A
2N/A clp = &listp->ldata;
2N/A
2N/A /* Create logical and physical ap_id */
2N/A (void) snprintf(clp->ap_log_id, sizeof (clp->ap_log_id), "%s%s%s",
2N/A lap->xport_logp, DYN_SEP, pwwn);
2N/A (void) snprintf(clp->ap_phys_id, sizeof (clp->ap_phys_id), "%s%s%s",
2N/A lap->apidp->xport_phys, DYN_SEP, pwwn);
2N/A
2N/A /* Filled in by libcfgadm */
2N/A clp->ap_class[0] = '\0'; /* Filled by libcfgadm */
2N/A clp->ap_r_state = lap->xport_rstate;
2N/A /* set to ostate to configured. */
2N/A clp->ap_o_state = CFGA_STAT_CONFIGURED;
2N/A /*
2N/A * This routine is called when a port WWN is not found in dev list
2N/A * but path info node exists.
2N/A *
2N/A * Update the condition as failing if the pathinfo state was normal.
2N/A * Update the condition as unusable if the pathinfo state is failed
2N/A * or offline.
2N/A */
2N/A if (((pstate = di_path_state(path)) == DI_PATH_STATE_OFFLINE) ||
2N/A (pstate == DI_PATH_STATE_FAULT)) {
2N/A clp->ap_cond = CFGA_COND_UNUSABLE;
2N/A } else {
2N/A clp->ap_cond = CFGA_COND_FAILING;
2N/A }
2N/A clp->ap_status_time = (time_t)-1;
2N/A /* update ap_type and ap_info */
2N/A get_hw_info(client_node, clp);
2N/A
2N/A if (devctl_cmd(devpath, FPCFGA_DEV_GETSTATE,
2N/A &dctl_state, l_errnop) == FPCFGA_OK) {
2N/A busy = ((dctl_state & DEVICE_BUSY) == DEVICE_BUSY) ? 1 : 0;
2N/A } else {
2N/A busy = 0;
2N/A }
2N/A clp->ap_busy = busy;
2N/A /* Link it in */
2N/A listp->next = lap->listp;
2N/A lap->listp = listp;
2N/A
2N/A di_devfs_path_free(client_path);
2N/A S_FREE(devpath);
2N/A
2N/A /* now return with ok status with ldata. */
2N/A lap->ret = FPCFGA_OK;
2N/A return (FPCFGA_OK);
2N/A}
2N/A
2N/A/*
2N/A * Initialize the cfga_list_data struct for an accessible device
2N/A * from g_get_dev_list().
2N/A *
2N/A * Input: fca port ldata.
2N/A * Output: device cfga_list_data.
2N/A *
2N/A */
2N/Astatic fpcfga_ret_t
2N/Ainit_ldata_for_accessible_dev(const char *dyncomp, uchar_t inq_type,
2N/A fpcfga_list_t *lap)
2N/A{
2N/A ldata_list_t *listp = NULL;
2N/A cfga_list_data_t *clp = NULL;
2N/A int i;
2N/A
2N/A listp = calloc(1, sizeof (ldata_list_t));
2N/A if (listp == NULL) {
2N/A lap->l_errno = errno;
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A
2N/A clp = &listp->ldata;
2N/A
2N/A assert(dyncomp != NULL);
2N/A
2N/A /* Create logical and physical ap_id */
2N/A (void) snprintf(clp->ap_log_id, sizeof (clp->ap_log_id), "%s%s%s",
2N/A lap->xport_logp, DYN_SEP, dyncomp);
2N/A
2N/A (void) snprintf(clp->ap_phys_id, sizeof (clp->ap_phys_id), "%s%s%s",
2N/A lap->apidp->xport_phys, DYN_SEP, dyncomp);
2N/A
2N/A clp->ap_class[0] = '\0'; /* Filled in by libcfgadm */
2N/A clp->ap_r_state = lap->xport_rstate;
2N/A clp->ap_o_state = CFGA_STAT_UNCONFIGURED;
2N/A clp->ap_cond = CFGA_COND_UNKNOWN;
2N/A clp->ap_busy = 0;
2N/A clp->ap_status_time = (time_t)-1;
2N/A clp->ap_info[0] = '\0';
2N/A for (i = 0; i < N_DEVICE_TYPES; i++) {
2N/A if (inq_type == device_list[i].itype) {
2N/A (void) snprintf(clp->ap_type, sizeof (clp->ap_type),
2N/A "%s", (char *)device_list[i].name);
2N/A break;
2N/A }
2N/A }
2N/A if (i == N_DEVICE_TYPES) {
2N/A if (inq_type == ERR_INQ_DTYPE) {
2N/A clp->ap_cond = CFGA_COND_FAILED;
2N/A snprintf(clp->ap_type, sizeof (clp->ap_type), "%s",
2N/A (char *)GET_MSG_STR(ERR_UNAVAILABLE));
2N/A } else {
2N/A (void) snprintf(clp->ap_type, sizeof (clp->ap_type),
2N/A "%s", "unknown");
2N/A }
2N/A }
2N/A
2N/A /* Link it in */
2N/A (void) insert_ldata_to_ldatalist(dyncomp, NULL, listp, &(lap->listp));
2N/A
2N/A return (FPCFGA_OK);
2N/A}
2N/A
2N/A/*
2N/A * Initialize the cfga_list_data struct for an accessible FCP SCSI LUN device
2N/A * from the report lun data.
2N/A *
2N/A * Input: fca port ldata. report lun info
2N/A * Output: device cfga_list_data.
2N/A *
2N/A */
2N/Astatic fpcfga_ret_t
2N/Ainit_ldata_for_accessible_FCP_dev(
2N/A const char *port_wwn,
2N/A int num_luns,
2N/A struct report_lun_resp *resp_buf,
2N/A fpcfga_list_t *lap,
2N/A int *l_errnop)
2N/A{
2N/A ldata_list_t *listp = NULL, *listp_start = NULL, *listp_end = NULL,
2N/A *prevlp = NULL, *curlp = NULL, *matchp_start = NULL,
2N/A *matchp_end = NULL;
2N/A cfga_list_data_t *clp = NULL;
2N/A char *dyn = NULL, *dyncomp = NULL;
2N/A uchar_t *lun_string;
2N/A uint16_t lun_num;
2N/A int i, j, str_ret;
2N/A fpcfga_ret_t ret;
2N/A char dtype[CFGA_TYPE_LEN];
2N/A struct scsi_inquiry *inq_buf;
2N/A uchar_t peri_qual;
2N/A cfga_cond_t cond = CFGA_COND_UNKNOWN;
2N/A uchar_t lun_num_raw[SAM_LUN_SIZE];
2N/A
2N/A /* when number of lun is 0 it is not an error. so just return ok. */
2N/A if (num_luns == 0) {
2N/A return (FPCFGA_OK);
2N/A }
2N/A
2N/A for (i = 0; i < num_luns; i++) {
2N/A lun_string = (uchar_t *)&(resp_buf->lun_string[i]);
2N/A memcpy(lun_num_raw, lun_string, sizeof (lun_num_raw));
2N/A if ((ret = get_standard_inq_data(lap->apidp->xport_phys, port_wwn,
2N/A lun_num_raw, &inq_buf, l_errnop))
2N/A != FPCFGA_OK) {
2N/A if (ret == FPCFGA_FCP_TGT_SEND_SCSI_FAILED) {
2N/A (void) strlcpy(dtype,
2N/A (char *)GET_MSG_STR(ERR_UNAVAILABLE), CFGA_TYPE_LEN);
2N/A cond = CFGA_COND_FAILED;
2N/A } else {
2N/A S_FREE(inq_buf);
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A } else {
2N/A peri_qual = inq_buf->inq_dtype & FP_PERI_QUAL_MASK;
2N/A /*
2N/A * peripheral qualifier is not 0 so the device node should not
2N/A * included in the ldata list. There should not be a device
2N/A * node for the lun either.
2N/A */
2N/A if (peri_qual != DPQ_POSSIBLE) {
2N/A S_FREE(inq_buf);
2N/A continue;
2N/A }
2N/A *dtype = NULL;
2N/A for (j = 0; j < N_DEVICE_TYPES; j++) {
2N/A if ((inq_buf->inq_dtype & DTYPE_MASK)
2N/A == device_list[j].itype) {
2N/A (void) strlcpy(dtype, (char *)device_list[j].name,
2N/A CFGA_TYPE_LEN);
2N/A break;
2N/A }
2N/A }
2N/A if (*dtype == NULL) {
2N/A (void) strlcpy(dtype,
2N/A (char *)device_list[DTYPE_UNKNOWN_INDEX].name,
2N/A CFGA_TYPE_LEN);
2N/A }
2N/A }
2N/A /*
2N/A * Followed FCP driver for getting lun number from report
2N/A * lun data.
2N/A * According to SAM-2 there are multiple address method for
2N/A * FCP SCIS LUN. Logincal unit addressing, peripheral device
2N/A * addressing, flat space addressing, and extended logical
2N/A * unit addressing.
2N/A *
2N/A * as of 11/2001 FCP supports logical unit addressing and
2N/A * peripheral device addressing even thoough 3 defined.
2N/A * SSFCP_LUN_ADDRESSING 0x80
2N/A * SSFCP_PD_ADDRESSING 0x00
2N/A * SSFCP_VOLUME_ADDRESSING 0x40
2N/A *
2N/A * the menthod below is used by FCP when (lun_string[0] & 0xC0)
2N/A * is either SSFCP_LUN_ADDRESSING or SSFCP_PD_ADDRESSING mode.
2N/A */
2N/A lun_num = ((lun_string[0] & 0x3F) << 8) | lun_string[1];
2N/A listp = calloc(1, sizeof (ldata_list_t));
2N/A if (listp == NULL) {
2N/A *l_errnop = errno;
2N/A list_free(&listp_start);
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A
2N/A clp = &listp->ldata;
2N/A /* Create logical and physical ap_id */
2N/A (void) snprintf(clp->ap_log_id, sizeof (clp->ap_log_id),
2N/A "%s%s%s%s%d", lap->xport_logp, DYN_SEP, port_wwn,
2N/A LUN_COMP_SEP, lun_num);
2N/A (void) snprintf(clp->ap_phys_id, sizeof (clp->ap_phys_id),
2N/A "%s%s%s%s%d", lap->apidp->xport_phys, DYN_SEP, port_wwn,
2N/A LUN_COMP_SEP, lun_num);
2N/A (void) strncpy(clp->ap_type, dtype, strlen(dtype));
2N/A clp->ap_class[0] = '\0'; /* Filled in by libcfgadm */
2N/A clp->ap_r_state = lap->xport_rstate;
2N/A clp->ap_o_state = CFGA_STAT_UNCONFIGURED;
2N/A clp->ap_cond = cond;
2N/A clp->ap_busy = 0;
2N/A clp->ap_status_time = (time_t)-1;
2N/A clp->ap_info[0] = '\0';
2N/A if (listp_start == NULL) {
2N/A listp_start = listp;
2N/A } else {
2N/A if ((ret = insert_FCP_dev_ldata(
2N/A port_wwn, lun_num, listp,
2N/A &listp_start)) != FPCFGA_OK) {
2N/A list_free(&listp_start);
2N/A return (ret);
2N/A }
2N/A }
2N/A listp = NULL;
2N/A S_FREE(inq_buf);
2N/A }
2N/A
2N/A /*
2N/A * list data can be null when device peripheral qualifier is not 0
2N/A * for any luns. Return ok to continue.
2N/A */
2N/A if (listp_start == NULL) {
2N/A return (FPCFGA_OK);
2N/A }
2N/A /*
2N/A * get the end of list for later uses.
2N/A */
2N/A curlp = listp_start->next;
2N/A prevlp = listp_start;
2N/A while (curlp) {
2N/A prevlp = curlp;
2N/A curlp = curlp->next;
2N/A }
2N/A listp_end = prevlp;
2N/A
2N/A /*
2N/A * if there is no list data just return the FCP dev list.
2N/A * Normally this should not occur since list data should
2N/A * be created through g_get_dev_list().
2N/A */
2N/A if (lap->listp == NULL) {
2N/A lap->listp = listp_start;
2N/A for (listp = listp_start; listp != NULL; listp = listp->next) {
2N/A listp->ldata.ap_cond = CFGA_COND_FAILING;
2N/A }
2N/A return (FPCFGA_OK);
2N/A }
2N/A
2N/A dyn = GET_DYN(lap->listp->ldata.ap_phys_id);
2N/A if ((dyn != NULL) && ((dyncomp = DYN_TO_DYNCOMP(dyn)) != NULL)) {
2N/A if ((str_ret = strncmp(dyncomp, port_wwn, WWN_SIZE*2)) == 0) {
2N/A matchp_start = matchp_end = lap->listp;
2N/A while (matchp_end->next != NULL) {
2N/A dyn = GET_DYN(
2N/A matchp_end->next->ldata.ap_phys_id);
2N/A if ((dyn != NULL) &&
2N/A ((dyncomp = DYN_TO_DYNCOMP(dyn)) != NULL)) {
2N/A if ((str_ret = strncmp(dyncomp,
2N/A port_wwn, WWN_SIZE*2)) == 0) {
2N/A matchp_end = matchp_end->next;
2N/A } else {
2N/A break;
2N/A }
2N/A } else {
2N/A break;
2N/A }
2N/A }
2N/A /* fillup inqdtype */
2N/A for (listp = listp_start; listp != NULL;
2N/A listp = listp->next) {
2N/A listp->ldata.ap_cond =
2N/A lap->listp->ldata.ap_cond;
2N/A }
2N/A /* link the new elem of lap->listp. */
2N/A listp_end->next = matchp_end->next;
2N/A /* free the one matching wwn. */
2N/A matchp_end->next = NULL;
2N/A list_free(&matchp_start);
2N/A /* link lap->listp to listp_start. */
2N/A lap->listp = listp_start;
2N/A return (FPCFGA_OK);
2N/A } else if (str_ret > 0) {
2N/A for (listp = listp_start; listp != NULL;
2N/A listp = listp->next) {
2N/A listp->ldata.ap_cond = CFGA_COND_FAILING;
2N/A }
2N/A listp_end->next = lap->listp->next;
2N/A lap->listp = listp_start;
2N/A return (FPCFGA_OK);
2N/A }
2N/A }
2N/A
2N/A prevlp = lap->listp;
2N/A curlp = lap->listp->next;
2N/A
2N/A dyn = dyncomp = NULL;
2N/A while (curlp != NULL) {
2N/A dyn = GET_DYN(curlp->ldata.ap_phys_id);
2N/A if ((dyn != NULL) &&
2N/A ((dyncomp = DYN_TO_DYNCOMP(dyn)) != NULL)) {
2N/A if ((str_ret = strncmp(dyncomp, port_wwn,
2N/A WWN_SIZE*2)) == 0) {
2N/A matchp_start = matchp_end = curlp;
2N/A while (matchp_end->next != NULL) {
2N/A dyn = GET_DYN(
2N/A matchp_end->next->ldata.ap_phys_id);
2N/A if ((dyn != NULL) &&
2N/A ((dyncomp = DYN_TO_DYNCOMP(dyn))
2N/A != NULL)) {
2N/A if ((str_ret = strncmp(dyncomp,
2N/A port_wwn, WWN_SIZE*2))
2N/A == 0) {
2N/A matchp_end =
2N/A matchp_end->next;
2N/A } else {
2N/A break;
2N/A }
2N/A } else {
2N/A break;
2N/A }
2N/A }
2N/A for (listp = listp_start; listp != NULL;
2N/A listp = listp->next) {
2N/A listp->ldata.ap_cond = curlp->ldata.ap_cond;
2N/A }
2N/A /* link the next elem to listp_end. */
2N/A listp_end->next = matchp_end->next;
2N/A /* link prevlp to listp_start to drop curlp. */
2N/A prevlp->next = listp_start;
2N/A /* free matching pwwn elem. */
2N/A matchp_end->next = NULL;
2N/A list_free(&matchp_start);
2N/A return (FPCFGA_OK);
2N/A } else if (str_ret > 0) {
2N/A for (listp = listp_start; listp != NULL;
2N/A listp = listp->next) {
2N/A /*
2N/A * Dev not found from accessible
2N/A * fc dev list but the node should
2N/A * exist. Set to failing cond now
2N/A * and check the node state later.
2N/A */
2N/A listp->ldata.ap_cond = CFGA_COND_FAILING;
2N/A }
2N/A /* keep the cur elem by linking to list_end. */
2N/A listp_end->next = curlp;
2N/A prevlp->next = listp_start;
2N/A return (FPCFGA_OK);
2N/A }
2N/A }
2N/A dyn = dyncomp = NULL;
2N/A prevlp = curlp;
2N/A curlp = curlp->next;
2N/A }
2N/A
2N/A prevlp->next = listp_start;
2N/A for (listp = listp_start; listp != NULL; listp = listp->next) {
2N/A listp->ldata.ap_cond = CFGA_COND_FAILING;
2N/A }
2N/A
2N/A return (FPCFGA_OK);
2N/A
2N/A}
2N/A
2N/A/* fill in device type, vid, pid from properties */
2N/Astatic void
2N/Aget_hw_info(di_node_t node, cfga_list_data_t *clp)
2N/A{
2N/A char *cp = NULL;
2N/A char *inq_vid, *inq_pid;
2N/A int i;
2N/A
2N/A /*
2N/A * if the type is not previously assigned with valid SCSI device type
2N/A * check devinfo to find the type.
2N/A * once device is configured it should have a valid device type.
2N/A * device node is configured but no valid device type is found
2N/A * the type will be set to unavailable.
2N/A */
2N/A if (clp->ap_type != NULL) {
2N/A /*
2N/A * if the type is not one of defined SCSI device type
2N/A * check devinfo to find the type.
2N/A *
2N/A * Note: unknown type is not a valid device type.
2N/A * It is added in to the device list table to provide
2N/A * constant string of "unknown".
2N/A */
2N/A for (i = 0; i < (N_DEVICE_TYPES -1); i++) {
2N/A if (strncmp((char *)clp->ap_type, (char *)device_list[i].name,
2N/A sizeof (clp->ap_type)) == 0) {
2N/A break;
2N/A }
2N/A }
2N/A if (i == (N_DEVICE_TYPES - 1)) {
2N/A cp = (char *)get_device_type(node);
2N/A if (cp == NULL) {
2N/A cp = (char *)GET_MSG_STR(ERR_UNAVAILABLE);
2N/A }
2N/A (void) snprintf(clp->ap_type, sizeof (clp->ap_type), "%s",
2N/A S_STR(cp));
2N/A }
2N/A } else {
2N/A cp = (char *)get_device_type(node);
2N/A if (cp == NULL) {
2N/A cp = (char *)GET_MSG_STR(ERR_UNAVAILABLE);
2N/A }
2N/A (void) snprintf(clp->ap_type, sizeof (clp->ap_type), "%s",
2N/A S_STR(cp));
2N/A }
2N/A
2N/A /*
2N/A * Fill in vendor and product ID.
2N/A */
2N/A if ((di_prop_lookup_strings(DDI_DEV_T_ANY, node,
2N/A "inquiry-product-id", &inq_pid) == 1) &&
2N/A (di_prop_lookup_strings(DDI_DEV_T_ANY, node,
2N/A "inquiry-vendor-id", &inq_vid) == 1)) {
2N/A (void) snprintf(clp->ap_info, sizeof (clp->ap_info),
2N/A "%s %s", inq_vid, inq_pid);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Get dtype from "inquiry-device-type" property. If not present,
2N/A * derive it from minor node type
2N/A */
2N/Astatic const char *
2N/Aget_device_type(di_node_t node)
2N/A{
2N/A char *name = NULL;
2N/A int *inq_dtype;
2N/A int i;
2N/A
2N/A if (node == DI_NODE_NIL) {
2N/A return (NULL);
2N/A }
2N/A
2N/A /* first, derive type based on inquiry property */
2N/A if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "inquiry-device-type",
2N/A &inq_dtype) != -1) {
2N/A int itype = (*inq_dtype) & DTYPE_MASK;
2N/A
2N/A for (i = 0; i < N_DEVICE_TYPES; i++) {
2N/A if (itype == device_list[i].itype) {
2N/A name = (char *)device_list[i].name;
2N/A break;
2N/A }
2N/A }
2N/A /*
2N/A * when found to be unknown type, set name to null to check
2N/A * device minor node type.
2N/A */
2N/A if (i == (N_DEVICE_TYPES - 1)) {
2N/A name = NULL;
2N/A }
2N/A }
2N/A
2N/A /* if property fails, use minor nodetype */
2N/A if (name == NULL) {
2N/A char *nodetype;
2N/A di_minor_t minor = di_minor_next(node, DI_MINOR_NIL);
2N/A
2N/A if ((minor != DI_MINOR_NIL) &&
2N/A ((nodetype = di_minor_nodetype(minor)) != NULL)) {
2N/A for (i = 0; i < N_DEVICE_TYPES; i++) {
2N/A if (device_list[i].ntype &&
2N/A (strcmp(nodetype, device_list[i].ntype)
2N/A == 0)) {
2N/A name = (char *)device_list[i].name;
2N/A break;
2N/A }
2N/A }
2N/A }
2N/A }
2N/A
2N/A return (name);
2N/A}
2N/A
2N/A/* Transform list data to stat data */
2N/Afpcfga_ret_t
2N/Alist_ext_postprocess(
2N/A ldata_list_t **llpp,
2N/A int nelem,
2N/A cfga_list_data_t **ap_id_list,
2N/A int *nlistp,
2N/A char **errstring)
2N/A{
2N/A cfga_list_data_t *ldatap = NULL;
2N/A ldata_list_t *tmplp = NULL;
2N/A int i = -1;
2N/A
2N/A *ap_id_list = NULL;
2N/A *nlistp = 0;
2N/A
2N/A if (*llpp == NULL || nelem < 0) {
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A
2N/A if (nelem == 0) {
2N/A return (FPCFGA_APID_NOEXIST);
2N/A }
2N/A
2N/A ldatap = calloc(nelem, sizeof (cfga_list_data_t));
2N/A if (ldatap == NULL) {
2N/A cfga_err(errstring, errno, ERR_LIST, 0);
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A
2N/A /* Extract the list_data structures from the linked list */
2N/A tmplp = *llpp;
2N/A for (i = 0; i < nelem && tmplp != NULL; i++) {
2N/A ldatap[i] = tmplp->ldata;
2N/A tmplp = tmplp->next;
2N/A }
2N/A
2N/A if (i < nelem || tmplp != NULL) {
2N/A S_FREE(ldatap);
2N/A return (FPCFGA_LIB_ERR);
2N/A }
2N/A
2N/A *nlistp = nelem;
2N/A *ap_id_list = ldatap;
2N/A
2N/A return (FPCFGA_OK);
2N/A}
2N/A
2N/A/*
2N/A * Convert bus state to receptacle state
2N/A */
2N/Astatic cfga_stat_t
2N/Axport_devinfo_to_recep_state(uint_t xport_di_state)
2N/A{
2N/A cfga_stat_t rs;
2N/A
2N/A switch (xport_di_state) {
2N/A case DI_BUS_QUIESCED:
2N/A case DI_BUS_DOWN:
2N/A rs = CFGA_STAT_DISCONNECTED;
2N/A break;
2N/A /*
2N/A * NOTE: An explicit flag for active should probably be added to
2N/A * libdevinfo.
2N/A */
2N/A default:
2N/A rs = CFGA_STAT_CONNECTED;
2N/A break;
2N/A }
2N/A
2N/A return (rs);
2N/A}
2N/A
2N/A/*
2N/A * Convert device state to occupant state
2N/A * if driver is attached the node is configured.
2N/A * if offline or down the node is unconfigured.
2N/A * if only driver detached it is none state which is treated the same
2N/A * way as configured state.
2N/A */
2N/Astatic cfga_stat_t
2N/Adev_devinfo_to_occupant_state(uint_t dev_di_state)
2N/A{
2N/A /* Driver attached ? */
2N/A if ((dev_di_state & DI_DRIVER_DETACHED) != DI_DRIVER_DETACHED) {
2N/A return (CFGA_STAT_CONFIGURED);
2N/A }
2N/A
2N/A if ((dev_di_state & DI_DEVICE_OFFLINE) == DI_DEVICE_OFFLINE ||
2N/A (dev_di_state & DI_DEVICE_DOWN) == DI_DEVICE_DOWN) {
2N/A return (CFGA_STAT_UNCONFIGURED);
2N/A } else {
2N/A return (CFGA_STAT_NONE);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Wrapper routine for inserting ldata to make an sorted ldata list.
2N/A *
2N/A * When show_FCP_dev option is given insert_FCP_dev_ldata() is called.
2N/A * Otherwise insert_fc_dev_ldata() is called.
2N/A */
2N/Astatic fpcfga_ret_t
2N/Ainsert_ldata_to_ldatalist(
2N/A const char *port_wwn,
2N/A int *lun_nump,
2N/A ldata_list_t *listp,
2N/A ldata_list_t **ldatapp)
2N/A{
2N/A
2N/A if (lun_nump == NULL) {
2N/A return (insert_fc_dev_ldata(port_wwn, listp, ldatapp));
2N/A } else {
2N/A return
2N/A (insert_FCP_dev_ldata(port_wwn, *lun_nump, listp, ldatapp));
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Insert an input ldata to ldata list to make sorted ldata list.
2N/A */
2N/Astatic fpcfga_ret_t
2N/Ainsert_fc_dev_ldata(
2N/A const char *port_wwn,
2N/A ldata_list_t *listp,
2N/A ldata_list_t **ldatapp)
2N/A{
2N/A ldata_list_t *prevlp = NULL, *curlp = NULL;
2N/A char *dyn = NULL, *dyncomp = NULL;
2N/A
2N/A if (*ldatapp == NULL) {
2N/A *ldatapp = listp;
2N/A return (FPCFGA_OK);
2N/A }
2N/A
2N/A dyn = GET_DYN((*ldatapp)->ldata.ap_phys_id);
2N/A if (dyn != NULL) dyncomp = DYN_TO_DYNCOMP(dyn);
2N/A if ((dyncomp != NULL) &&
2N/A (strncmp(dyncomp, port_wwn, WWN_SIZE*2) >= 0)) {
2N/A listp->next = *ldatapp;
2N/A *ldatapp = listp;
2N/A return (FPCFGA_OK);
2N/A }
2N/A /* else continue */
2N/A
2N/A prevlp = *ldatapp;
2N/A curlp = (*ldatapp)->next;
2N/A
2N/A dyn = dyncomp = NULL;
2N/A while (curlp != NULL) {
2N/A dyn = GET_DYN(curlp->ldata.ap_phys_id);
2N/A if (dyn != NULL) dyncomp = DYN_TO_DYNCOMP(dyn);
2N/A if ((dyncomp != NULL) &&
2N/A (strncmp(dyncomp, port_wwn, WWN_SIZE*2) >= 0)) {
2N/A listp->next = prevlp->next;
2N/A prevlp->next = listp;
2N/A return (FPCFGA_OK);
2N/A }
2N/A dyn = dyncomp = NULL;
2N/A prevlp = curlp;
2N/A curlp = curlp->next;
2N/A }
2N/A
2N/A /* add the ldata to the end of the list. */
2N/A prevlp->next = listp;
2N/A return (FPCFGA_OK);
2N/A}
2N/A
2N/A/*
2N/A * Insert an input ldata to ldata list to make sorted ldata list.
2N/A */
2N/Astatic fpcfga_ret_t
2N/Ainsert_FCP_dev_ldata(
2N/A const char *port_wwn,
2N/A int lun_num,
2N/A ldata_list_t *listp,
2N/A ldata_list_t **ldatapp)
2N/A{
2N/A ldata_list_t *prevlp = NULL, *curlp = NULL;
2N/A char *dyn = NULL, *dyncomp = NULL;
2N/A char *lun_dyn = NULL, *lunp = NULL;
2N/A
2N/A if (*ldatapp == NULL) {
2N/A *ldatapp = listp;
2N/A return (FPCFGA_OK);
2N/A }
2N/A
2N/A dyn = GET_DYN((*ldatapp)->ldata.ap_phys_id);
2N/A if (dyn != NULL) dyncomp = DYN_TO_DYNCOMP(dyn);
2N/A if ((dyncomp != NULL) &&
2N/A (strncmp(dyncomp, port_wwn, WWN_SIZE*2) == 0)) {
2N/A lun_dyn = GET_LUN_DYN(dyncomp);
2N/A if (lun_dyn != NULL) {
2N/A lunp = LUN_DYN_TO_LUNCOMP(lun_dyn);
2N/A if ((atoi(lunp)) >= lun_num) {
2N/A listp->next = *ldatapp;
2N/A *ldatapp = listp;
2N/A return (FPCFGA_OK);
2N/A }
2N/A }
2N/A } else if ((dyncomp != NULL) &&
2N/A (strncmp(dyncomp, port_wwn, WWN_SIZE*2) > 0)) {
2N/A listp->next = *ldatapp;
2N/A *ldatapp = listp;
2N/A return (FPCFGA_OK);
2N/A }
2N/A
2N/A prevlp = *ldatapp;
2N/A curlp = (*ldatapp)->next;
2N/A
2N/A dyn = dyncomp = NULL;
2N/A lun_dyn = lunp = NULL;
2N/A while (curlp != NULL) {
2N/A dyn = GET_DYN(curlp->ldata.ap_phys_id);
2N/A if (dyn != NULL) dyncomp = DYN_TO_DYNCOMP(dyn);
2N/A
2N/A if ((dyncomp != NULL) &&
2N/A (strncmp(dyncomp, port_wwn, WWN_SIZE*2) == 0)) {
2N/A lun_dyn = GET_LUN_DYN(dyncomp);
2N/A if (lun_dyn != NULL) {
2N/A lunp = LUN_DYN_TO_LUNCOMP(lun_dyn);
2N/A if ((atoi(lunp)) >= lun_num) {
2N/A listp->next = prevlp->next;
2N/A prevlp->next = listp;
2N/A return (FPCFGA_OK);
2N/A }
2N/A }
2N/A /* else continue */
2N/A } else if ((dyncomp != NULL) &&
2N/A (strncmp(dyncomp, port_wwn, WWN_SIZE*2) > 0)) {
2N/A listp->next = prevlp->next;
2N/A prevlp->next = listp;
2N/A return (FPCFGA_OK);
2N/A }
2N/A /* else continue */
2N/A
2N/A dyn = dyncomp = NULL;
2N/A lun_dyn = lunp = NULL;
2N/A prevlp = curlp;
2N/A curlp = curlp->next;
2N/A }
2N/A
2N/A /* add the ldata to the end of the list. */
2N/A prevlp->next = listp;
2N/A return (FPCFGA_OK);
2N/A}
2N/A
2N/A/*
2N/A * This function will return the dtype for the given device
2N/A * It will first issue a report lun to lun 0 and then it will issue a SCSI
2N/A * Inquiry to the first lun returned by report luns.
2N/A *
2N/A * If everything is successful, the dtype will be returned with the peri
2N/A * qualifier masked out.
2N/A *
2N/A * If either the report lun or the scsi inquiry fails, we will first check
2N/A * the return status. If the return status is SCSI_DEVICE_NOT_TGT, then
2N/A * we will assume this is a remote HBA and return an UNKNOWN DTYPE
2N/A * for all other failures, we will return a dtype of ERR_INQ_DTYPE
2N/A */
2N/Astatic uchar_t
2N/Aget_inq_dtype(char *xport_phys, char *dyncomp, HBA_HANDLE handle,
2N/A HBA_PORTATTRIBUTES *portAttrs, HBA_PORTATTRIBUTES *discPortAttrs) {
2N/A HBA_STATUS status;
2N/A report_lun_resp_t *resp_buf;
2N/A int num_luns = 0, ret, l_errno;
2N/A uchar_t *lun_string;
2N/A uint64_t lun = 0;
2N/A struct scsi_inquiry inq;
2N/A struct scsi_extended_sense sense;
2N/A HBA_UINT8 scsiStatus;
2N/A uint32_t inquirySize = sizeof (inq);
2N/A uint32_t senseSize = sizeof (sense);
2N/A
2N/A memset(&inq, 0, sizeof (inq));
2N/A memset(&sense, 0, sizeof (sense));
2N/A if ((ret = get_report_lun_data(xport_phys, dyncomp,
2N/A &num_luns, &resp_buf, &sense, &l_errno))
2N/A != FPCFGA_OK) {
2N/A /*
2N/A * Checking the sense key data as well as the additional
2N/A * sense key. The SES Node is not required to repond
2N/A * to Report LUN. In the case of Minnow, the SES node
2N/A * returns with KEY_ILLEGAL_REQUEST and the additional
2N/A * sense key of 0x20. In this case we will blindly
2N/A * send the SCSI Inquiry call to lun 0
2N/A *
2N/A * if we get any other error we will set the inq_type
2N/A * appropriately
2N/A */
2N/A if ((sense.es_key == KEY_ILLEGAL_REQUEST) &&
2N/A (sense.es_add_code == 0x20)) {
2N/A lun = 0;
2N/A } else {
2N/A if (ret == FPCFGA_FCP_SEND_SCSI_DEV_NOT_TGT) {
2N/A inq.inq_dtype = DTYPE_UNKNOWN;
2N/A } else {
2N/A inq.inq_dtype = ERR_INQ_DTYPE;
2N/A }
2N/A return (inq.inq_dtype);
2N/A }
2N/A } else {
2N/A /* send the inquiry to the first lun */
2N/A lun_string = (uchar_t *)&(resp_buf->lun_string[0]);
2N/A memcpy(&lun, lun_string, sizeof (lun));
2N/A S_FREE(resp_buf);
2N/A }
2N/A
2N/A memset(&sense, 0, sizeof (sense));
2N/A status = HBA_ScsiInquiryV2(handle,
2N/A portAttrs->PortWWN, discPortAttrs->PortWWN, lun, 0, 0,
2N/A &inq, &inquirySize, &scsiStatus, &sense, &senseSize);
2N/A if (status == HBA_STATUS_OK) {
2N/A inq.inq_dtype = inq.inq_dtype & DTYPE_MASK;
2N/A } else if (status == HBA_STATUS_ERROR_NOT_A_TARGET) {
2N/A inq.inq_dtype = DTYPE_UNKNOWN;
2N/A } else {
2N/A inq.inq_dtype = ERR_INQ_DTYPE;
2N/A }
2N/A return (inq.inq_dtype);
2N/A}