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