2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A
2N/A/*LINTLIBRARY*/
2N/A
2N/A/*
2N/A * I18N message number ranges
2N/A * This file: 12000 - 12499
2N/A * Shared common messages: 1 - 1999
2N/A */
2N/A
2N/A/*
2N/A * This module is part of the Fibre Channel Interface library.
2N/A */
2N/A
2N/A/* #define _POSIX_SOURCE 1 */
2N/A
2N/A
2N/A/* Includes */
2N/A#include <stdlib.h>
2N/A#include <stdio.h>
2N/A#include <sys/file.h>
2N/A#include <sys/types.h>
2N/A#include <sys/stat.h>
2N/A#include <sys/mkdev.h>
2N/A#include <sys/param.h>
2N/A#include <fcntl.h>
2N/A#include <unistd.h>
2N/A#include <string.h>
2N/A#include <sys/scsi/scsi.h>
2N/A#include <dirent.h> /* for DIR */
2N/A#include <sys/vtoc.h>
2N/A#include <nl_types.h>
2N/A#include <strings.h>
2N/A#include <errno.h>
2N/A#include <sys/ddi.h> /* for max */
2N/A#include <fnmatch.h>
2N/A#include <l_common.h>
2N/A#include <stgcom.h>
2N/A#include <l_error.h>
2N/A#include <g_state.h>
2N/A#include <g_scsi.h>
2N/A#include <sys/fibre-channel/ulp/fcp_util.h>
2N/A#include <sys/fibre-channel/impl/fc_error.h>
2N/A#include <sys/fibre-channel/impl/fcph.h>
2N/A#include <sys/socalio.h>
2N/A#include <libdevinfo.h>
2N/A#include <ctype.h>
2N/A#include <devid.h>
2N/A
2N/A/* Some forward declarations of static functions */
2N/A/*
2N/A * becomes extern interface for Tapestry.
2N/A * static int g_get_inq_dtype(char *, la_wwn_t, uchar_t *);
2N/A * static int g_get_dev_list(char *, fc_port_dev_t **, int *, int);
2N/A */
2N/Astatic int g_issue_fcp_ioctl(int, struct fcp_ioctl *, int);
2N/Astatic int g_set_port_state(char *, int);
2N/Astatic int g_dev_log_in_out(char *, la_wwn_t, uint16_t);
2N/Astatic int g_get_dev_port_state(char *, la_wwn_t, uint32_t *);
2N/Astatic void g_free_rls(AL_rls *);
2N/Astatic int g_scsi_inquiry_cmd80(int, uchar_t *, int);
2N/Astatic int get_fca_inq_dtype(char *, la_wwn_t, uchar_t *);
2N/Astatic int g_find_supported_inq_page(int, int);
2N/Astatic int wwn_list_name_compare(const void *, const void *);
2N/Astatic int devid_get_all(ddi_devid_t, di_node_t, char *,
2N/A struct mplist_struct **);
2N/Astatic int get_multipath(char *, struct dlist **,
2N/A struct wwn_list_struct *);
2N/Astatic int get_multipath_disk(char *, struct dlist **,
2N/A struct wwn_list_struct *);
2N/Astatic void mplist_free(struct mplist_struct *);
2N/Astatic int get_wwn_data(di_node_t, uchar_t **, uchar_t **);
2N/Astatic int get_dev_path(struct wwn_list_struct **, char *, char *);
2N/Astatic int insert_missing_pwwn(char *, struct wwn_list_struct **);
2N/Astatic int get_scsi_vhci_port_wwn(char *, uchar_t *);
2N/Astatic int search_wwn_entry(struct wwn_list_found_struct *, uchar_t *,
2N/A uchar_t *);
2N/Astatic int add_wwn_entry(struct wwn_list_found_struct **, uchar_t *,
2N/A uchar_t *);
2N/Astatic int string_to_wwn(uchar_t *, uchar_t *);
2N/Astatic int get_wwns(char *, uchar_t *, uchar_t *, int *,
2N/A struct wwn_list_found_struct **);
2N/A
2N/A/* type for g_dev_map_init related routines */
2N/A
2N/A#define S_FREE(x) (((x) != NULL) ? (free(x), (x) = NULL) : (void *)0)
2N/A
2N/Atypedef struct impl_map_dev_prop {
2N/A char prop_name[MAXNAMELEN];
2N/A int prop_type;
2N/A int prop_size;
2N/A void *prop_data;
2N/A int prop_error;
2N/A struct impl_map_dev_prop *next;
2N/A} impl_map_dev_prop_t;
2N/A
2N/Atypedef struct impl_map_dev {
2N/A int flag;
2N/A uint_t topo;
2N/A impl_map_dev_prop_t *prop_list;
2N/A struct impl_map_dev *parent;
2N/A struct impl_map_dev *child;
2N/A struct impl_map_dev *next;
2N/A} impl_map_dev_t;
2N/A
2N/A/* Defines */
2N/A#define VERBPRINT if (verbose) (void) printf
2N/A
2N/A#define DIR_MATCH_ST "*[0-9+]n"
2N/A#define DIR_MATCH_SSD "*s2"
2N/A
2N/A#define PROP_NOEXIST 0
2N/A#define PROP_EXIST 1
2N/A
2N/A/* Prototypes */
2N/Astatic int create_map(char *, gfc_map_t *, int, int);
2N/Astatic char ctoi(char);
2N/Astatic int lilp_map_cmp(const void*, const void*);
2N/Astatic int devices_get_all(di_node_t, char *, char *,
2N/A struct wwn_list_struct **);
2N/Astatic char *my_devfs_path(di_node_t);
2N/Astatic void my_devfs_path_free(char *path);
2N/Astatic void copy_wwn_data_to_str(char *, const uchar_t *);
2N/Astatic void init_drv(char *, char *, char *);
2N/A
2N/A/* static for g_dev_map_init related routines */
2N/A
2N/Astatic int update_map_dev_fc_prop(impl_map_dev_prop_t **, uint32_t,
2N/A uchar_t *, uchar_t *, int, int);
2N/Astatic int update_map_dev_FCP_prop(impl_map_dev_prop_t **, uchar_t *, int, int);
2N/Astatic int handle_map_dev_FCP_prop(minor_t, la_wwn_t, impl_map_dev_prop_t **);
2N/Astatic void free_prop_list(impl_map_dev_prop_t **);
2N/Astatic void free_child_list(impl_map_dev_t **);
2N/Astatic u_longlong_t wwnConversion(uchar_t *wwn);
2N/A
2N/Auchar_t g_switch_to_alpa[] = {
2N/A 0xef, 0xe8, 0xe4, 0xe2, 0xe1, 0xe0, 0xdc, 0xda, 0xd9, 0xd6,
2N/A 0xd5, 0xd4, 0xd3, 0xd2, 0xd1, 0xce, 0xcd, 0xcc, 0xcb, 0xca,
2N/A 0xc9, 0xc7, 0xc6, 0xc5, 0xc3, 0xbc, 0xba, 0xb9, 0xb6, 0xb5,
2N/A 0xb4, 0xb3, 0xb2, 0xb1, 0xae, 0xad, 0xac, 0xab, 0xaa, 0xa9,
2N/A 0xa7, 0xa6, 0xa5, 0xa3, 0x9f, 0x9e, 0x9d, 0x9b, 0x98, 0x97,
2N/A 0x90, 0x8f, 0x88, 0x84, 0x82, 0x81, 0x80, 0x7c, 0x7a, 0x79,
2N/A 0x76, 0x75, 0x74, 0x73, 0x72, 0x71, 0x6e, 0x6d, 0x6c, 0x6b,
2N/A 0x6a, 0x69, 0x67, 0x66, 0x65, 0x63, 0x5c, 0x5a, 0x59, 0x56,
2N/A 0x55, 0x54, 0x53, 0x52, 0x51, 0x4e, 0x4d, 0x4c, 0x4b, 0x4a,
2N/A 0x49, 0x47, 0x46, 0x45, 0x43, 0x3c, 0x3a, 0x39, 0x36, 0x35,
2N/A 0x34, 0x33, 0x32, 0x31, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29,
2N/A 0x27, 0x26, 0x25, 0x23, 0x1f, 0x1e, 0x1d, 0x1b, 0x18, 0x17,
2N/A 0x10, 0x0f, 0x08, 0x04, 0x02, 0x01
2N/A};
2N/A
2N/Auchar_t g_sf_alpa_to_switch[] = {
2N/A 0x00, 0x7d, 0x7c, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x7a, 0x00,
2N/A 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x78, 0x00, 0x00, 0x00,
2N/A 0x00, 0x00, 0x00, 0x77, 0x76, 0x00, 0x00, 0x75, 0x00, 0x74,
2N/A 0x73, 0x72, 0x00, 0x00, 0x00, 0x71, 0x00, 0x70, 0x6f, 0x6e,
2N/A 0x00, 0x6d, 0x6c, 0x6b, 0x6a, 0x69, 0x68, 0x00, 0x00, 0x67,
2N/A 0x66, 0x65, 0x64, 0x63, 0x62, 0x00, 0x00, 0x61, 0x60, 0x00,
2N/A 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5d,
2N/A 0x5c, 0x5b, 0x00, 0x5a, 0x59, 0x58, 0x57, 0x56, 0x55, 0x00,
2N/A 0x00, 0x54, 0x53, 0x52, 0x51, 0x50, 0x4f, 0x00, 0x00, 0x4e,
2N/A 0x4d, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4b,
2N/A 0x00, 0x4a, 0x49, 0x48, 0x00, 0x47, 0x46, 0x45, 0x44, 0x43,
2N/A 0x42, 0x00, 0x00, 0x41, 0x40, 0x3f, 0x3e, 0x3d, 0x3c, 0x00,
2N/A 0x00, 0x3b, 0x3a, 0x00, 0x39, 0x00, 0x00, 0x00, 0x38, 0x37,
2N/A 0x36, 0x00, 0x35, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
2N/A 0x00, 0x00, 0x00, 0x33, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00,
2N/A 0x00, 0x31, 0x30, 0x00, 0x00, 0x2f, 0x00, 0x2e, 0x2d, 0x2c,
2N/A 0x00, 0x00, 0x00, 0x2b, 0x00, 0x2a, 0x29, 0x28, 0x00, 0x27,
2N/A 0x26, 0x25, 0x24, 0x23, 0x22, 0x00, 0x00, 0x21, 0x20, 0x1f,
2N/A 0x1e, 0x1d, 0x1c, 0x00, 0x00, 0x1b, 0x1a, 0x00, 0x19, 0x00,
2N/A 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x17, 0x16, 0x15,
2N/A 0x00, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x00, 0x00, 0x0e,
2N/A 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x00, 0x00, 0x08, 0x07, 0x00,
2N/A 0x06, 0x00, 0x00, 0x00, 0x05, 0x04, 0x03, 0x00, 0x02, 0x00,
2N/A 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
2N/A};
2N/A
2N/A
2N/A
2N/A/*
2N/A * Check if device is in the map.
2N/A *
2N/A * PARAMS:
2N/A * map - loop map returned from fc port
2N/A * tid - device ID for private map or 24-bit alpa for fabric map
2N/A *
2N/A * RETURNS:
2N/A * 1 if device present in the map.
2N/A * 0 otherwise.
2N/A *
2N/A */
2N/Aint
2N/Ag_device_in_map(gfc_map_t *map, int tid)
2N/A{
2N/A int i, j;
2N/A gfc_port_dev_info_t *dev_ptr;
2N/A
2N/A dev_ptr = map->dev_addr;
2N/A if ((map->hba_addr.port_topology == FC_TOP_PUBLIC_LOOP) ||
2N/A (map->hba_addr.port_topology == FC_TOP_FABRIC)) {
2N/A for (i = 0; i < map->count; i++, dev_ptr++) {
2N/A if (dev_ptr->
2N/A gfc_port_dev.pub_port.dev_did.port_id == tid) {
2N/A /* Does not count if WWN == 0 */
2N/A for (j = 0; j < FC_WWN_SIZE; j++)
2N/A if (dev_ptr->gfc_port_dev.pub_port.
2N/A dev_pwwn.raw_wwn[j] != 0)
2N/A return (1);
2N/A }
2N/A }
2N/A } else {
2N/A for (i = 0; i < map->count; i++, dev_ptr++) {
2N/A if (dev_ptr->gfc_port_dev.priv_port.sf_al_pa ==
2N/A (int)g_switch_to_alpa[tid]) {
2N/A /* Does not count if WWN == 0 */
2N/A for (j = 0; j < WWN_SIZE; j++)
2N/A if (dev_ptr->gfc_port_dev.priv_port.
2N/A sf_port_wwn[j] != 0)
2N/A return (1);
2N/A }
2N/A }
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Inserts any missing port wwns for mpxio device paths
2N/A * which are in ONLINE or STANDBY state.
2N/A */
2N/Astatic int
2N/Ainsert_missing_pwwn(char *phys_path, struct wwn_list_struct **wwn_list_ptr)
2N/A{
2N/A mp_pathlist_t pathlist;
2N/A int i, pathcnt, match;
2N/A struct wwn_list_struct *new_wwn, *wwn_list_s, *wwn_list_found;
2N/A char pwwn1[WWN_S_LEN];
2N/A
2N/A /*
2N/A * Now check each scsi_vhci device path to find any missed
2N/A * port wwns and insert a new wwn list entry for the missed
2N/A * port wwn
2N/A */
2N/A if (g_get_pathlist(phys_path, &pathlist)) {
2N/A /* Free memory for pathlist before return */
2N/A S_FREE(pathlist.path_info);
2N/A return (L_INVALID_PATH);
2N/A }
2N/A
2N/A pathcnt = pathlist.path_count;
2N/A for (i = 0; i < pathcnt; i++) {
2N/A /*
2N/A * Just search for ONLINE and STANDBY paths as
2N/A * those should be the only missing wwn entries.
2N/A * There is a very small window for an offline
2N/A * to have occurred between the time we retrieved
2N/A * the device list and a call to this function.
2N/A * If that happens, we just won't add it to
2N/A * the list which is probably a good thing.
2N/A */
2N/A if (pathlist.path_info[i].path_state ==
2N/A MDI_PATHINFO_STATE_ONLINE ||
2N/A pathlist.path_info[i].path_state ==
2N/A MDI_PATHINFO_STATE_STANDBY) {
2N/A (void) strncpy(pwwn1, pathlist.path_info[i].path_addr,
2N/A WWN_S_LEN - 1);
2N/A pwwn1[WWN_S_LEN - 1] = '\0';
2N/A /*
2N/A * Now search through wwn list for matching
2N/A * device path AND pwwn
2N/A * If it's found, continue to next path.
2N/A * If it's not found, add it the wwn list.
2N/A */
2N/A match = 0;
2N/A
2N/A for (wwn_list_s = *wwn_list_ptr; wwn_list_s != NULL;
2N/A wwn_list_s = wwn_list_s->wwn_next) {
2N/A if (strncmp(phys_path,
2N/A wwn_list_s->physical_path,
2N/A strlen(phys_path)) == 0) {
2N/A wwn_list_found = wwn_list_s;
2N/A if (strncmp(pwwn1,
2N/A wwn_list_s->port_wwn_s,
2N/A WWN_S_LEN) == 0) {
2N/A match++;
2N/A break;
2N/A }
2N/A }
2N/A }
2N/A if (match) {
2N/A continue;
2N/A } else {
2N/A /*
2N/A * didn't find a match but the mpxio
2N/A * device is in the list. Retrieve
2N/A * the info from the wwn_list_found
2N/A * and add it to the list.
2N/A */
2N/A if ((new_wwn = (struct wwn_list_struct *)
2N/A calloc(1,
2N/A sizeof (struct wwn_list_struct)))
2N/A == NULL) {
2N/A S_FREE(pathlist.path_info);
2N/A return (L_MALLOC_FAILED);
2N/A }
2N/A if ((new_wwn->physical_path = (char *)
2N/A calloc(1,
2N/A strlen(wwn_list_found->physical_path)
2N/A + 1)) == NULL) {
2N/A S_FREE(pathlist.path_info);
2N/A return (L_MALLOC_FAILED);
2N/A }
2N/A if ((new_wwn->logical_path = (char *)
2N/A calloc(1,
2N/A strlen(wwn_list_found->logical_path)
2N/A + 1)) == NULL) {
2N/A S_FREE(pathlist.path_info);
2N/A return (L_MALLOC_FAILED);
2N/A }
2N/A
2N/A /*
2N/A * Insert new_wwn at the beginning of the list.
2N/A */
2N/A new_wwn->wwn_next = *wwn_list_ptr;
2N/A (*wwn_list_ptr)->wwn_prev = new_wwn;
2N/A
2N/A /* set new starting ptr */
2N/A *wwn_list_ptr = new_wwn;
2N/A
2N/A memcpy(new_wwn->physical_path,
2N/A wwn_list_found->physical_path,
2N/A strlen(wwn_list_found->physical_path));
2N/A memcpy(new_wwn->logical_path,
2N/A wwn_list_found->logical_path,
2N/A strlen(wwn_list_found->logical_path));
2N/A /*
2N/A * Copy found node wwn data to this new entry
2N/A * Node wwn is required for the wwn_list
2N/A * however for mpxio devices it is not
2N/A * relevant as it may apply to multiple
2N/A * target controllers, so just use what
2N/A * we already have in wwn_list_found.
2N/A */
2N/A memcpy(new_wwn->node_wwn_s,
2N/A wwn_list_found->node_wwn_s, WWN_S_LEN);
2N/A memcpy(new_wwn->w_node_wwn,
2N/A wwn_list_found->w_node_wwn, WWN_SIZE);
2N/A new_wwn->device_type =
2N/A wwn_list_found->device_type;
2N/A memcpy(new_wwn->port_wwn_s, pwwn1, WWN_S_LEN);
2N/A }
2N/A }
2N/A }
2N/A S_FREE(pathlist.path_info);
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * gets the port wwn for a scsi_vhci device using ONLINE path priority
2N/A */
2N/Astatic int
2N/Aget_scsi_vhci_port_wwn(char *phys_path, uchar_t *port_wwn)
2N/A{
2N/A mp_pathlist_t pathlist;
2N/A int i, pathcnt, found;
2N/A char pwwn1[WWN_S_LEN];
2N/A
2N/A if (g_get_pathlist(phys_path, &pathlist)) {
2N/A return (L_INVALID_PATH);
2N/A }
2N/A
2N/A found = 0;
2N/A pathcnt = pathlist.path_count;
2N/A /*
2N/A * Look for an ONLINE path first.
2N/A * If that fails, get the STANDBY path port WWN
2N/A * If that fails, give up
2N/A */
2N/A for (i = 0; found == 0 && i < pathcnt; i++) {
2N/A if (pathlist.path_info[i].path_state ==
2N/A MDI_PATHINFO_STATE_ONLINE) {
2N/A (void) strncpy(pwwn1, pathlist.path_info[i].path_addr,
2N/A WWN_S_LEN - 1);
2N/A pwwn1[WWN_S_LEN - 1] = '\0';
2N/A found++;
2N/A }
2N/A }
2N/A
2N/A for (i = 0; found == 0 && i < pathcnt; i++) {
2N/A if (pathlist.path_info[i].path_state ==
2N/A MDI_PATHINFO_STATE_STANDBY) {
2N/A (void) strncpy(pwwn1, pathlist.path_info[i].path_addr,
2N/A WWN_S_LEN - 1);
2N/A pwwn1[WWN_S_LEN - 1] = '\0';
2N/A found++;
2N/A }
2N/A }
2N/A
2N/A S_FREE(pathlist.path_info);
2N/A if (found) {
2N/A return (string_to_wwn((uchar_t *)pwwn1, port_wwn));
2N/A } else {
2N/A return (-1);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * searches wwn_list_found for the pwwn passed in
2N/A * and sets the corresponding nwwn on return.
2N/A * If no match is found, -1 is returned and nwwn is not set.
2N/A */
2N/Astatic int
2N/Asearch_wwn_entry(struct wwn_list_found_struct *wwn_list_found, uchar_t *pwwn,
2N/A uchar_t *nwwn)
2N/A{
2N/A struct wwn_list_found_struct *wwn_list_s;
2N/A
2N/A for (wwn_list_s = wwn_list_found; wwn_list_s != NULL;
2N/A wwn_list_s = wwn_list_s->wwn_next) {
2N/A if (memcmp(pwwn, wwn_list_s->port_wwn, WWN_SIZE) == 0) {
2N/A memcpy(nwwn, wwn_list_s->node_wwn, WWN_SIZE);
2N/A return (0);
2N/A }
2N/A }
2N/A return (-1);
2N/A}
2N/A
2N/A/*
2N/A * adds a nwwn, pwwn entry to the next entry in wwn_list_found list
2N/A */
2N/Astatic int
2N/Aadd_wwn_entry(struct wwn_list_found_struct **wwn_list_found, uchar_t *pwwn,
2N/A uchar_t *nwwn)
2N/A{
2N/A struct wwn_list_found_struct *new_wwn, *temp_wwn_list_found = NULL;
2N/A
2N/A /* Got wwns, load data in list */
2N/A if ((new_wwn = (struct wwn_list_found_struct *)
2N/A calloc(1, sizeof (struct wwn_list_found_struct))) == NULL) {
2N/A return (L_MALLOC_FAILED);
2N/A }
2N/A
2N/A memcpy(new_wwn->node_wwn, nwwn, WWN_SIZE);
2N/A memcpy(new_wwn->port_wwn, pwwn, WWN_SIZE);
2N/A
2N/A /*
2N/A * Insert new_wwn in the list
2N/A */
2N/A if (*wwn_list_found != NULL) {
2N/A temp_wwn_list_found = (*wwn_list_found)->wwn_next;
2N/A (*wwn_list_found)->wwn_next = new_wwn;
2N/A } else {
2N/A *wwn_list_found = new_wwn;
2N/A }
2N/A new_wwn->wwn_next = temp_wwn_list_found;
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Create a linked list of all the WWN's for all FC_AL disks and
2N/A * tapes that are attached to this host.
2N/A *
2N/A * RETURN VALUES: 0 O.K.
2N/A *
2N/A * wwn_list pointer:
2N/A * NULL: No devices found.
2N/A * !NULL: Devices found
2N/A * wwn_list points to a linked list of wwn's.
2N/A */
2N/Aint
2N/Ag_get_wwn_list(struct wwn_list_struct **wwn_list_ptr, int verbose)
2N/A{
2N/A struct wwn_list_struct *wwn_list_p = NULL, *wwn_list_tmp_p = NULL;
2N/A struct wwn_list_found_struct *wwn_list_found = NULL;
2N/A int err;
2N/A int al_pa;
2N/A uchar_t node_wwn[WWN_SIZE], port_wwn[WWN_SIZE];
2N/A hrtime_t start_time, end_time;
2N/A char *env = NULL;
2N/A
2N/A /* return L_NULL_WWN_LIST if wwn_list_ptr is NULL */
2N/A if (wwn_list_ptr == NULL) {
2N/A return (L_NULL_WWN_LIST);
2N/A }
2N/A
2N/A if ((env = getenv("_LUX_T_DEBUG")) != NULL) {
2N/A start_time = gethrtime();
2N/A }
2N/A
2N/A if ((err = g_devices_get_all(wwn_list_ptr)) != 0) {
2N/A return (err);
2N/A }
2N/A
2N/A /*
2N/A * retain backward compatibility with g_get_wwn_list
2N/A * and retrieve the WWN for scsi_vhci devices in the
2N/A * same fashion
2N/A * Note that for scsi_vhci devices, the wwn fields are
2N/A * not relevant but in the previous versions
2N/A * we loaded the wwns so...
2N/A */
2N/A wwn_list_p = *wwn_list_ptr;
2N/A while (wwn_list_p != NULL) {
2N/A if (strstr(wwn_list_p->physical_path, SCSI_VHCI) != NULL) {
2N/A /* get port wwn of first ONLINE, STANDBY */
2N/A if ((get_scsi_vhci_port_wwn(wwn_list_p->physical_path,
2N/A port_wwn)) == 0) {
2N/A if ((search_wwn_entry(wwn_list_found, port_wwn,
2N/A node_wwn)) != 0) {
2N/A if ((err =
2N/A get_wwns(wwn_list_p->physical_path,
2N/A port_wwn,
2N/A node_wwn, &al_pa,
2N/A &wwn_list_found)) != 0) {
2N/A g_free_wwn_list_found(
2N/A &wwn_list_found);
2N/A return (err);
2N/A }
2N/A }
2N/A } else {
2N/A /* Use g_get_wwn as a last resort */
2N/A if ((err = g_get_wwn(wwn_list_p->physical_path,
2N/A port_wwn, node_wwn, &al_pa, 0)) != 0) {
2N/A /*
2N/A * this is a bad WWN.
2N/A * remove it from the wwn_list.
2N/A *
2N/A * After removing the bad WWN,
2N/A * wwn_list_p should point to the next
2N/A * node in the list.
2N/A */
2N/A if ((wwn_list_p->wwn_prev == NULL) &&
2N/A (wwn_list_p->wwn_next == NULL)) {
2N/A *wwn_list_ptr = NULL;
2N/A free(wwn_list_p);
2N/A g_free_wwn_list_found(
2N/A &wwn_list_found);
2N/A return (L_NO_DEVICES_FOUND);
2N/A } else if (
2N/A wwn_list_p->wwn_prev == NULL) {
2N/A *wwn_list_ptr =
2N/A wwn_list_p->wwn_next;
2N/A free(wwn_list_p);
2N/A wwn_list_p = *wwn_list_ptr;
2N/A wwn_list_p->wwn_prev = NULL;
2N/A } else if (
2N/A wwn_list_p->wwn_next == NULL) {
2N/A wwn_list_p->wwn_prev->wwn_next =
2N/A NULL;
2N/A free(wwn_list_p);
2N/A wwn_list_p = NULL;
2N/A } else {
2N/A wwn_list_tmp_p =
2N/A wwn_list_p->wwn_next;
2N/A wwn_list_p->wwn_prev->wwn_next =
2N/A wwn_list_p->wwn_next;
2N/A wwn_list_p->wwn_next->wwn_prev =
2N/A wwn_list_p->wwn_prev;
2N/A free(wwn_list_p);
2N/A wwn_list_p = wwn_list_tmp_p;
2N/A }
2N/A continue;
2N/A }
2N/A }
2N/A copy_wwn_data_to_str(wwn_list_p->node_wwn_s, node_wwn);
2N/A copy_wwn_data_to_str(wwn_list_p->port_wwn_s, port_wwn);
2N/A memcpy(wwn_list_p->w_node_wwn, node_wwn, WWN_SIZE);
2N/A }
2N/A wwn_list_p = wwn_list_p->wwn_next;
2N/A }
2N/A g_free_wwn_list_found(&wwn_list_found);
2N/A
2N/A /*
2N/A * Now go through the list one more time to add entries for
2N/A * any missing port wwns.
2N/A * This allows a search on port wwn for any paths which are
2N/A * ONLINE or STANDBY. We don't care about OFFLINE as those won't
2N/A * and should not show up in the list
2N/A */
2N/A for (wwn_list_p = *wwn_list_ptr; wwn_list_p != NULL;
2N/A wwn_list_p = wwn_list_p->wwn_next) {
2N/A if (strstr(wwn_list_p->physical_path, SCSI_VHCI) != NULL) {
2N/A if ((err = insert_missing_pwwn(
2N/A wwn_list_p->physical_path, wwn_list_ptr)) != 0)
2N/A return (err);
2N/A }
2N/A }
2N/A
2N/A if (env != NULL) {
2N/A end_time = gethrtime();
2N/A fprintf(stdout, " g_get_wwn_list: "
2N/A "\t\tTime = %lld millisec\n",
2N/A (end_time - start_time)/1000000);
2N/A }
2N/A return (0);
2N/A
2N/A}
2N/A
2N/Aint
2N/Ag_devices_get_all(struct wwn_list_struct **wwn_list_ptr)
2N/A{
2N/A struct wwn_list_struct *tape_ptr = NULL;
2N/A struct wwn_list_struct *tmp;
2N/A int err;
2N/A di_node_t root;
2N/A hrtime_t start_time, end_time;
2N/A char *env = NULL;
2N/A
2N/A if ((env = getenv("_LUX_T_DEBUG")) != NULL) {
2N/A start_time = gethrtime();
2N/A }
2N/A
2N/A /*
2N/A * Try to prime di_drv_first_node()
2N/A * If there are no nodes bound, di_drv_first_node()
2N/A * will return nothing.
2N/A */
2N/A init_drv(DEV_TAPE_DIR, DIR_MATCH_ST, SLSH_DRV_NAME_ST);
2N/A init_drv(DEV_RDIR, DIR_MATCH_SSD, SLSH_DRV_NAME_SSD);
2N/A
2N/A if ((root = di_init("/", DINFOCPYALL)) == DI_NODE_NIL) {
2N/A return (L_DEV_SNAPSHOT_FAILED);
2N/A }
2N/A
2N/A if (env != NULL) {
2N/A end_time = gethrtime();
2N/A fprintf(stdout, " di_init - /: "
2N/A "\t\tTime = %lld millisec\n",
2N/A (end_time - start_time)/1000000);
2N/A }
2N/A
2N/A if (env != NULL) {
2N/A start_time = gethrtime();
2N/A }
2N/A
2N/A if ((err = devices_get_all(root, SSD_DRVR_NAME, SSD_MINOR_NAME,
2N/A wwn_list_ptr)) != 0) {
2N/A if (err != L_NO_DEVICES_FOUND) {
2N/A di_fini(root);
2N/A g_free_wwn_list(&tape_ptr);
2N/A g_free_wwn_list(wwn_list_ptr);
2N/A return (err);
2N/A }
2N/A }
2N/A
2N/A if (env != NULL) {
2N/A end_time = gethrtime();
2N/A fprintf(stdout, " devices_get_all - ssd: "
2N/A "\t\tTime = %lld millisec\n",
2N/A (end_time - start_time)/1000000);
2N/A }
2N/A
2N/A if (env != NULL) {
2N/A start_time = gethrtime();
2N/A }
2N/A
2N/A if ((err = devices_get_all(root, ST_DRVR_NAME, ST_MINOR_NAME,
2N/A &tape_ptr)) != 0) {
2N/A di_fini(root);
2N/A if (err != L_NO_DEVICES_FOUND) {
2N/A g_free_wwn_list(&tape_ptr);
2N/A g_free_wwn_list(wwn_list_ptr);
2N/A return (err);
2N/A } else {
2N/A /*
2N/A * if *wwn_list_ptr == NULL
2N/A * we have disks but no tapes
2N/A * Just return
2N/A */
2N/A if (*wwn_list_ptr != NULL) {
2N/A return (0);
2N/A } else {
2N/A /*
2N/A * No disks or tapes
2N/A */
2N/A g_free_wwn_list(&tape_ptr);
2N/A g_free_wwn_list(wwn_list_ptr);
2N/A return (err);
2N/A }
2N/A }
2N/A }
2N/A
2N/A if (env != NULL) {
2N/A end_time = gethrtime();
2N/A fprintf(stdout, " devices_get_all - st: "
2N/A "\t\tTime = %lld millisec\n",
2N/A (end_time - start_time)/1000000);
2N/A }
2N/A
2N/A /* Now link the two together */
2N/A if (*wwn_list_ptr != NULL) { /* We have both disks and tapes */
2N/A /* Walk to the end of it */
2N/A for (tmp = *wwn_list_ptr; tmp->wwn_next != NULL;
2N/A tmp = tmp->wwn_next)
2N/A ;
2N/A tmp->wwn_next = tape_ptr;
2N/A tape_ptr->wwn_prev = tmp;
2N/A di_fini(root);
2N/A return (0);
2N/A }
2N/A
2N/A /* else we have no disks */
2N/A *wwn_list_ptr = tape_ptr;
2N/A di_fini(root);
2N/A return (0);
2N/A}
2N/A
2N/Avoid
2N/Ag_free_wwn_list_found(struct wwn_list_found_struct **wwn_list_found) {
2N/A WWN_list_found *next = NULL;
2N/A
2N/A /* return if wwn_list_found is NULL */
2N/A if (wwn_list_found == NULL) {
2N/A return;
2N/A }
2N/A for (; *wwn_list_found != NULL; *wwn_list_found = next) {
2N/A next = (*wwn_list_found)->wwn_next;
2N/A g_destroy_data(*wwn_list_found);
2N/A *wwn_list_found = NULL;
2N/A }
2N/A}
2N/A
2N/Avoid
2N/Ag_free_wwn_list(struct wwn_list_struct **wwn_list)
2N/A{
2N/A WWN_list *next = NULL;
2N/A
2N/A /* return if wwn_list is NULL */
2N/A if (wwn_list == NULL) {
2N/A return;
2N/A }
2N/A
2N/A for (; *wwn_list != NULL; *wwn_list = next) {
2N/A next = (*wwn_list)->wwn_next;
2N/A if ((*wwn_list)->physical_path != NULL)
2N/A (void) g_destroy_data((*wwn_list)->physical_path);
2N/A if ((*wwn_list)->logical_path != NULL)
2N/A (void) g_destroy_data((*wwn_list)->logical_path);
2N/A (void) g_destroy_data(*wwn_list);
2N/A }
2N/A wwn_list = NULL;
2N/A}
2N/A
2N/A
2N/A
2N/A
2N/Avoid
2N/Ag_sort_wwn_list(struct wwn_list_struct **wwn_list)
2N/A{
2N/A int i, n;
2N/A struct wwn_list_struct **wwn_list_array;
2N/A struct wwn_list_struct *wwn_list_ptr;
2N/A struct wwn_list_struct **wwn_list_array_ptr1;
2N/A struct wwn_list_struct **wwn_list_array_ptr2;
2N/A
2N/A /*
2N/A * Count the number of wwn_list in the list
2N/A */
2N/A for (n = 0, wwn_list_ptr = *wwn_list;
2N/A wwn_list_ptr != NULL;
2N/A wwn_list_ptr = wwn_list_ptr->wwn_next) {
2N/A n++;
2N/A }
2N/A if (n <= 1) {
2N/A return;
2N/A }
2N/A
2N/A /*
2N/A * Allocate a simple wwn_list array and fill it in
2N/A */
2N/A wwn_list_array = (struct wwn_list_struct **)
2N/A g_zalloc((n+1) * sizeof (struct wwn_list_struct *));
2N/A
2N/A wwn_list_array_ptr1 = wwn_list_array;
2N/A for (wwn_list_ptr = *wwn_list;
2N/A wwn_list_ptr != NULL;
2N/A wwn_list_ptr = wwn_list_ptr->wwn_next) {
2N/A *wwn_list_array_ptr1++ = wwn_list_ptr;
2N/A }
2N/A *wwn_list_array_ptr1 = NULL;
2N/A
2N/A /*
2N/A * Sort the wwn_list array
2N/A */
2N/A qsort((void *) wwn_list_array, n,
2N/A sizeof (struct wwn_list_struct *), wwn_list_name_compare);
2N/A
2N/A /*
2N/A * Rebuild the linked list wwn_list structure
2N/A */
2N/A wwn_list_array_ptr1 = wwn_list_array;
2N/A *wwn_list = *wwn_list_array_ptr1;
2N/A wwn_list_array_ptr2 = wwn_list_array_ptr1 + 1;
2N/A (*wwn_list_array_ptr1)->wwn_prev = NULL;
2N/A for (i = 0; i < n - 1; i++) {
2N/A (*wwn_list_array_ptr2)->wwn_prev = *wwn_list_array_ptr1;
2N/A (*wwn_list_array_ptr1++)->wwn_next = *wwn_list_array_ptr2++;
2N/A }
2N/A (*wwn_list_array_ptr1)->wwn_next = NULL;
2N/A
2N/A /*
2N/A * Clean up
2N/A */
2N/A (void) g_destroy_data((void *)wwn_list_array);
2N/A}
2N/A
2N/Aint
2N/Awwn_list_name_compare(const void *arg1, const void *arg2)
2N/A{
2N/A char *s1, *s2;
2N/A int n1, n2;
2N/A char *p1, *p2;
2N/A
2N/A s1 = (*((struct wwn_list_struct **)arg1))->logical_path;
2N/A s2 = (*((struct wwn_list_struct **)arg2))->logical_path;
2N/A for (;;) {
2N/A if (*s1 == 0 || *s2 == 0)
2N/A break;
2N/A if ((isdigit(*s1) && isdigit(*s2))) {
2N/A n1 = strtol(s1, &p1, 10);
2N/A n2 = strtol(s2, &p2, 10);
2N/A if (n1 != n2) {
2N/A return (n1 - n2);
2N/A }
2N/A s1 = p1;
2N/A s2 = p2;
2N/A } else if (*s1 != *s2) {
2N/A break;
2N/A } else {
2N/A s1++;
2N/A s2++;
2N/A }
2N/A }
2N/A return (*s1 - *s2);
2N/A}
2N/A
2N/A/*
2N/A * Get the limited map for FC4 devices.
2N/A * This function is specific to FC4
2N/A * devices and doesn't work for FC (leadville) devices.
2N/A *
2N/A * RETURN VALUES:
2N/A * 0 O.K.
2N/A * non-zero otherwise
2N/A *
2N/A * lilpmap *map_ptr:
2N/A * NULL: No devices found
2N/A * !NULL: if devices found
2N/A */
2N/Aint
2N/Ag_get_limited_map(char *path, struct lilpmap *map_ptr, int verbose)
2N/A{
2N/A int fd, i;
2N/A char drvr_path[MAXPATHLEN];
2N/A struct stat stbuf;
2N/A
2N/A
2N/A /* initialize map */
2N/A (void) memset(map_ptr, 0, sizeof (struct lilpmap));
2N/A
2N/A (void) strcpy(drvr_path, path);
2N/A /*
2N/A * Get the path to the :devctl driver
2N/A *
2N/A * This assumes the path looks something like this:
2N/A * /devices/sbus@1f,0/SUNW,socal@1,0:1
2N/A * or
2N/A * /devices/sbus@1f,0/SUNW,socal@1,0
2N/A * or
2N/A * a 1 level PCI type driver
2N/A */
2N/A if (stat(drvr_path, &stbuf) < 0) {
2N/A return (L_LSTAT_ERROR);
2N/A }
2N/A if ((stbuf.st_mode & S_IFMT) == S_IFDIR) {
2N/A /* append a port. Just try 0 since they did not give us one */
2N/A (void) strcat(drvr_path, ":0");
2N/A }
2N/A
2N/A P_DPRINTF(" g_get_limited_map: Geting drive map from:"
2N/A " %s\n", drvr_path);
2N/A
2N/A /* open controller */
2N/A if ((fd = g_object_open(drvr_path, O_NDELAY | O_RDONLY)) == -1)
2N/A return (L_OPEN_PATH_FAIL);
2N/A
2N/A if (ioctl(fd, FCIO_GETMAP, map_ptr) != 0) {
2N/A I_DPRINTF(" FCIO_GETMAP ioctl failed\n");
2N/A (void) close(fd);
2N/A return (L_FCIO_GETMAP_IOCTL_FAIL);
2N/A }
2N/A (void) close(fd);
2N/A
2N/A /*
2N/A * Check for reasonableness.
2N/A */
2N/A if ((map_ptr->lilp_length > 126) || (map_ptr->lilp_magic != 0x1107)) {
2N/A return (L_INVALID_LOOP_MAP);
2N/A }
2N/A for (i = 0; i < (uint_t)map_ptr->lilp_length; i++) {
2N/A if (map_ptr->lilp_list[i] > 0xef) {
2N/A return (L_INVALID_LOOP_MAP);
2N/A }
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * For leadville specific HBA's ONLY.
2N/A * Get the host specific parameters,
2N/A * al_pa, hard address, node/port WWN etc.
2N/A *
2N/A * OUTPUT:
2N/A * fc_port_dev_t structure.
2N/A *
2N/A * RETURNS:
2N/A * 0 if OK
2N/A * non-zero in case of error.
2N/A */
2N/Aint
2N/Ag_get_host_params(char *host_path, fc_port_dev_t *host_val, int verbose)
2N/A{
2N/A int err;
2N/A int fd;
2N/A int dev_type;
2N/A fcio_t fcio;
2N/A
2N/A /* return invalid path if host_path is NULL */
2N/A if (host_path == NULL) {
2N/A return (L_INVALID_PATH);
2N/A }
2N/A /* return invalid arg if host_val is NULL */
2N/A if (host_val == NULL) {
2N/A return (L_INVALID_ARG);
2N/A }
2N/A
2N/A dev_type = g_get_path_type(host_path);
2N/A if ((dev_type == 0) || !(dev_type & FC_GEN_XPORT)) {
2N/A return (L_INVALID_PATH_TYPE);
2N/A }
2N/A if ((fd = g_object_open(host_path, O_NDELAY | O_RDONLY)) == -1) {
2N/A return (L_OPEN_PATH_FAIL);
2N/A }
2N/A
2N/A /* initialize structure */
2N/A (void) memset(host_val, 0, sizeof (struct fc_port_dev));
2N/A
2N/A fcio.fcio_cmd = FCIO_GET_HOST_PARAMS;
2N/A fcio.fcio_xfer = FCIO_XFER_READ;
2N/A fcio.fcio_obuf = (caddr_t)host_val;
2N/A fcio.fcio_olen = sizeof (fc_port_dev_t);
2N/A
2N/A if (g_issue_fcio_ioctl(fd, &fcio, verbose) != 0) {
2N/A I_DPRINTF(" FCIO_GET_HOST_PARAMS ioctl failed.\n");
2N/A (void) close(fd);
2N/A return (L_FCIO_GET_HOST_PARAMS_FAIL);
2N/A }
2N/A (void) close(fd);
2N/A
2N/A /* get the inquiry information for the leadville HBA. */
2N/A if ((err = get_fca_inq_dtype(host_path, host_val->dev_pwwn,
2N/A &host_val->dev_dtype)) != 0) {
2N/A return (err);
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/A
2N/A
2N/A/*
2N/A * Issue FCIO ioctls to the port(fp) driver.
2N/A * FCIO ioctl needs to be retried when it
2N/A * is returned with an EINVAL error, wait
2N/A * time between retries should be atleast
2N/A * WAIT_FCIO_IOCTL (too much of a time to wait!!)
2N/A *
2N/A * OUTPUT:
2N/A * fcio_t structure
2N/A *
2N/A * RETURNS:
2N/A * 0 if O.K.
2N/A * non-zero otherwise.
2N/A */
2N/Aint
2N/Ag_issue_fcio_ioctl(int fd, fcio_t *fcio, int verbose)
2N/A{
2N/A int ntries;
2N/A
2N/A for (ntries = 0; ntries < RETRY_FCIO_IOCTL; ntries++) {
2N/A if (ioctl(fd, FCIO_CMD, fcio) != 0) {
2N/A if ((errno == EAGAIN) &&
2N/A (ntries+1 < RETRY_FCIO_IOCTL)) {
2N/A /* wait WAIT_FCIO_IOCTL */
2N/A (void) usleep(WAIT_FCIO_IOCTL);
2N/A continue;
2N/A }
2N/A I_DPRINTF("FCIO ioctl failed.\n"
2N/A "Error: %s. fc_error = %d (0x%x)\n",
2N/A strerror(errno), fcio->fcio_errno,
2N/A fcio->fcio_errno);
2N/A if (errno == EINVAL) {
2N/A if (fcio->fcio_errno == FC_TOOMANY) {
2N/A return (L_INVALID_DEVICE_COUNT);
2N/A } else {
2N/A return (errno);
2N/A }
2N/A }
2N/A /*
2N/A * When port is offlined, qlc
2N/A * returns the FC_OFFLINE error and errno
2N/A * is set to EIO.
2N/A * We do want to ignore this error,
2N/A * especially when an enclosure is
2N/A * removed from the loop.
2N/A */
2N/A if (fcio->fcio_errno == FC_OFFLINE)
2N/A break;
2N/A return (-1);
2N/A }
2N/A break;
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * This function issues the FCP_TGT_INQUIRY ioctl to
2N/A * the fcp module
2N/A *
2N/A * OUTPUT:
2N/A * fcp_ioctl structure in fcp_data is filled in by fcp
2N/A *
2N/A * RETURN VALUES :
2N/A * 0 on Success
2N/A * Non-zero otherwise
2N/A */
2N/Astatic int
2N/Ag_issue_fcp_ioctl(int fd, struct fcp_ioctl *fcp_data, int verbose)
2N/A{
2N/A int num_tries = 0;
2N/A struct device_data *dev_data = NULL;
2N/A
2N/A /*
2N/A * Issue the ioctl to FCP
2N/A * The retries are required because the driver may
2N/A * need some time to respond at times.
2N/A */
2N/A while (num_tries++ < RETRY_FCP_IOCTL) {
2N/A /* if ioctl fails it is an error from Solaris operation. */
2N/A if (ioctl(fd, FCP_TGT_INQUIRY, fcp_data) == -1) {
2N/A if (errno == EAGAIN) {
2N/A (void) usleep(WAIT_FCP_IOCTL);
2N/A continue;
2N/A } else {
2N/A break;
2N/A }
2N/A }
2N/A dev_data = (struct device_data *)((void *)(fcp_data->list));
2N/A if (dev_data->dev_status == 0) {
2N/A return (0);
2N/A }
2N/A
2N/A if (dev_data->dev_status == EAGAIN) {
2N/A (void) usleep(WAIT_FCP_IOCTL);
2N/A continue;
2N/A } else {
2N/A dev_data->dev0_type = DTYPE_UNKNOWN;
2N/A return (0);
2N/A }
2N/A }
2N/A
2N/A return (L_FCP_TGT_INQUIRY_FAIL);
2N/A}
2N/A
2N/A/*
2N/A * Get the number of devices and also
2N/A * a list of devices accessible through
2N/A * the device's port as specified by path.
2N/A * The calling function * is responsible for freeing the dev_list.
2N/A *
2N/A * Acquires inq_dtype from g_get_inq_dtype() and
2N/A * stores into dev_dtype field of fc_port_dev.
2N/A *
2N/A * For fabric devices call FCIO_DEV_LOGIN (if necessary) to execute port login
2N/A * and get inq dtype.
2N/A *
2N/A * dev_list:
2N/A * NULL: No devices found, in case of an error
2N/A * Non-NULL: Devices found.
2N/A * ndevs:
2N/A * set to the number of devices
2N/A * accessible through the port.
2N/A *
2N/A * RETURNS:
2N/A * 0 if O.K.
2N/A * non-zero otherwise
2N/A */
2N/Aint
2N/Ag_get_dev_list(char *path, fc_port_dev_t **dev_list, int *ndevs)
2N/A{
2N/A int num_devices = 0;
2N/A int i, err, ulp_failure = 0, new_count = 0;
2N/A int dev_type;
2N/A int fd;
2N/A char fcapath[MAXPATHLEN];
2N/A char *char_ptr;
2N/A struct stat stbuf;
2N/A fcio_t fcio;
2N/A uint32_t port_top;
2N/A fc_port_dev_t *dlist;
2N/A
2N/A *dev_list = dlist = NULL;
2N/A (void) strcpy(fcapath, path);
2N/A /*
2N/A * Get the path to the :devctl driver
2N/A *
2N/A * This assumes the path looks something like this:
2N/A * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0/ses@e,0:0
2N/A * or
2N/A * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0
2N/A * or
2N/A * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0:devctl
2N/A * or
2N/A * a 1 level PCI type driver but still :devctl
2N/A */
2N/A if (strstr(fcapath, DRV_NAME_SSD) || strstr(fcapath, SES_NAME)) {
2N/A if ((char_ptr = strrchr(fcapath, '/')) == NULL) {
2N/A return (L_INVALID_PATH);
2N/A }
2N/A *char_ptr = '\0'; /* Terminate sting */
2N/A /* append controller */
2N/A (void) strcat(fcapath, FC_CTLR);
2N/A } else {
2N/A if (stat(fcapath, &stbuf) < 0) {
2N/A return (L_LSTAT_ERROR);
2N/A }
2N/A if ((stbuf.st_mode & S_IFMT) == S_IFDIR) {
2N/A /* append controller */
2N/A (void) strcat(fcapath, FC_CTLR);
2N/A }
2N/A }
2N/A dev_type = g_get_path_type(fcapath);
2N/A if ((dev_type == 0) || !(dev_type & FC_GEN_XPORT)) {
2N/A return (L_INVALID_PATH_TYPE);
2N/A }
2N/A if ((fd = g_object_open(fcapath, O_NDELAY | O_RDONLY)) == -1) {
2N/A return (L_OPEN_PATH_FAIL);
2N/A }
2N/A
2N/A /*
2N/A * Get the device list from port driver
2N/A */
2N/A fcio.fcio_cmd = FCIO_GET_NUM_DEVS;
2N/A fcio.fcio_olen = sizeof (num_devices);
2N/A fcio.fcio_xfer = FCIO_XFER_READ;
2N/A fcio.fcio_obuf = (caddr_t)&num_devices;
2N/A if (g_issue_fcio_ioctl(fd, &fcio, 0) != 0) {
2N/A I_DPRINTF(" FCIO_GET_NUM_DEVS ioctl failed.\n");
2N/A (void) close(fd);
2N/A return (L_FCIO_GET_NUM_DEVS_FAIL);
2N/A }
2N/A if (num_devices == 0) {
2N/A *ndevs = 0;
2N/A (void) close(fd);
2N/A return (L_NO_DEVICES_FOUND);
2N/A }
2N/A
2N/A if ((dlist = (fc_port_dev_t *)calloc(num_devices,
2N/A sizeof (fc_port_dev_t))) == NULL) {
2N/A (void) close(fd);
2N/A return (L_MALLOC_FAILED);
2N/A }
2N/A bzero((caddr_t)&fcio, sizeof (fcio));
2N/A /* Get the device list */
2N/A fcio.fcio_cmd = FCIO_GET_DEV_LIST;
2N/A /* Information read operation */
2N/A fcio.fcio_xfer = FCIO_XFER_READ;
2N/A fcio.fcio_olen = num_devices * sizeof (fc_port_dev_t);
2N/A fcio.fcio_obuf = (caddr_t)dlist;
2N/A /* new device count */
2N/A fcio.fcio_alen = sizeof (new_count);
2N/A fcio.fcio_abuf = (caddr_t)&new_count;
2N/A if ((err = g_issue_fcio_ioctl(fd, &fcio, 0)) != 0) {
2N/A if (err == L_INVALID_DEVICE_COUNT) {
2N/A /*
2N/A * original buffer was small so allocate buffer
2N/A * with a new count and retry.
2N/A */
2N/A free(dlist);
2N/A num_devices = new_count;
2N/A new_count = 0;
2N/A if ((dlist = (fc_port_dev_t *)calloc(num_devices,
2N/A sizeof (fc_port_dev_t))) == NULL) {
2N/A (void) close(fd);
2N/A return (L_MALLOC_FAILED);
2N/A }
2N/A fcio.fcio_cmd = FCIO_GET_DEV_LIST;
2N/A /* Information read operation */
2N/A fcio.fcio_xfer = FCIO_XFER_READ;
2N/A fcio.fcio_obuf = (caddr_t)dlist;
2N/A fcio.fcio_olen = num_devices * sizeof (fc_port_dev_t);
2N/A /* new device count */
2N/A fcio.fcio_alen = sizeof (new_count);
2N/A fcio.fcio_abuf = (caddr_t)&new_count;
2N/A if ((err = g_issue_fcio_ioctl(fd, &fcio, 0)) != 0) {
2N/A if (err == L_INVALID_DEVICE_COUNT) {
2N/A /*
2N/A * No more retry. There may be severe
2N/A * hardware problem so return error
2N/A * here.
2N/A */
2N/A I_DPRINTF(" Device count was %d"
2N/A " should have been %d\n",
2N/A num_devices, new_count);
2N/A } else {
2N/A I_DPRINTF(
2N/A " FCIO_GET_DEV_LIST ioctl failed.");
2N/A err = L_FCIO_GET_DEV_LIST_FAIL;
2N/A }
2N/A free(dlist);
2N/A (void) close(fd);
2N/A return (err);
2N/A }
2N/A } else {
2N/A I_DPRINTF(" FCIO_GET_DEV_LIST ioctl failed.");
2N/A free(dlist);
2N/A (void) close(fd);
2N/A return (L_FCIO_GET_DEV_LIST_FAIL);
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * if new count is smaller than the original number from
2N/A * FCIO_GET_NUM_DEVS, adjust new count and buffer size
2N/A * and continue.
2N/A */
2N/A if (new_count < num_devices) {
2N/A if (new_count == 0) {
2N/A *ndevs = 0;
2N/A (void) close(fd);
2N/A S_FREE(dlist);
2N/A return (L_NO_DEVICES_FOUND);
2N/A }
2N/A num_devices = new_count;
2N/A if ((dlist = (fc_port_dev_t *)realloc(dlist,
2N/A (new_count * sizeof (fc_port_dev_t))))
2N/A == NULL) {
2N/A S_FREE(dlist);
2N/A (void) close(fd);
2N/A return (L_MALLOC_FAILED);
2N/A }
2N/A }
2N/A
2N/A *dev_list = dlist;
2N/A *ndevs = num_devices;
2N/A
2N/A /* close here since fcapath will be passed to other routines. */
2N/A (void) close(fd);
2N/A
2N/A if ((err = g_get_fca_port_topology(fcapath, &port_top, 0)) != 0) {
2N/A free(*dev_list);
2N/A *dev_list = NULL;
2N/A return (err);
2N/A }
2N/A
2N/A /* Get the inq_dtype for each device on dev list. */
2N/A for (i = 0; i < num_devices; i++, dlist++) {
2N/A /* Get the inq_dtype for each device. */
2N/A if ((err = g_get_inq_dtype(fcapath, dlist->dev_pwwn,
2N/A &dlist->dev_dtype)) != 0) {
2N/A /*
2N/A * if g_get_inq_dtype failed on g_dev_login
2N/A * or g_issue_fcp_ioctl, continue to the next
2N/A * dev on dlist.
2N/A * L_GET_DEV_LIST_ULP_FAILURE is returned
2N/A * after processing the whole dlist.
2N/A */
2N/A if ((err == L_FCIO_DEV_LOGIN_FAIL) ||
2N/A (err == L_FCP_TGT_INQUIRY_FAIL)) {
2N/A ulp_failure = 1;
2N/A dlist->dev_dtype = GFC_ERR_INQ_DTYPE;
2N/A } else {
2N/A (void) free(*dev_list);
2N/A *dev_list = NULL;
2N/A return (err);
2N/A }
2N/A }
2N/A }
2N/A
2N/A if (ulp_failure) {
2N/A return (L_GET_DEV_LIST_ULP_FAILURE);
2N/A } else {
2N/A return (0);
2N/A }
2N/A}
2N/A
2N/A
2N/A/* Constant used by g_get_inq_dtype() */
2N/A#define FCP_PATH "/devices/pseudo/fcp@0:fcp"
2N/A
2N/A/*
2N/A * Gets the inq_dtype for devices on the fabric FC driver
2N/A * through an ioctl to the FCP module.
2N/A *
2N/A * OUTPUT:
2N/A * inq_dtype is set to the dtype on success
2N/A *
2N/A * RETURN VALUES:
2N/A * 0 on Success
2N/A * Non-zero on error
2N/A */
2N/Aint
2N/Ag_get_inq_dtype(char *fcapath, la_wwn_t pwwn, uchar_t *inq_dtype)
2N/A{
2N/A int dev_type, fd;
2N/A int err, fcp_fd;
2N/A uint32_t state;
2N/A uint32_t port_top = 0;
2N/A struct fcp_ioctl fcp_data;
2N/A struct device_data inq_data;
2N/A struct stat sbuf;
2N/A
2N/A dev_type = g_get_path_type(fcapath);
2N/A if ((dev_type == 0) || !(dev_type & FC_GEN_XPORT)) {
2N/A return (L_INVALID_PATH_TYPE);
2N/A }
2N/A
2N/A if ((err = g_get_fca_port_topology(fcapath, &port_top, 0)) != 0) {
2N/A return (err);
2N/A }
2N/A
2N/A if ((port_top == FC_TOP_FABRIC) || (port_top == FC_TOP_PUBLIC_LOOP)) {
2N/A /*
2N/A * if there is an error on getting port state we will
2N/A * continue to login.
2N/A * state can be either of
2N/A * PORT_DEVICE_INVALID, PORT_DEVICE_VALID,
2N/A * PORT_DEVICE_LOGGED_IN. Trying port login
2N/A * unless already logged in.
2N/A * It will be examined if there is an adverse
2N/A * effect on invalid state device.
2N/A */
2N/A if (((err = g_get_dev_port_state(fcapath, pwwn, &state))
2N/A != 0) || (state != PORT_DEVICE_LOGGED_IN)) {
2N/A /* do port login to fabric device. */
2N/A if ((err = g_dev_login(fcapath, pwwn)) != 0) {
2N/A return (err);
2N/A }
2N/A }
2N/A }
2N/A
2N/A if ((fd = g_object_open(fcapath, O_NDELAY | O_RDONLY)) == -1)
2N/A return (L_OPEN_PATH_FAIL);
2N/A
2N/A if (fstat(fd, &sbuf) == -1) {
2N/A (void) close(fd);
2N/A return (L_FSTAT_ERROR);
2N/A }
2N/A
2N/A if ((fcp_fd = g_object_open(FCP_PATH, O_RDONLY)) == -1) {
2N/A (void) close(fd);
2N/A return (L_OPEN_PATH_FAIL);
2N/A }
2N/A
2N/A /* Get the minor number for an fp instance */
2N/A fcp_data.fp_minor = minor(sbuf.st_rdev);
2N/A
2N/A fcp_data.listlen = 1;
2N/A inq_data.dev_pwwn = pwwn; /* The port WWN as passed */
2N/A fcp_data.list = (caddr_t)&inq_data;
2N/A
2N/A if (err = g_issue_fcp_ioctl(fcp_fd, &fcp_data, 0)) {
2N/A close(fd);
2N/A close(fcp_fd);
2N/A return (err);
2N/A }
2N/A *inq_dtype = inq_data.dev0_type;
2N/A
2N/A close(fd);
2N/A close(fcp_fd);
2N/A
2N/A return (err);
2N/A}
2N/A
2N/A/*
2N/A * Gets the inq_dtype for devices on the fabric FC driver
2N/A * through an ioctl to the FCP module.
2N/A *
2N/A * This is exactly same as g_get_inq_dtype except that it does not do
2N/A * g_dev_login(). That is for the case when the FCA tries to get its own
2N/A * inq_dtype and in such a case, it cannot PLOGI into itself.
2N/A *
2N/A * OUTPUT:
2N/A * inq_dtype is set to the dtype on success
2N/A *
2N/A * RETURN VALUES:
2N/A * 0 on Success
2N/A * Non-zero on error
2N/A */
2N/Astatic int
2N/Aget_fca_inq_dtype(char *fcapath, la_wwn_t pwwn, uchar_t *inq_dtype)
2N/A{
2N/A int dev_type, fd;
2N/A int err, fcp_fd;
2N/A struct fcp_ioctl fcp_data;
2N/A struct device_data inq_data;
2N/A struct stat sbuf;
2N/A
2N/A dev_type = g_get_path_type(fcapath);
2N/A if ((dev_type == 0) || !(dev_type & FC_GEN_XPORT)) {
2N/A return (L_INVALID_PATH_TYPE);
2N/A }
2N/A
2N/A if ((fd = g_object_open(fcapath, O_NDELAY | O_RDONLY)) == -1) {
2N/A return (L_OPEN_PATH_FAIL);
2N/A }
2N/A
2N/A if (fstat(fd, &sbuf) == -1) {
2N/A (void) close(fd);
2N/A return (L_FSTAT_ERROR);
2N/A }
2N/A
2N/A if ((fcp_fd = g_object_open(FCP_PATH, O_RDONLY)) == -1) {
2N/A (void) close(fd);
2N/A return (L_OPEN_PATH_FAIL);
2N/A }
2N/A
2N/A /* Get the minor number for an fp instance */
2N/A fcp_data.fp_minor = minor(sbuf.st_rdev);
2N/A
2N/A fcp_data.listlen = 1;
2N/A inq_data.dev_pwwn = pwwn; /* The port WWN as passed */
2N/A fcp_data.list = (caddr_t)&inq_data;
2N/A
2N/A if (err = g_issue_fcp_ioctl(fcp_fd, &fcp_data, 0)) {
2N/A close(fd);
2N/A close(fcp_fd);
2N/A return (err);
2N/A }
2N/A *inq_dtype = inq_data.dev0_type;
2N/A
2N/A close(fd);
2N/A close(fcp_fd);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * This function returns the traditional g_get_dev_map. Device list
2N/A * and local hba separate.
2N/A */
2N/Aint
2N/Ag_get_dev_map(char *path, gfc_map_t *map_ptr, int verbose)
2N/A{
2N/A return (create_map(path, map_ptr, verbose, MAP_FORMAT_STANDARD));
2N/A}
2N/A
2N/A/*
2N/A * This function returns the device map with local hba in physical
2N/A * order. Note: Physical order is only returned properly for
2N/A * private loop. local hba is also included separate
2N/A */
2N/Aint
2N/Ag_get_lilp_map(char *path, gfc_map_t *map_ptr, int verbose)
2N/A{
2N/A return (create_map(path, map_ptr, verbose, MAP_FORMAT_LILP));
2N/A}
2N/A
2N/A/*
2N/A * Gets device map from nexus driver
2N/A *
2N/A * PARAMS:
2N/A * path - must be the physical path to a device
2N/A * map - loop map returned from fc port.
2N/A * verbose - options.
2N/A *
2N/A * LOGIC:
2N/A * 1. check the validity of path via g_get_path_type.
2N/A * 2. If FC path, get the topology of the path via
2N/A * g_get_fca_port_topology.
2N/A *
2N/A * 3. If FC type(Leadville statck)
2N/A * g_get_dev_list to get the device node list of fc_port_dev_t.
2N/A * g_get_host_params to get the fca port node of fc_port_dev_t.
2N/A *
2N/A * Case of fabric or public loop topology
2N/A * Check if the port id > 0xffff.
2N/A * Move device node and fca port node to
2N/A * gfc_map structure via gfc_port_dev_info_t
2N/A * pub_port union.
2N/A * Issue g_get_inq_dtype to get FCP inquiry data
2N/A * and store it into gfc_port_dev_info_t.
2N/A *
2N/A * Case of private loop topology
2N/A * Check if the port id < 0xff.
2N/A * Move device node and fca port node to
2N/A * gfc_map structure via gfc_port_dev_info_t
2N/A * priv_port union.
2N/A * Issue g_get_inq_dtype to get FCP inquiry data
2N/A * and store it into gfc_port_dev_info_t.
2N/A *
2N/A * else FC4 type(socal/sf or ifp stack)
2N/A * SFIOCGMAP ioctl to get the device and hba nodes of
2N/A * sf_addr_pair_t.
2N/A *
2N/A *
2N/A * RETURNS:
2N/A * 0 : if OK
2N/A * non-zero: otherwise
2N/A */
2N/Aint
2N/Acreate_map(char *path, gfc_map_t *map_ptr, int verbose, int map_type)
2N/A{
2N/A int fd, i, j, num_devices = 0, err, pathcnt = 1;
2N/A char drvr_path[MAXPATHLEN], drvr_path0[MAXPATHLEN];
2N/A char *char_ptr;
2N/A struct stat stbuf;
2N/A fc_port_dev_t *dev_list, *dlistptr;
2N/A uint32_t hba_port_top = 0;
2N/A uint_t dev_type;
2N/A sf_al_map_t sf_map;
2N/A gfc_port_dev_info_t *dev_ptr;
2N/A fc_port_dev_t fp_hba_port;
2N/A mp_pathlist_t pathlist;
2N/A int p_on = 0, p_st = 0;
2N/A
2N/A /* return invalid path if path is NULL */
2N/A if (path == NULL) {
2N/A return (L_INVALID_PATH);
2N/A }
2N/A /* return invalid arg if map_ptr is NULL */
2N/A if (map_ptr == NULL) {
2N/A return (L_INVALID_ARG);
2N/A }
2N/A
2N/A map_ptr->dev_addr = NULL;
2N/A map_ptr->count = 0;
2N/A (void) strcpy(drvr_path, path);
2N/A /*
2N/A * Get the path to the :devctl driver
2N/A *
2N/A * This assumes the path looks something like this:
2N/A * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0/ses@e,0:0
2N/A * or
2N/A * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0
2N/A * or
2N/A * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0:devctl
2N/A * or
2N/A * a 1 level PCI type driver but still :devctl
2N/A */
2N/A if (strstr(path, SCSI_VHCI)) {
2N/A (void) strcpy(drvr_path0, path);
2N/A if (g_get_pathlist(drvr_path0, &pathlist)) {
2N/A return (L_INVALID_PATH);
2N/A }
2N/A pathcnt = pathlist.path_count;
2N/A p_on = p_st = 0;
2N/A for (i = 0; i < pathcnt; i++) {
2N/A if (pathlist.path_info[i].path_state < MAXPATHSTATE) {
2N/A if (pathlist.path_info[i].path_state ==
2N/A MDI_PATHINFO_STATE_ONLINE) {
2N/A p_on = i;
2N/A break;
2N/A } else if (pathlist.path_info[i].path_state ==
2N/A MDI_PATHINFO_STATE_STANDBY) {
2N/A p_st = i;
2N/A }
2N/A }
2N/A }
2N/A if (pathlist.path_info[p_on].path_state ==
2N/A MDI_PATHINFO_STATE_ONLINE) {
2N/A /* on_line path */
2N/A (void) strcpy(drvr_path,
2N/A pathlist.path_info[p_on].path_hba);
2N/A } else {
2N/A /* standby or path0 */
2N/A (void) strcpy(drvr_path,
2N/A pathlist.path_info[p_st].path_hba);
2N/A }
2N/A free(pathlist.path_info);
2N/A (void) strcat(drvr_path, FC_CTLR);
2N/A } else {
2N/A (void) strcpy(drvr_path, path);
2N/A if (strstr(drvr_path, DRV_NAME_SSD) ||
2N/A strstr(drvr_path, SES_NAME) ||
2N/A strstr(drvr_path, DRV_NAME_ST)) {
2N/A if ((char_ptr = strrchr(drvr_path, '/')) == NULL) {
2N/A return (L_INVALID_PATH);
2N/A }
2N/A *char_ptr = '\0'; /* Terminate sting */
2N/A /* append controller */
2N/A (void) strcat(drvr_path, FC_CTLR);
2N/A } else {
2N/A if (stat(drvr_path, &stbuf) < 0) {
2N/A return (L_LSTAT_ERROR);
2N/A }
2N/A if ((stbuf.st_mode & S_IFMT) == S_IFDIR) {
2N/A /* append controller */
2N/A (void) strcat(drvr_path, FC_CTLR);
2N/A }
2N/A }
2N/A }
2N/A
2N/A P_DPRINTF(" g_get_dev_map: Geting drive map from:"
2N/A " %s\n", drvr_path);
2N/A
2N/A dev_type = g_get_path_type(drvr_path);
2N/A if ((dev_type == 0) || !(dev_type & XPORT_MASK)) {
2N/A return (L_INVALID_PATH_TYPE);
2N/A }
2N/A
2N/A /* get fiber topology */
2N/A if ((err = g_get_fca_port_topology(drvr_path,
2N/A &hba_port_top, verbose)) != 0) {
2N/A return (err);
2N/A }
2N/A
2N/A /* for FC devices. */
2N/A if (dev_type & FC_FCA_MASK) {
2N/A /*
2N/A * if g_get_dev_list fails with L_NO_DEVICES_FOUND
2N/A * we still want to call g_get_host_params to try to find the
2N/A * HBA. If we do not see any HBAs on the loop, the
2N/A * g_get_host_params will fail when it trys to issue the target
2N/A * inquiry ioctl. In this case, we would still like to return
2N/A * L_NO_DEVICES_FOUND.
2N/A *
2N/A * If g_get_dev_list fails with L_NO_DEVICES_FOUND and
2N/A * g_get_host_params fails, the function returns
2N/A * L_NO_DEVICES_FOUND
2N/A */
2N/A if ((err = g_get_dev_list(drvr_path, &dev_list,
2N/A &num_devices)) != 0) {
2N/A /*
2N/A * g_get_dev_map doesn't allow ulp failure
2N/A * to continue thus we need to free dev_list
2N/A * here.
2N/A */
2N/A if (err == L_GET_DEV_LIST_ULP_FAILURE) {
2N/A (void) free(dev_list);
2N/A }
2N/A if (err != L_NO_DEVICES_FOUND) {
2N/A return (err);
2N/A }
2N/A }
2N/A
2N/A /* Get local HBA information */
2N/A if ((err = g_get_host_params(drvr_path, &fp_hba_port,
2N/A verbose)) != 0) {
2N/A (void) free(dev_list);
2N/A if (num_devices == 0)
2N/A return (L_NO_DEVICES_FOUND);
2N/A else
2N/A return (err);
2N/A }
2N/A
2N/A /* If devices, other than local HBA are found */
2N/A /* allocate space for them in the gfc_map. */
2N/A if (num_devices > 0) {
2N/A
2N/A /* If map type is on MAP_FORMAT_LILP we need */
2N/A /* to add space for the local HBA */
2N/A if (map_type == MAP_FORMAT_LILP) {
2N/A map_ptr->count = ++num_devices;
2N/A } else {
2N/A map_ptr->count = num_devices;
2N/A }
2N/A
2N/A if ((map_ptr->dev_addr = (gfc_port_dev_info_t *)
2N/A calloc(map_ptr->count,
2N/A sizeof (gfc_port_dev_info_t))) == NULL) {
2N/A (void) free(dev_list);
2N/A return (L_MALLOC_FAILED);
2N/A }
2N/A }
2N/A
2N/A /* If we want the lilp map then we need to do a little */
2N/A /* work here. The lilp map contains the local hba in */
2N/A /* the dev_addr. Once this has been added qsort the */
2N/A /* dev_addr array so it's in physical order. */
2N/A /* The lilp map will contain the local hba in the */
2N/A /* dev_addr array only when num_devices > 0 */
2N/A if (map_type == MAP_FORMAT_LILP && num_devices > 0) {
2N/A
2N/A /* First we need to allocate one additional */
2N/A /* device to the dev_addr structure, for the */
2N/A /* local hba */
2N/A if ((dev_list = (fc_port_dev_t *)realloc(dev_list,
2N/A (num_devices * sizeof (fc_port_dev_t))))
2N/A == NULL) {
2N/A S_FREE(dev_list);
2N/A (void) free(map_ptr->dev_addr);
2N/A map_ptr->dev_addr = NULL;
2N/A return (L_MALLOC_FAILED);
2N/A }
2N/A
2N/A /* Next, copy the local hba into this new loc. */
2N/A if (memcpy(dev_list+(num_devices-1), &fp_hba_port,
2N/A sizeof (fc_port_dev_t)) == NULL) {
2N/A (void) free(dev_list);
2N/A (void) free(map_ptr->dev_addr);
2N/A map_ptr->dev_addr = NULL;
2N/A return (L_MEMCPY_FAILED);
2N/A }
2N/A
2N/A /* Now sort by physical location */
2N/A qsort((void*)dev_list, num_devices,
2N/A sizeof (fc_port_dev_t), lilp_map_cmp);
2N/A }
2N/A
2N/A dlistptr = dev_list;
2N/A dev_ptr = map_ptr->dev_addr;
2N/A
2N/A switch (hba_port_top) {
2N/A case FC_TOP_FABRIC:
2N/A case FC_TOP_PUBLIC_LOOP:
2N/A if (fp_hba_port.dev_did.port_id <= 0xffff) {
2N/A (void) free(dlistptr);
2N/A (void) free(map_ptr->dev_addr);
2N/A map_ptr->dev_addr = NULL;
2N/A return (L_INVALID_FABRIC_ADDRESS);
2N/A } else {
2N/A map_ptr->hba_addr.port_topology = hba_port_top;
2N/A map_ptr->hba_addr.gfc_port_dev.pub_port =
2N/A fp_hba_port;
2N/A }
2N/A for (i = 0; i < num_devices; i++, dev_ptr++,
2N/A dev_list++) {
2N/A if (dev_list->dev_did.port_id <= 0xffff) {
2N/A (void) free(dlistptr);
2N/A (void) free(map_ptr->dev_addr);
2N/A map_ptr->dev_addr = NULL;
2N/A return (L_INVALID_FABRIC_ADDRESS);
2N/A } else {
2N/A dev_ptr->port_topology = hba_port_top;
2N/A dev_ptr->gfc_port_dev.pub_port =
2N/A *dev_list;
2N/A }
2N/A }
2N/A break;
2N/A case FC_TOP_PRIVATE_LOOP:
2N/A /*
2N/A * Map the (new->old) structures here.
2N/A * Checking (i < SF_NUM_ENTRIES_IN_MAP) just to
2N/A * make sure that we don't overrun the map structure
2N/A * since it can hold data for upto 126 devices.
2N/A */
2N/A if (fp_hba_port.dev_did.port_id > 0xff) {
2N/A (void) free(dlistptr);
2N/A (void) free(map_ptr->dev_addr);
2N/A map_ptr->dev_addr = NULL;
2N/A return (L_INVALID_PRIVATE_LOOP_ADDRESS);
2N/A } else {
2N/A map_ptr->hba_addr.port_topology = hba_port_top;
2N/A map_ptr->hba_addr.gfc_port_dev.
2N/A priv_port.sf_al_pa =
2N/A (uchar_t)fp_hba_port.dev_did.port_id;
2N/A map_ptr->hba_addr.gfc_port_dev.
2N/A priv_port.sf_hard_address = (uchar_t)
2N/A fp_hba_port.dev_hard_addr.hard_addr;
2N/A for (j = 0; j < FC_WWN_SIZE; j++) {
2N/A map_ptr->hba_addr.gfc_port_dev.
2N/A priv_port.sf_node_wwn[j] =
2N/A fp_hba_port.dev_nwwn.raw_wwn[j];
2N/A map_ptr->hba_addr.gfc_port_dev.
2N/A priv_port.sf_port_wwn[j] =
2N/A fp_hba_port.dev_pwwn.raw_wwn[j];
2N/A }
2N/A map_ptr->hba_addr.gfc_port_dev.
2N/A priv_port.sf_inq_dtype =
2N/A fp_hba_port.dev_dtype;
2N/A }
2N/A
2N/A for (i = 0; (i < num_devices &&
2N/A i < SF_NUM_ENTRIES_IN_MAP);
2N/A i++, dev_ptr++, dev_list++) {
2N/A /*
2N/A * Out of 24 bits of port_id, copy only
2N/A * 8 bits to al_pa. This works okay for
2N/A * devices that're on a private loop.
2N/A */
2N/A if (dev_list->dev_did.port_id > 0xff) {
2N/A (void) free(dlistptr);
2N/A (void) free(map_ptr->dev_addr);
2N/A map_ptr->dev_addr = NULL;
2N/A return (L_INVALID_PRIVATE_LOOP_ADDRESS);
2N/A }
2N/A dev_ptr->port_topology = hba_port_top;
2N/A dev_ptr->gfc_port_dev.priv_port.sf_al_pa
2N/A = (uchar_t)dev_list->dev_did.port_id;
2N/A
2N/A /* Code refactorization is needed for C style */
2N/A dev_ptr->gfc_port_dev.priv_port.sf_hard_address
2N/A = (uchar_t)dev_list->dev_hard_addr.hard_addr;
2N/A
2N/A for (j = 0; j < FC_WWN_SIZE; j++) {
2N/A
2N/A dev_ptr->gfc_port_dev.priv_port.sf_node_wwn[j] =
2N/A dev_list->dev_nwwn.raw_wwn[j];
2N/A dev_ptr->gfc_port_dev.priv_port.sf_port_wwn[j] =
2N/A dev_list->dev_pwwn.raw_wwn[j];
2N/A
2N/A }
2N/A dev_ptr->gfc_port_dev.priv_port.sf_inq_dtype =
2N/A dev_list->dev_dtype;
2N/A }
2N/A break;
2N/A case FC_TOP_PT_PT:
2N/A (void) free(dlistptr);
2N/A (void) free(map_ptr->dev_addr);
2N/A map_ptr->dev_addr = NULL;
2N/A return (L_PT_PT_FC_TOP_NOT_SUPPORTED);
2N/A default:
2N/A (void) free(dlistptr);
2N/A (void) free(map_ptr->dev_addr);
2N/A map_ptr->dev_addr = NULL;
2N/A return (L_UNEXPECTED_FC_TOPOLOGY);
2N/A } /* End of switch on port_topology */
2N/A (void) free(dlistptr);
2N/A
2N/A } else { /* sf and fc4/pci devices */
2N/A if ((fd = g_object_open(drvr_path, O_NDELAY | O_RDONLY)) == -1)
2N/A return (errno);
2N/A /* initialize map */
2N/A (void) memset(&sf_map, 0, sizeof (struct sf_al_map));
2N/A if (ioctl(fd, SFIOCGMAP, &sf_map) != 0) {
2N/A I_DPRINTF(" SFIOCGMAP ioctl failed.\n");
2N/A (void) close(fd);
2N/A return (L_SFIOCGMAP_IOCTL_FAIL);
2N/A }
2N/A /* Check for reasonableness. */
2N/A if ((sf_map.sf_count > 126) || (sf_map.sf_count < 0)) {
2N/A (void) close(fd);
2N/A return (L_INVALID_LOOP_MAP);
2N/A }
2N/A if (sf_map.sf_count == 0) {
2N/A (void) close(fd);
2N/A return (L_NO_DEVICES_FOUND);
2N/A }
2N/A
2N/A map_ptr->count = sf_map.sf_count;
2N/A if ((map_ptr->dev_addr =
2N/A (gfc_port_dev_info_t *)calloc(map_ptr->count,
2N/A sizeof (gfc_port_dev_info_t))) == NULL) {
2N/A (void) close(fd);
2N/A return (L_MALLOC_FAILED);
2N/A }
2N/A dev_ptr = map_ptr->dev_addr;
2N/A for (i = 0; i < sf_map.sf_count; i++, dev_ptr++) {
2N/A if (sf_map.sf_addr_pair[i].sf_al_pa > 0xef) {
2N/A (void) free(map_ptr->dev_addr);
2N/A map_ptr->dev_addr = NULL;
2N/A (void) close(fd);
2N/A return (L_INVALID_LOOP_MAP);
2N/A }
2N/A dev_ptr->port_topology = hba_port_top;
2N/A dev_ptr->gfc_port_dev.priv_port =
2N/A sf_map.sf_addr_pair[i];
2N/A }
2N/A map_ptr->hba_addr.port_topology = hba_port_top;
2N/A map_ptr->hba_addr.gfc_port_dev.priv_port =
2N/A sf_map.sf_hba_addr;
2N/A (void) close(fd);
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * This function consturct FC proerty list using map_dev_fc_prop_list.
2N/A *
2N/A * port WWN, node WWN, port addr and hard addr properties is constructed.
2N/A *
2N/A * return 0 if OK.
2N/A * otherwise returns error code.
2N/A */
2N/Astatic int
2N/Aupdate_map_dev_fc_prop(impl_map_dev_prop_t **prop_list, uint32_t map_topo,
2N/A uchar_t *port_wwn, uchar_t *node_wwn, int port_addr, int hard_addr)
2N/A{
2N/A impl_map_dev_prop_t *prop_ptr, *pl_start = NULL, *pl_end = NULL;
2N/A uchar_t *port_wwn_data, *node_wwn_data;
2N/A int *port_addr_data, *hard_addr_data;
2N/A
2N/A /* consrtruct port addr property. */
2N/A if ((map_topo == FC_TOP_FABRIC) || (map_topo == FC_TOP_PUBLIC_LOOP)) {
2N/A if (port_addr <= 0xffff) {
2N/A return (L_INVALID_FABRIC_ADDRESS);
2N/A }
2N/A } else if (map_topo == FC_TOP_PRIVATE_LOOP) {
2N/A if (port_addr > 0xff) {
2N/A return (L_INVALID_PRIVATE_LOOP_ADDRESS);
2N/A }
2N/A }
2N/A
2N/A if ((prop_ptr = (impl_map_dev_prop_t *)calloc(
2N/A 1, sizeof (impl_map_dev_prop_t))) == NULL) {
2N/A return (L_MALLOC_FAILED);
2N/A }
2N/A (void) strncpy(prop_ptr->prop_name, PORT_ADDR_PROP,
2N/A strlen(PORT_ADDR_PROP));
2N/A prop_ptr->prop_type = GFC_PROP_TYPE_INT;
2N/A
2N/A if ((port_addr_data = (int *)calloc(1, sizeof (int))) == NULL) {
2N/A free(prop_ptr);
2N/A return (L_MALLOC_FAILED);
2N/A }
2N/A *port_addr_data = port_addr;
2N/A prop_ptr->prop_data = port_addr_data;
2N/A
2N/A pl_start = pl_end = prop_ptr;
2N/A
2N/A /* consrtruct port WWN property. */
2N/A if ((prop_ptr = (impl_map_dev_prop_t *)calloc(
2N/A 1, sizeof (impl_map_dev_prop_t))) == NULL) {
2N/A free_prop_list(&pl_start);
2N/A return (L_MALLOC_FAILED);
2N/A }
2N/A (void) strncpy(prop_ptr->prop_name, PORT_WWN_PROP,
2N/A strlen(PORT_WWN_PROP));
2N/A prop_ptr->prop_type = GFC_PROP_TYPE_BYTES;
2N/A
2N/A if ((port_wwn_data = (uchar_t *)calloc(1, FC_WWN_SIZE)) == NULL) {
2N/A free_prop_list(&pl_start);
2N/A return (L_MALLOC_FAILED);
2N/A }
2N/A memcpy(port_wwn_data, port_wwn, FC_WWN_SIZE);
2N/A prop_ptr->prop_data = port_wwn_data;
2N/A prop_ptr->prop_size = FC_WWN_SIZE;
2N/A pl_end->next = prop_ptr;
2N/A pl_end = prop_ptr;
2N/A
2N/A /* consrtruct node WWN property. */
2N/A if ((prop_ptr = (impl_map_dev_prop_t *)calloc(
2N/A 1, sizeof (impl_map_dev_prop_t))) == NULL) {
2N/A free_prop_list(&pl_start);
2N/A return (L_MALLOC_FAILED);
2N/A }
2N/A (void) strncpy(prop_ptr->prop_name, NODE_WWN_PROP,
2N/A strlen(NODE_WWN_PROP));
2N/A prop_ptr->prop_type = GFC_PROP_TYPE_BYTES;
2N/A
2N/A if ((node_wwn_data = (uchar_t *)calloc(
2N/A 1, FC_WWN_SIZE)) == NULL) {
2N/A free_prop_list(&pl_start);
2N/A return (L_MALLOC_FAILED);
2N/A }
2N/A memcpy(node_wwn_data, node_wwn, FC_WWN_SIZE);
2N/A prop_ptr->prop_data = node_wwn_data;
2N/A prop_ptr->prop_size = FC_WWN_SIZE;
2N/A pl_end->next = prop_ptr;
2N/A pl_end = prop_ptr;
2N/A
2N/A /* consrtruct hard addr property. */
2N/A if ((prop_ptr = (impl_map_dev_prop_t *)calloc(
2N/A 1, sizeof (impl_map_dev_prop_t))) == NULL) {
2N/A free_prop_list(&pl_start);
2N/A return (L_MALLOC_FAILED);
2N/A }
2N/A (void) strncpy(prop_ptr->prop_name, HARD_ADDR_PROP,
2N/A strlen(HARD_ADDR_PROP));
2N/A prop_ptr->prop_type = GFC_PROP_TYPE_INT;
2N/A
2N/A if ((hard_addr_data = (int *)calloc(1, sizeof (int))) == NULL) {
2N/A free_prop_list(&pl_start);
2N/A return (L_MALLOC_FAILED);
2N/A }
2N/A *hard_addr_data = hard_addr;
2N/A prop_ptr->prop_data = hard_addr_data;
2N/A pl_end->next = prop_ptr;
2N/A pl_end = prop_ptr;
2N/A
2N/A if (*prop_list == NULL) {
2N/A *prop_list = pl_start;
2N/A } else {
2N/A pl_end->next = (*prop_list)->next;
2N/A *prop_list = pl_start;
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * This function consturct FCP inq dtype propery.
2N/A * if inq_dtype is null the property is constrcted with err info.
2N/A *
2N/A * L_MALLOC_FAILED is the only possible error.
2N/A */
2N/Astatic int
2N/Aupdate_map_dev_FCP_prop(impl_map_dev_prop_t **prop_list,
2N/A uchar_t *inq_dtype, int err, int exist)
2N/A{
2N/A impl_map_dev_prop_t *prop_ptr, *old_prop_ptr;
2N/A uchar_t *inq_dtype_data;
2N/A
2N/A if ((prop_ptr = (impl_map_dev_prop_t *)calloc(
2N/A 1, sizeof (impl_map_dev_prop_t))) == NULL) {
2N/A return (L_MALLOC_FAILED);
2N/A }
2N/A
2N/A (void) strncpy(prop_ptr->prop_name, INQ_DTYPE_PROP,
2N/A strlen(INQ_DTYPE_PROP));
2N/A
2N/A if (inq_dtype == NULL) {
2N/A prop_ptr->prop_data = NULL;
2N/A prop_ptr->prop_error = err;
2N/A } else {
2N/A if ((inq_dtype_data = (uchar_t *)calloc(
2N/A 1, sizeof (uchar_t))) == NULL) {
2N/A free(prop_ptr);
2N/A return (L_MALLOC_FAILED);
2N/A }
2N/A memcpy(inq_dtype_data, inq_dtype, sizeof (uchar_t));
2N/A prop_ptr->prop_data = inq_dtype_data;
2N/A prop_ptr->prop_type = GFC_PROP_TYPE_BYTES;
2N/A prop_ptr->prop_size = sizeof (uchar_t);
2N/A }
2N/A
2N/A if (*prop_list == NULL) {
2N/A *prop_list = prop_ptr;
2N/A } else {
2N/A if (exist == PROP_EXIST) {
2N/A prop_ptr->next = (*prop_list)->next;
2N/A old_prop_ptr = *prop_list;
2N/A *prop_list = prop_ptr;
2N/A free((uchar_t *)(old_prop_ptr->prop_data));
2N/A old_prop_ptr->prop_data = NULL;
2N/A S_FREE(old_prop_ptr);
2N/A } else {
2N/A prop_ptr->next = *prop_list;
2N/A *prop_list = prop_ptr;
2N/A }
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * This function calls FCP_TGT_INQUIRY via g_issue_fcp_ioctl()
2N/A * to get the inq_dtype of input device and calls update_map_dev_FCP_prop().
2N/A * inq_dtype is set to NULL and pass error code if inq_dtype data is not
2N/A * requried.
2N/A *
2N/A * return error from update_map_dev_FCP_prop().
2N/A */
2N/Astatic int
2N/Ahandle_map_dev_FCP_prop(minor_t fp_xport_minor, la_wwn_t port_wwn,
2N/A impl_map_dev_prop_t **prop_list)
2N/A{
2N/A struct device_data inq_data;
2N/A int fcp_fd, err;
2N/A struct fcp_ioctl fcp_data;
2N/A uchar_t inq_dtype;
2N/A
2N/A if ((fcp_fd = g_object_open(FCP_PATH, O_RDONLY)) == -1) {
2N/A update_map_dev_FCP_prop(prop_list, NULL,
2N/A L_OPEN_PATH_FAIL, PROP_NOEXIST);
2N/A }
2N/A
2N/A /* Get the minor number for an fp instance */
2N/A fcp_data.fp_minor = fp_xport_minor;
2N/A
2N/A /* Get FCP prop for the hba first. */
2N/A fcp_data.listlen = 1;
2N/A inq_data.dev_pwwn = port_wwn;
2N/A fcp_data.list = (caddr_t)&inq_data;
2N/A
2N/A if (err = g_issue_fcp_ioctl(fcp_fd, &fcp_data, 0)) {
2N/A /* if ioctl error then set the prop_error. */
2N/A if ((err = update_map_dev_FCP_prop(
2N/A prop_list, NULL, err, PROP_NOEXIST)) != 0) {
2N/A return (err);
2N/A }
2N/A } else {
2N/A inq_dtype = inq_data.dev0_type;
2N/A if ((err = update_map_dev_FCP_prop(
2N/A prop_list, &inq_dtype, 0, PROP_NOEXIST)) != 0) {
2N/A return (err);
2N/A }
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Construct device map tree from nexus driver
2N/A *
2N/A * PARAMS:
2N/A * path - must be the physical path to a device
2N/A * l_err - ptr to an error code. Set when NULL is returned.
2N/A * flag - device map fomat and property type.
2N/A *
2N/A * LOGIC:
2N/A * 1. check the validity of path via g_get_path_type.
2N/A * 2. If FC path, get the topology of the path via
2N/A * g_get_fca_port_topology.
2N/A *
2N/A * 3. If FC type(Leadville statck)
2N/A * FCIO_GET_DEV_LIST to get the device node list of fc_port_dev_t.
2N/A * FCIO_GET_HOST_PARAMS to get the fca port node of fc_port_dev_t.
2N/A *
2N/A * root of tree is set with host_params info
2N/A * FC propery is set.
2N/A * FCP property is set if reqyested through flag.
2N/A * Issue g_issue_fcp_ioctl to get FCP inquiry data
2N/A * consruruct list of children via dev_list.
2N/A * FC property is set.
2N/A * FCP property is set if reqyested through flag.
2N/A * Issue FCIO_DEV_LOGIN if it is fabric device.
2N/A * Issue g_issue_fcp_ioctl to get FCP inquiry data.
2N/A *
2N/A * else FC4 type(socal/sf or ifp stack)
2N/A * SFIOCGMAP ioctl to get the device and hba nodes of
2N/A * sf_addr_pair_t.
2N/A * FCIO_GETMAP ioctl to get hba port info.
2N/A * consturct map and child tree list and
2N/A * set the properties as private loop devices.
2N/A *
2N/A * RETURNS:
2N/A * ptr to map is returned if OK.
2N/A * NULL and l_err is set otherwise.
2N/A */
2N/Agfc_dev_t
2N/Ag_dev_map_init(char *path, int *l_err, int flag)
2N/A{
2N/A int fd, i, num_devices = 0, err, pathcnt = 1, new_count = 0;
2N/A char drvr_path[MAXPATHLEN], drvr_path0[MAXPATHLEN];
2N/A char *char_ptr, *nexus_path;
2N/A struct stat stbuf;
2N/A fc_port_dev_t *dev_list = NULL, *dlist;
2N/A uint32_t hba_port_top, state;
2N/A uint_t path_type;
2N/A sf_al_map_t sf_map;
2N/A fc_port_dev_t fp_hba_port;
2N/A mp_pathlist_t pathlist;
2N/A int p_on = 0, p_st = 0, hba_alpa_found = 0, nexus_fd;
2N/A fcio_t fcio;
2N/A struct lilpmap limited_map;
2N/A impl_map_dev_t *impl_map, *impl_dev;
2N/A impl_map_dev_t *mdl_start = NULL, *mdl_end = NULL;
2N/A struct stat sbuf;
2N/A
2N/A if (l_err == NULL) {
2N/A return (NULL);
2N/A }
2N/A
2N/A if (path == NULL) {
2N/A *l_err = L_INVALID_PATH;
2N/A return (NULL);
2N/A }
2N/A
2N/A *l_err = 0;
2N/A
2N/A (void) strcpy(drvr_path, path);
2N/A /*
2N/A * Get the path to the :devctl driver
2N/A *
2N/A * This assumes the path looks something like this:
2N/A * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0/ses@e,0:0
2N/A * or
2N/A * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0
2N/A * or
2N/A * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0:devctl
2N/A * or
2N/A * a 1 level PCI type driver but still :devctl
2N/A */
2N/A if (strstr(path, SCSI_VHCI)) {
2N/A (void) strcpy(drvr_path0, path);
2N/A if (g_get_pathlist(drvr_path0, &pathlist)) {
2N/A *l_err = L_INVALID_PATH;
2N/A return (NULL);
2N/A }
2N/A pathcnt = pathlist.path_count;
2N/A p_on = p_st = 0;
2N/A for (i = 0; i < pathcnt; i++) {
2N/A if (pathlist.path_info[i].path_state < MAXPATHSTATE) {
2N/A if (pathlist.path_info[i].path_state ==
2N/A MDI_PATHINFO_STATE_ONLINE) {
2N/A p_on = i;
2N/A break;
2N/A } else if (pathlist.path_info[i].path_state ==
2N/A MDI_PATHINFO_STATE_STANDBY) {
2N/A p_st = i;
2N/A }
2N/A }
2N/A }
2N/A if (pathlist.path_info[p_on].path_state ==
2N/A MDI_PATHINFO_STATE_ONLINE) {
2N/A /* on_line path */
2N/A (void) strcpy(drvr_path,
2N/A pathlist.path_info[p_on].path_hba);
2N/A } else {
2N/A /* standby or path0 */
2N/A (void) strcpy(drvr_path,
2N/A pathlist.path_info[p_st].path_hba);
2N/A }
2N/A free(pathlist.path_info);
2N/A (void) strcat(drvr_path, FC_CTLR);
2N/A } else {
2N/A (void) strcpy(drvr_path, path);
2N/A if (strstr(drvr_path, DRV_NAME_SSD) ||
2N/A strstr(drvr_path, SES_NAME) ||
2N/A strstr(drvr_path, DRV_NAME_ST)) {
2N/A if ((char_ptr = strrchr(drvr_path, '/')) == NULL) {
2N/A *l_err = L_INVALID_PATH;
2N/A return (NULL);
2N/A }
2N/A *char_ptr = '\0'; /* Terminate sting */
2N/A /* append controller */
2N/A (void) strcat(drvr_path, FC_CTLR);
2N/A } else {
2N/A if (stat(drvr_path, &stbuf) < 0) {
2N/A *l_err = L_LSTAT_ERROR;
2N/A return (NULL);
2N/A }
2N/A if ((stbuf.st_mode & S_IFMT) == S_IFDIR) {
2N/A /* append controller */
2N/A (void) strcat(drvr_path, FC_CTLR);
2N/A }
2N/A }
2N/A }
2N/A
2N/A P_DPRINTF(" g_dev_map_init: Geting drive map from:"
2N/A " %s\n", drvr_path);
2N/A
2N/A path_type = g_get_path_type(drvr_path);
2N/A if ((path_type == 0) || !(path_type & XPORT_MASK)) {
2N/A *l_err = L_INVALID_PATH_TYPE;
2N/A return (NULL);
2N/A }
2N/A
2N/A /* get fiber topology */
2N/A if ((err = g_get_fca_port_topology(drvr_path,
2N/A &hba_port_top, 0)) != 0) {
2N/A *l_err = err;
2N/A return (NULL);
2N/A }
2N/A
2N/A if ((fd = g_object_open(drvr_path, O_NDELAY | O_RDONLY)) == -1) {
2N/A *l_err = errno;
2N/A return (NULL);
2N/A }
2N/A
2N/A /* for FC devices. */
2N/A if (path_type & FC_FCA_MASK) {
2N/A /* get the number of device first. */
2N/A fcio.fcio_cmd = FCIO_GET_NUM_DEVS;
2N/A fcio.fcio_olen = sizeof (num_devices);
2N/A fcio.fcio_xfer = FCIO_XFER_READ;
2N/A fcio.fcio_obuf = (caddr_t)&num_devices;
2N/A if (g_issue_fcio_ioctl(fd, &fcio, 0) != 0) {
2N/A I_DPRINTF(" FCIO_GET_NUM_DEVS ioctl failed.\n");
2N/A (void) close(fd);
2N/A *l_err = L_FCIO_GET_NUM_DEVS_FAIL;
2N/A return (NULL);
2N/A }
2N/A if (num_devices != 0) {
2N/A if ((dev_list = (fc_port_dev_t *)calloc(num_devices,
2N/A sizeof (fc_port_dev_t))) == NULL) {
2N/A (void) close(fd);
2N/A *l_err = L_MALLOC_FAILED;
2N/A return (NULL);
2N/A }
2N/A
2N/A bzero((caddr_t)&fcio, sizeof (fcio));
2N/A /* Get the device list */
2N/A fcio.fcio_cmd = FCIO_GET_DEV_LIST;
2N/A /* Information read operation */
2N/A fcio.fcio_xfer = FCIO_XFER_READ;
2N/A fcio.fcio_olen = num_devices * sizeof (fc_port_dev_t);
2N/A fcio.fcio_obuf = (caddr_t)dev_list;
2N/A /* new device count */
2N/A fcio.fcio_alen = sizeof (new_count);
2N/A fcio.fcio_abuf = (caddr_t)&new_count;
2N/A if ((err = g_issue_fcio_ioctl(fd, &fcio, 0)) != 0) {
2N/A if (err == L_INVALID_DEVICE_COUNT) {
2N/A /*
2N/A * original buffer was small so allocate
2N/A * buffer with a new count and retry.
2N/A */
2N/A free(dev_list);
2N/A num_devices = new_count;
2N/A new_count = 0;
2N/A if ((dev_list = (fc_port_dev_t *)
2N/A calloc(num_devices,
2N/A sizeof (fc_port_dev_t))) == NULL) {
2N/A (void) close(fd);
2N/A *l_err = L_MALLOC_FAILED;
2N/A return (NULL);
2N/A }
2N/A fcio.fcio_cmd = FCIO_GET_DEV_LIST;
2N/A /* Information read operation */
2N/A fcio.fcio_xfer = FCIO_XFER_READ;
2N/A fcio.fcio_obuf = (caddr_t)dev_list;
2N/A fcio.fcio_olen = num_devices *
2N/A sizeof (fc_port_dev_t);
2N/A /* new device count */
2N/A fcio.fcio_alen = sizeof (new_count);
2N/A fcio.fcio_abuf = (caddr_t)&new_count;
2N/A if ((err = g_issue_fcio_ioctl(fd, &fcio,
2N/A 0)) != 0) {
2N/A if (err ==
2N/A L_INVALID_DEVICE_COUNT) {
2N/A /*
2N/A * No more retry. There
2N/A * may be severe
2N/A * hardware problem so
2N/A * return error here.
2N/A */
2N/A I_DPRINTF(" Device"
2N/A " count was %d"
2N/A " should have been"
2N/A " %d\n",
2N/A num_devices,
2N/A new_count);
2N/A free(dev_list);
2N/A (void) close(fd);
2N/A
2N/A *l_err = L_INVALID_DEVICE_COUNT;
2N/A
2N/A return (NULL);
2N/A } else {
2N/A
2N/A /* Code refactorization is needed for C style */
2N/A I_DPRINTF(" FCIO_GET_DEV_LIST ioctl failed.");
2N/A
2N/A free(dev_list);
2N/A (void) close(fd);
2N/A
2N/A *l_err = L_FCIO_GET_DEV_LIST_FAIL;
2N/A
2N/A return (NULL);
2N/A }
2N/A }
2N/A }
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * if new count is smaller than the original number from
2N/A * FCIO_GET_NUM_DEVS, adjust new count and buffer size
2N/A * and continue.
2N/A */
2N/A if (new_count < num_devices) {
2N/A num_devices = new_count;
2N/A if (new_count > 0) {
2N/A if ((dev_list = (fc_port_dev_t *)
2N/A realloc(dev_list,
2N/A (new_count * sizeof (fc_port_dev_t))))
2N/A == NULL) {
2N/A S_FREE(dev_list);
2N/A (void) close(fd);
2N/A *l_err = L_MALLOC_FAILED;
2N/A return (NULL);
2N/A }
2N/A }
2N/A }
2N/A
2N/A /* get the host param info */
2N/A (void) memset(&fp_hba_port, 0, sizeof (struct fc_port_dev));
2N/A fcio.fcio_cmd = FCIO_GET_HOST_PARAMS;
2N/A fcio.fcio_xfer = FCIO_XFER_READ;
2N/A fcio.fcio_obuf = (caddr_t)&fp_hba_port;
2N/A fcio.fcio_olen = sizeof (fc_port_dev_t);
2N/A
2N/A if (g_issue_fcio_ioctl(fd, &fcio, 0) != 0) {
2N/A I_DPRINTF(" FCIO_GET_HOST_PARAMS ioctl failed.\n");
2N/A (void) close(fd);
2N/A if (num_devices == 0) {
2N/A *l_err = L_NO_DEVICES_FOUND;
2N/A } else {
2N/A free(dev_list);
2N/A *l_err = L_FCIO_GET_HOST_PARAMS_FAIL;
2N/A }
2N/A (void) close(fd);
2N/A return (NULL);
2N/A }
2N/A
2N/A /* If we want the lilp map then we need to do a little */
2N/A /* work here. The lilp map contains the local hba in */
2N/A /* the dev_addr. Once this has been added qsort the */
2N/A /* dev_addr array so it's in physical order. */
2N/A if ((flag & MAP_FORMAT_LILP) == MAP_FORMAT_LILP) {
2N/A /* First we need to allocate one additional */
2N/A /* device to the dev_addr structure, for the */
2N/A /* local hba */
2N/A if (num_devices > 0) {
2N/A if ((dev_list = (fc_port_dev_t *)
2N/A realloc(dev_list,
2N/A (++num_devices *
2N/A sizeof (fc_port_dev_t)))) == NULL) {
2N/A (void) close(fd);
2N/A /*
2N/A * In case dev_list is not null free
2N/A * it.
2N/A */
2N/A S_FREE(dev_list);
2N/A *l_err = L_MALLOC_FAILED;
2N/A return (NULL);
2N/A }
2N/A
2N/A /*
2N/A * Next, copy the local hba into this new
2N/A * loc.
2N/A */
2N/A if (memcpy(dev_list+(num_devices-1),
2N/A &fp_hba_port,
2N/A sizeof (fc_port_dev_t)) == NULL) {
2N/A (void) free(dev_list);
2N/A (void) close(fd);
2N/A *l_err = L_MEMCPY_FAILED;
2N/A return (NULL);
2N/A }
2N/A
2N/A /* Now sort by physical location */
2N/A qsort((void*)dev_list, num_devices,
2N/A sizeof (fc_port_dev_t), lilp_map_cmp);
2N/A }
2N/A }
2N/A
2N/A
2N/A /* We have dev list info and host param info. */
2N/A /* Now constructs map tree with these info. */
2N/A /* First consturct the root of the map tree */
2N/A /* with host param. */
2N/A if ((impl_map = (impl_map_dev_t *)calloc(
2N/A 1, sizeof (impl_map_dev_t))) == NULL) {
2N/A (void) free(dev_list);
2N/A (void) close(fd);
2N/A *l_err = L_MALLOC_FAILED;
2N/A return (NULL);
2N/A }
2N/A impl_map->flag = flag;
2N/A impl_map->topo = hba_port_top;
2N/A
2N/A /* consturct hba property list. */
2N/A if ((err = update_map_dev_fc_prop(&impl_map->prop_list,
2N/A hba_port_top, fp_hba_port.dev_pwwn.raw_wwn,
2N/A fp_hba_port.dev_nwwn.raw_wwn, fp_hba_port.dev_did.port_id,
2N/A fp_hba_port.dev_hard_addr.hard_addr)) != 0) {
2N/A (void) free(dev_list);
2N/A (void) close(fd);
2N/A g_dev_map_fini(impl_map);
2N/A *l_err = err;
2N/A return (NULL);
2N/A }
2N/A
2N/A if ((flag & MAP_XPORT_PROP_ONLY) != MAP_XPORT_PROP_ONLY) {
2N/A if (fstat(fd, &sbuf) == -1) {
2N/A (void) free(dev_list);
2N/A (void) close(fd);
2N/A g_dev_map_fini(impl_map);
2N/A *l_err = L_FSTAT_ERROR;
2N/A return (NULL);
2N/A }
2N/A if ((err = handle_map_dev_FCP_prop(minor(sbuf.st_rdev),
2N/A fp_hba_port.dev_pwwn, &impl_map->prop_list)) != 0) {
2N/A (void) free(dev_list);
2N/A (void) close(fd);
2N/A g_dev_map_fini(impl_map);
2N/A *l_err = err;
2N/A return (NULL);
2N/A }
2N/A }
2N/A
2N/A /* consturct child for each device and */
2N/A /* set device property list. */
2N/A dlist = dev_list;
2N/A for (i = 0; i < num_devices; i++, dlist++) {
2N/A if ((impl_dev = (impl_map_dev_t *)calloc(
2N/A 1, sizeof (impl_map_dev_t))) == NULL) {
2N/A (void) free(dev_list);
2N/A (void) close(fd);
2N/A g_dev_map_fini(impl_map);
2N/A *l_err = L_MALLOC_FAILED;
2N/A return (NULL);
2N/A }
2N/A /* set the map as parent */
2N/A impl_dev->parent = impl_map;
2N/A if ((err = update_map_dev_fc_prop(&impl_dev->prop_list,
2N/A hba_port_top, dlist->dev_pwwn.raw_wwn,
2N/A dlist->dev_nwwn.raw_wwn, dlist->dev_did.port_id,
2N/A dlist->dev_hard_addr.hard_addr)) != 0) {
2N/A (void) free(dev_list);
2N/A (void) close(fd);
2N/A g_dev_map_fini(impl_map);
2N/A *l_err = err;
2N/A return (NULL);
2N/A }
2N/A if (i == 0) {
2N/A mdl_start = mdl_end = impl_dev;
2N/A } else {
2N/A mdl_end->next = impl_dev;
2N/A mdl_end = impl_dev;
2N/A }
2N/A if ((flag & MAP_XPORT_PROP_ONLY) !=
2N/A MAP_XPORT_PROP_ONLY) {
2N/A if (((hba_port_top == FC_TOP_PUBLIC_LOOP) ||
2N/A (hba_port_top == FC_TOP_FABRIC)) &&
2N/A (memcmp(fp_hba_port.dev_pwwn.raw_wwn,
2N/A dlist->dev_pwwn.raw_wwn, FC_WWN_SIZE) != 0)) {
2N/A (void) memset(&fcio, 0, sizeof (fcio_t));
2N/A fcio.fcio_cmd = FCIO_GET_STATE;
2N/A fcio.fcio_ilen = sizeof (dlist->dev_pwwn);
2N/A fcio.fcio_ibuf = (caddr_t)&dlist->dev_pwwn;
2N/A fcio.fcio_xfer = FCIO_XFER_READ |
2N/A FCIO_XFER_WRITE;
2N/A fcio.fcio_olen = sizeof (uint32_t);
2N/A fcio.fcio_obuf = (caddr_t)&state;
2N/A fcio.fcio_alen = 0;
2N/A fcio.fcio_abuf = NULL;
2N/A if (g_issue_fcio_ioctl(fd, &fcio, 0) != 0) {
2N/A I_DPRINTF(
2N/A " FCIO_GET_STATE ioctl failed.\n");
2N/A if ((err = update_map_dev_FCP_prop(
2N/A &impl_dev->prop_list, NULL,
2N/A L_FCIO_GET_STATE_FAIL,
2N/A PROP_NOEXIST)) != 0) {
2N/A (void) free(dev_list);
2N/A (void) close(fd);
2N/A g_dev_map_fini(impl_map);
2N/A *l_err = err;
2N/A return (NULL);
2N/A }
2N/A }
2N/A if (state != PORT_DEVICE_LOGGED_IN) {
2N/A (void) close(fd);
2N/A if ((fd = g_object_open(drvr_path,
2N/A O_NDELAY | O_RDONLY | O_EXCL)) ==
2N/A -1) {
2N/A (void) free(dev_list);
2N/A g_dev_map_fini(impl_map);
2N/A *l_err = L_OPEN_PATH_FAIL;
2N/A return (NULL);
2N/A }
2N/A (void) memset(&fcio, 0,
2N/A sizeof (fcio_t));
2N/A fcio.fcio_cmd = FCIO_DEV_LOGIN;
2N/A fcio.fcio_ilen =
2N/A sizeof (dlist->dev_pwwn);
2N/A fcio.fcio_ibuf =
2N/A (caddr_t)&dlist->dev_pwwn;
2N/A fcio.fcio_xfer = FCIO_XFER_WRITE;
2N/A fcio.fcio_olen = fcio.fcio_alen = 0;
2N/A fcio.fcio_obuf = fcio.fcio_abuf = NULL;
2N/A if (g_issue_fcio_ioctl(fd, &fcio, 0) !=
2N/A 0) {
2N/A
2N/A /* Code refactorization is needed for C style */
2N/A I_DPRINTF(" FCIO_DEV_LOGIN ioctl failed.\n");
2N/A
2N/A if ((err = update_map_dev_FCP_prop(
2N/A &impl_dev->prop_list, NULL,
2N/A L_FCIO_DEV_LOGIN_FAIL,
2N/A PROP_NOEXIST)) != 0) {
2N/A (void) free(dev_list);
2N/A (void) close(fd);
2N/A g_dev_map_fini(impl_map);
2N/A *l_err = err;
2N/A return (NULL);
2N/A }
2N/A
2N/A /*
2N/A * plogi failed continue
2N/A * to next dev
2N/A */
2N/A continue;
2N/A }
2N/A }
2N/A }
2N/A /* sbuf should be set from hba_port handling. */
2N/A if ((err = handle_map_dev_FCP_prop(
2N/A minor(sbuf.st_rdev),
2N/A dlist->dev_pwwn, &impl_dev->prop_list)) !=
2N/A 0) {
2N/A (void) free(dev_list);
2N/A (void) close(fd);
2N/A g_dev_map_fini(impl_map);
2N/A *l_err = err;
2N/A return (NULL);
2N/A }
2N/A }
2N/A }
2N/A /* connect the children to to map. */
2N/A impl_map->child = mdl_start;
2N/A S_FREE(dev_list);
2N/A
2N/A } else { /* sf and fc4/pci devices */
2N/A /* initialize map */
2N/A (void) memset(&sf_map, 0, sizeof (struct sf_al_map));
2N/A if (ioctl(fd, SFIOCGMAP, &sf_map) != 0) {
2N/A I_DPRINTF(" SFIOCGMAP ioctl failed.\n");
2N/A (void) close(fd);
2N/A *l_err = L_SFIOCGMAP_IOCTL_FAIL;
2N/A return (NULL);
2N/A }
2N/A /* Check for reasonableness. */
2N/A if ((sf_map.sf_count > 126) || (sf_map.sf_count < 0)) {
2N/A (void) close(fd);
2N/A *l_err = L_INVALID_LOOP_MAP;
2N/A return (NULL);
2N/A }
2N/A
2N/A if (sf_map.sf_count == 0) {
2N/A (void) close(fd);
2N/A *l_err = L_NO_DEVICES_FOUND;
2N/A return (NULL);
2N/A }
2N/A
2N/A if ((err = g_get_nexus_path(drvr_path, &nexus_path)) != 0) {
2N/A (void) close(fd);
2N/A *l_err = err;
2N/A return (NULL);
2N/A }
2N/A
2N/A if ((nexus_fd =
2N/A g_object_open(nexus_path, O_NDELAY | O_RDONLY)) == -1) {
2N/A (void) close(fd);
2N/A S_FREE(nexus_path);
2N/A *l_err = errno;
2N/A return (NULL);
2N/A }
2N/A
2N/A /* get limited map to get hba param info */
2N/A if (ioctl(nexus_fd, FCIO_GETMAP, &limited_map) != 0) {
2N/A I_DPRINTF(" FCIO_GETMAP ioctl failed\n");
2N/A (void) close(fd);
2N/A (void) close(nexus_fd);
2N/A S_FREE(nexus_path);
2N/A *l_err = L_FCIO_GETMAP_IOCTL_FAIL;
2N/A return (NULL);
2N/A }
2N/A (void) close(nexus_fd);
2N/A S_FREE(nexus_path);
2N/A
2N/A for (i = 0; i < sf_map.sf_count; i++) {
2N/A if (sf_map.sf_addr_pair[i].sf_al_pa ==
2N/A limited_map.lilp_myalpa) {
2N/A sf_map.sf_hba_addr = sf_map.sf_addr_pair[i];
2N/A hba_alpa_found = 1;
2N/A }
2N/A }
2N/A
2N/A if (!(hba_alpa_found)) {
2N/A (void) close(fd);
2N/A *l_err = L_INVALID_LOOP_MAP;
2N/A return (NULL);
2N/A }
2N/A
2N/A /* We have dev list info and host param info. */
2N/A /* Now constructs map tree with these info. */
2N/A /* First consturct the root of the map tree */
2N/A /* with host param. */
2N/A if ((impl_map = (impl_map_dev_t *)calloc(
2N/A 1, sizeof (impl_map_dev_t))) == NULL) {
2N/A (void) close(fd);
2N/A *l_err = L_MALLOC_FAILED;
2N/A return (NULL);
2N/A }
2N/A impl_map->flag = flag;
2N/A impl_map->topo = hba_port_top;
2N/A
2N/A /* consturct hba property list. */
2N/A if ((err = update_map_dev_fc_prop(&impl_map->prop_list,
2N/A hba_port_top, sf_map.sf_hba_addr.sf_port_wwn,
2N/A sf_map.sf_hba_addr.sf_node_wwn,
2N/A (int)sf_map.sf_hba_addr.sf_al_pa,
2N/A (int)sf_map.sf_hba_addr.sf_hard_address)) != 0) {
2N/A (void) close(fd);
2N/A g_dev_map_fini(impl_map);
2N/A *l_err = err;
2N/A return (NULL);
2N/A }
2N/A
2N/A if ((flag & MAP_XPORT_PROP_ONLY) != MAP_XPORT_PROP_ONLY) {
2N/A if ((err = update_map_dev_FCP_prop(&impl_map->prop_list,
2N/A &sf_map.sf_hba_addr.sf_inq_dtype, 0,
2N/A PROP_NOEXIST)) != 0) {
2N/A (void) close(fd);
2N/A g_dev_map_fini(impl_map);
2N/A *l_err = err;
2N/A return (NULL);
2N/A }
2N/A }
2N/A
2N/A for (i = 0; i < sf_map.sf_count; i++) {
2N/A if ((impl_dev = (impl_map_dev_t *)calloc(
2N/A 1, sizeof (impl_map_dev_t))) == NULL) {
2N/A (void) close(fd);
2N/A g_dev_map_fini(impl_map);
2N/A *l_err = L_MALLOC_FAILED;
2N/A return (NULL);
2N/A }
2N/A /* set the map as parent */
2N/A impl_dev->parent = impl_map;
2N/A if ((err = update_map_dev_fc_prop(&impl_dev->prop_list,
2N/A hba_port_top, sf_map.sf_addr_pair[i].sf_port_wwn,
2N/A sf_map.sf_addr_pair[i].sf_node_wwn,
2N/A (int)(sf_map.sf_addr_pair[i].sf_al_pa),
2N/A (int)(sf_map.sf_addr_pair[i].sf_hard_address))) !=
2N/A 0) {
2N/A (void) close(fd);
2N/A g_dev_map_fini(impl_map);
2N/A *l_err = err;
2N/A return (NULL);
2N/A }
2N/A if (i == 0) {
2N/A mdl_start = mdl_end = impl_dev;
2N/A } else {
2N/A mdl_end->next = impl_dev;
2N/A mdl_end = impl_dev;
2N/A }
2N/A if ((flag & MAP_XPORT_PROP_ONLY) !=
2N/A MAP_XPORT_PROP_ONLY) {
2N/A if ((err = update_map_dev_FCP_prop(
2N/A &impl_dev->prop_list,
2N/A &sf_map.sf_addr_pair[i].sf_inq_dtype, 0,
2N/A PROP_NOEXIST)) != 0) {
2N/A (void) close(fd);
2N/A g_dev_map_fini(impl_map);
2N/A *l_err = err;
2N/A return (NULL);
2N/A }
2N/A }
2N/A } /* end of for loop */
2N/A
2N/A impl_map->child = mdl_start;
2N/A } /* end of else */
2N/A
2N/A close(fd);
2N/A return ((gfc_dev_t)(impl_map));
2N/A}
2N/A
2N/A/*
2N/A * This function deallocates memory for propery list.
2N/A */
2N/Astatic void
2N/Afree_prop_list(impl_map_dev_prop_t **prop_list)
2N/A{
2N/A impl_map_dev_prop_t *lp, *olp;
2N/A
2N/A lp = *prop_list;
2N/A while (lp != NULL) {
2N/A switch (lp->prop_type) {
2N/A case GFC_PROP_TYPE_BYTES:
2N/A free((uchar_t *)(lp->prop_data));
2N/A break;
2N/A case GFC_PROP_TYPE_INT:
2N/A free((int *)(lp->prop_data));
2N/A break;
2N/A case GFC_PROP_TYPE_STRING:
2N/A free((char *)(lp->prop_data));
2N/A break;
2N/A default:
2N/A break;
2N/A }
2N/A lp->prop_data = NULL;
2N/A olp = lp;
2N/A lp = olp->next;
2N/A S_FREE(olp);
2N/A }
2N/A
2N/A *prop_list = NULL;
2N/A}
2N/A
2N/A/*
2N/A * This function deallocates memory for children list.
2N/A */
2N/Astatic void
2N/Afree_child_list(impl_map_dev_t **dev_list)
2N/A{
2N/A impl_map_dev_t *lp, *olp;
2N/A
2N/A lp = *dev_list;
2N/A while (lp != NULL) {
2N/A free_prop_list(&lp->prop_list);
2N/A olp = lp;
2N/A lp = olp->next;
2N/A S_FREE(olp);
2N/A }
2N/A
2N/A *dev_list = NULL;
2N/A}
2N/A
2N/A/*
2N/A * This function deallocates memory for the whole map.
2N/A */
2N/Avoid
2N/Ag_dev_map_fini(gfc_dev_t map)
2N/A{
2N/A impl_map_dev_t *impl_map;
2N/A
2N/A impl_map = (impl_map_dev_t *)map;
2N/A
2N/A if (impl_map != NULL) {
2N/A free_prop_list(&impl_map->prop_list);
2N/A free_child_list(&impl_map->child);
2N/A S_FREE(impl_map);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * This function passes back topology of the input map.
2N/A * input should be a handle form g_dev_map_init().
2N/A *
2N/A * return 0 if OK.
2N/A * return error code otherwise.
2N/A */
2N/Aint
2N/Ag_get_map_topology(gfc_dev_t map, uint_t *topology)
2N/A{
2N/A impl_map_dev_t *impl_map;
2N/A
2N/A if (map == NULL) {
2N/A return (L_INVALID_MAP_DEV_ADDR);
2N/A }
2N/A
2N/A if (topology == NULL) {
2N/A return (L_INVALID_ARG);
2N/A }
2N/A
2N/A impl_map = (impl_map_dev_t *)map;
2N/A
2N/A *topology = impl_map->topo;
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * This function returns the first device handle of the input map.
2N/A * map input should be a handle form g_dev_map_init().
2N/A *
2N/A * l_err set to 0 if OK.
2N/A * l_err set to error code otherwise.
2N/A */
2N/Agfc_dev_t
2N/Ag_get_first_dev(gfc_dev_t map, int *l_err)
2N/A{
2N/A impl_map_dev_t *impl_map;
2N/A
2N/A if (l_err == NULL) {
2N/A return (NULL);
2N/A }
2N/A
2N/A *l_err = 0;
2N/A
2N/A if (map == NULL) {
2N/A *l_err = L_INVALID_MAP_DEV_ADDR;
2N/A return (NULL);
2N/A }
2N/A
2N/A impl_map = (impl_map_dev_t *)map;
2N/A
2N/A if (impl_map->child == NULL) {
2N/A *l_err = L_NO_SUCH_DEV_FOUND;
2N/A }
2N/A
2N/A return ((gfc_dev_t)(impl_map->child));
2N/A}
2N/A
2N/A/*
2N/A * This function returns the next device handle of the input map.
2N/A * map_dev input should be a handle for device.
2N/A *
2N/A * l_err set to 0 if OK.
2N/A * l_err set to error code otherwise.
2N/A */
2N/Agfc_dev_t
2N/Ag_get_next_dev(gfc_dev_t map_dev, int *l_err)
2N/A{
2N/A impl_map_dev_t *impl_dev;
2N/A
2N/A if (l_err == NULL) {
2N/A return (NULL);
2N/A }
2N/A
2N/A *l_err = 0;
2N/A
2N/A if (map_dev == NULL) {
2N/A *l_err = L_INVALID_MAP_DEV_ADDR;
2N/A return (NULL);
2N/A }
2N/A
2N/A impl_dev = (impl_map_dev_t *)map_dev;
2N/A
2N/A if (impl_dev->next == NULL) {
2N/A *l_err = L_NO_SUCH_DEV_FOUND;
2N/A }
2N/A
2N/A return ((gfc_dev_t)(impl_dev->next));
2N/A}
2N/A
2N/A/*
2N/A * This function passes back uchar_t type property and its count.
2N/A * map_dev input should be a handle for device.
2N/A *
2N/A * return 0 if OK.
2N/A * return error code otherwise.
2N/A */
2N/Aint
2N/Ag_dev_prop_lookup_bytes(gfc_dev_t map_dev, const char *prop_name,
2N/A int *prop_data_count, uchar_t **prop_data)
2N/A{
2N/A impl_map_dev_t *impl_dev;
2N/A impl_map_dev_prop_t *impl_prop;
2N/A int err;
2N/A
2N/A if (map_dev == NULL) {
2N/A return (L_INVALID_MAP_DEV_ADDR);
2N/A }
2N/A
2N/A if ((prop_name == NULL) || (prop_data == NULL) ||
2N/A (prop_data_count == NULL)) {
2N/A return (L_INVALID_ARG);
2N/A }
2N/A
2N/A impl_dev = (impl_map_dev_t *)map_dev;
2N/A impl_prop = impl_dev->prop_list;
2N/A
2N/A err = L_INVALID_MAP_DEV_PROP_NAME;
2N/A
2N/A while (impl_prop) {
2N/A if (strncmp(impl_prop->prop_name, prop_name,
2N/A strlen(prop_name)) == 0) {
2N/A if (impl_prop->prop_type != GFC_PROP_TYPE_BYTES) {
2N/A err = L_INVALID_MAP_DEV_PROP_TYPE;
2N/A break;
2N/A }
2N/A if (impl_prop->prop_data) {
2N/A *prop_data = (uchar_t *)(impl_prop->prop_data);
2N/A *prop_data_count = impl_prop->prop_size;
2N/A return (0);
2N/A } else {
2N/A err = impl_prop->prop_error;
2N/A }
2N/A break;
2N/A }
2N/A impl_prop = impl_prop->next;
2N/A }
2N/A
2N/A return (err);
2N/A}
2N/A
2N/A/*
2N/A * This function passes back int type property.
2N/A * map_dev input should be a handle for device.
2N/A *
2N/A * return 0 if OK.
2N/A * return error code otherwise.
2N/A */
2N/Aint
2N/Ag_dev_prop_lookup_ints(gfc_dev_t map_dev, const char *prop_name,
2N/A int **prop_data)
2N/A{
2N/A impl_map_dev_t *impl_dev;
2N/A impl_map_dev_prop_t *impl_prop;
2N/A int err;
2N/A
2N/A if (map_dev == NULL) {
2N/A return (L_INVALID_MAP_DEV_ADDR);
2N/A }
2N/A
2N/A if ((prop_name == NULL) || (prop_data == NULL)) {
2N/A return (L_INVALID_ARG);
2N/A }
2N/A
2N/A impl_dev = (impl_map_dev_t *)map_dev;
2N/A impl_prop = impl_dev->prop_list;
2N/A
2N/A err = L_INVALID_MAP_DEV_PROP_NAME;
2N/A
2N/A while (impl_prop) {
2N/A if (strncmp(impl_prop->prop_name, prop_name,
2N/A strlen(prop_name)) == 0) {
2N/A if (impl_prop->prop_type != GFC_PROP_TYPE_INT) {
2N/A err = L_INVALID_MAP_DEV_PROP_TYPE;
2N/A break;
2N/A }
2N/A if (impl_prop->prop_data) {
2N/A *prop_data = (int *)(impl_prop->prop_data);
2N/A return (0);
2N/A } else {
2N/A err = impl_prop->prop_error;
2N/A }
2N/A break;
2N/A }
2N/A impl_prop = impl_prop->next;
2N/A }
2N/A
2N/A return (err);
2N/A}
2N/A
2N/A/*
2N/A * This function passes back int type property.
2N/A * map_dev input should be a handle for device.
2N/A *
2N/A * return 0 if OK.
2N/A * return error code otherwise.
2N/A */
2N/Aint
2N/Ag_dev_prop_lookup_strings(gfc_dev_t map_dev, const char *prop_name,
2N/A char **prop_data)
2N/A{
2N/A impl_map_dev_t *impl_dev;
2N/A impl_map_dev_prop_t *impl_prop;
2N/A int err;
2N/A
2N/A if (map_dev == NULL) {
2N/A return (L_INVALID_MAP_DEV_ADDR);
2N/A }
2N/A
2N/A if ((prop_name == NULL) || (prop_data == NULL)) {
2N/A return (L_INVALID_ARG);
2N/A }
2N/A
2N/A impl_dev = (impl_map_dev_t *)map_dev;
2N/A impl_prop = impl_dev->prop_list;
2N/A
2N/A err = L_INVALID_MAP_DEV_PROP_NAME;
2N/A
2N/A while (impl_prop) {
2N/A if (strncmp(impl_prop->prop_name, prop_name,
2N/A strlen(prop_name)) == 0) {
2N/A if (impl_prop->prop_type != GFC_PROP_TYPE_STRING) {
2N/A err = L_INVALID_MAP_DEV_PROP_TYPE;
2N/A break;
2N/A }
2N/A if (impl_prop->prop_data) {
2N/A *prop_data = (char *)(impl_prop->prop_data);
2N/A return (0);
2N/A } else {
2N/A err = impl_prop->prop_error;
2N/A }
2N/A break;
2N/A }
2N/A impl_prop = impl_prop->next;
2N/A }
2N/A
2N/A return (err);
2N/A}
2N/A
2N/A/*
2N/A * This function returns the handle for the first property of the input device.
2N/A * map_dev input should be a handle form a device.
2N/A *
2N/A * l_err set to 0 if OK.
2N/A * l_err set to error code otherwise.
2N/A */
2N/Agfc_prop_t
2N/Ag_get_first_dev_prop(gfc_dev_t map_dev, int *l_err)
2N/A{
2N/A impl_map_dev_t *impl_dev;
2N/A
2N/A if (l_err == NULL) {
2N/A return (NULL);
2N/A }
2N/A
2N/A *l_err = 0;
2N/A
2N/A if (map_dev == NULL) {
2N/A *l_err = L_INVALID_MAP_DEV_ADDR;
2N/A return (NULL);
2N/A }
2N/A
2N/A impl_dev = (impl_map_dev_t *)map_dev;
2N/A
2N/A if (impl_dev->prop_list == NULL) {
2N/A *l_err = L_NO_SUCH_PROP_FOUND;
2N/A }
2N/A
2N/A return ((gfc_prop_t)(impl_dev->prop_list));
2N/A}
2N/A
2N/A/*
2N/A * This function returns the handle for next property handle of the input prop.
2N/A * map_prop input should be a handle for property.
2N/A *
2N/A * l_err set to 0 if OK.
2N/A * l_err set to error code otherwise.
2N/A */
2N/Agfc_prop_t
2N/Ag_get_next_dev_prop(gfc_prop_t map_prop, int *l_err)
2N/A{
2N/A impl_map_dev_prop_t *impl_prop;
2N/A
2N/A if (l_err == NULL) {
2N/A return (NULL);
2N/A }
2N/A
2N/A *l_err = 0;
2N/A
2N/A if (map_prop == NULL) {
2N/A *l_err = L_INVALID_MAP_DEV_PROP;
2N/A return (NULL);
2N/A }
2N/A
2N/A impl_prop = (impl_map_dev_prop_t *)map_prop;
2N/A
2N/A if (impl_prop->next == NULL) {
2N/A *l_err = L_NO_SUCH_PROP_FOUND;
2N/A }
2N/A
2N/A return ((gfc_prop_t)(impl_prop->next));
2N/A}
2N/A
2N/A/*
2N/A * This function returns the name of the property of the input prop.
2N/A * map_prop input should be a handle for property.
2N/A *
2N/A * return name string if OK.
2N/A * returns NULL and l_err set to error code otherwise.
2N/A */
2N/Achar *
2N/Ag_get_dev_prop_name(gfc_prop_t map_prop, int *l_err)
2N/A{
2N/A impl_map_dev_prop_t *impl_prop;
2N/A
2N/A if (l_err == NULL) {
2N/A return (NULL);
2N/A }
2N/A
2N/A *l_err = 0;
2N/A
2N/A if (map_prop == NULL) {
2N/A *l_err = L_INVALID_MAP_DEV_PROP;
2N/A return (NULL);
2N/A }
2N/A
2N/A impl_prop = (impl_map_dev_prop_t *)map_prop;
2N/A
2N/A return (impl_prop->prop_name);
2N/A}
2N/A
2N/A/*
2N/A * This function returns the type of the property of the input prop.
2N/A * map_prop input should be a handle for property.
2N/A *
2N/A * return type if OK.
2N/A * returns GFC_PROP_TYPE_UNKNOWN and l_err set to error code otherwise.
2N/A */
2N/Aint
2N/Ag_get_dev_prop_type(gfc_prop_t map_prop, int *l_err)
2N/A{
2N/A impl_map_dev_prop_t *impl_prop;
2N/A
2N/A if (l_err != NULL) {
2N/A *l_err = 0;
2N/A } else {
2N/A return (L_INVALID_ARG);
2N/A }
2N/A
2N/A if (map_prop == NULL) {
2N/A *l_err = L_INVALID_MAP_DEV_PROP;
2N/A return (GFC_PROP_TYPE_UNKNOWN);
2N/A }
2N/A
2N/A impl_prop = (impl_map_dev_prop_t *)map_prop;
2N/A
2N/A return (impl_prop->prop_type);
2N/A}
2N/A
2N/A/*
2N/A * This function passes back uchar_t type property and its count.
2N/A * map_prop input should be a handle for property.
2N/A *
2N/A * return 0 if OK.
2N/A * return error code otherwise.
2N/A */
2N/Aint
2N/Ag_get_dev_prop_bytes(gfc_prop_t map_prop, int *prop_data_count,
2N/A uchar_t **prop_data)
2N/A{
2N/A impl_map_dev_prop_t *impl_prop;
2N/A
2N/A if (map_prop == NULL) {
2N/A return (L_INVALID_MAP_DEV_ADDR);
2N/A }
2N/A
2N/A if ((prop_data == NULL) || (prop_data_count == NULL)) {
2N/A return (L_INVALID_ARG);
2N/A }
2N/A
2N/A impl_prop = (impl_map_dev_prop_t *)map_prop;
2N/A
2N/A if (impl_prop->prop_type != GFC_PROP_TYPE_BYTES) {
2N/A return (L_INVALID_MAP_DEV_PROP_TYPE);
2N/A }
2N/A if (impl_prop->prop_data) {
2N/A *prop_data = (uchar_t *)(impl_prop->prop_data);
2N/A *prop_data_count = impl_prop->prop_size;
2N/A } else {
2N/A return (impl_prop->prop_error);
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * This function passes back int type property.
2N/A * map_prop input should be a handle for property.
2N/A *
2N/A * return 0 if OK.
2N/A * return error code otherwise.
2N/A */
2N/Aint
2N/Ag_get_dev_prop_ints(gfc_prop_t map_prop, int **prop_data)
2N/A{
2N/A impl_map_dev_prop_t *impl_prop;
2N/A
2N/A if (map_prop == NULL) {
2N/A return (L_INVALID_MAP_DEV_ADDR);
2N/A }
2N/A
2N/A if (prop_data == NULL) {
2N/A return (L_INVALID_ARG);
2N/A }
2N/A
2N/A impl_prop = (impl_map_dev_prop_t *)map_prop;
2N/A
2N/A if (impl_prop->prop_type != GFC_PROP_TYPE_INT) {
2N/A return (L_INVALID_MAP_DEV_PROP_TYPE);
2N/A }
2N/A if (impl_prop->prop_data) {
2N/A *prop_data = (int *)(impl_prop->prop_data);
2N/A } else {
2N/A return (impl_prop->prop_error);
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * This function passes back string type property.
2N/A * map_prop input should be a handle for property.
2N/A *
2N/A * return 0 if OK.
2N/A * return error code otherwise.
2N/A */
2N/Aint
2N/Ag_get_dev_prop_strings(gfc_prop_t map_prop, char **prop_data)
2N/A{
2N/A impl_map_dev_prop_t *impl_prop;
2N/A
2N/A if (map_prop == NULL) {
2N/A return (L_INVALID_MAP_DEV_ADDR);
2N/A }
2N/A
2N/A if (prop_data == NULL) {
2N/A return (L_INVALID_ARG);
2N/A }
2N/A
2N/A impl_prop = (impl_map_dev_prop_t *)map_prop;
2N/A
2N/A if (impl_prop->prop_type != GFC_PROP_TYPE_STRING) {
2N/A return (L_INVALID_MAP_DEV_PROP_TYPE);
2N/A }
2N/A if (impl_prop->prop_data) {
2N/A *prop_data = (char *)(impl_prop->prop_data);
2N/A } else {
2N/A return (impl_prop->prop_error);
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Free the linked list allocated by g_rdls()
2N/A */
2N/Astatic void
2N/Ag_free_rls(AL_rls *rlsptr)
2N/A{
2N/A AL_rls *trlsptr;
2N/A
2N/A while (rlsptr != NULL) {
2N/A trlsptr = rlsptr->next;
2N/A free(rlsptr);
2N/A rlsptr = trlsptr;
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Read the extended link error status block
2N/A * from the specified device and Host Adapter.
2N/A *
2N/A * PARAMS:
2N/A * path_phys - physical path to an FC device
2N/A * rls_ptr - pointer to read link state structure
2N/A *
2N/A * RETURNS:
2N/A * 0 : if OK
2N/A * non-zero: otherwise
2N/A */
2N/Aint
2N/Ag_rdls(char *path_phys, struct al_rls **rls_ptr, int verbose)
2N/A{
2N/A char nexus_path[MAXPATHLEN], *nexus_path_ptr;
2N/A int fd, fp_fd, err, length, exp_map_flag = 0, *port_addr;
2N/A struct lilpmap map;
2N/A AL_rls *rls, *c1 = NULL, *c2 = NULL;
2N/A uchar_t i, *port_wwn_byte;
2N/A la_wwn_t port_wwn;
2N/A sf_al_map_t exp_map;
2N/A char *charPtr, fp_path[MAXPATHLEN];
2N/A uint_t dev_type;
2N/A struct stat stbuf;
2N/A fcio_t fcio;
2N/A fc_portid_t rls_req;
2N/A fc_rls_acc_t rls_payload;
2N/A gfc_dev_t map_root, map_dev;
2N/A uint32_t hba_port_top, state;
2N/A int pathcnt = 1, count;
2N/A mp_pathlist_t pathlist;
2N/A int p_on = 0, p_st = 0;
2N/A
2N/A /* return invalid path if path_phys is NULL */
2N/A if (path_phys == NULL) {
2N/A return (L_INVALID_PATH);
2N/A }
2N/A /* return invalid arg if rls_ptr is NULL */
2N/A if (rls_ptr == NULL) {
2N/A return (L_INVALID_ARG);
2N/A }
2N/A
2N/A *rls_ptr = rls = NULL;
2N/A
2N/A if (strstr(path_phys, SCSI_VHCI) != NULL) {
2N/A (void) strcpy(fp_path, path_phys);
2N/A if (g_get_pathlist(fp_path, &pathlist)) {
2N/A return (L_INVALID_PATH);
2N/A }
2N/A pathcnt = pathlist.path_count;
2N/A p_on = p_st = 0;
2N/A for (i = 0; i < pathcnt; i++) {
2N/A if (pathlist.path_info[i].path_state < MAXPATHSTATE) {
2N/A if (pathlist.path_info[i].path_state ==
2N/A MDI_PATHINFO_STATE_ONLINE) {
2N/A p_on = i;
2N/A break;
2N/A } else if (pathlist.path_info[i].path_state ==
2N/A MDI_PATHINFO_STATE_STANDBY) {
2N/A p_st = i;
2N/A }
2N/A }
2N/A }
2N/A if (pathlist.path_info[p_on].path_state ==
2N/A MDI_PATHINFO_STATE_ONLINE) {
2N/A /* on_line path */
2N/A (void) strcpy(fp_path,
2N/A pathlist.path_info[p_on].path_hba);
2N/A } else {
2N/A /* standby or path0 */
2N/A (void) strcpy(fp_path,
2N/A pathlist.path_info[p_st].path_hba);
2N/A }
2N/A free(pathlist.path_info);
2N/A } else {
2N/A (void) strcpy(fp_path, path_phys);
2N/A }
2N/A
2N/A /* Get map of devices on this loop. */
2N/A if ((dev_type = g_get_path_type(fp_path)) == 0) {
2N/A return (L_INVALID_PATH);
2N/A }
2N/A if (dev_type & FC_FCA_MASK) {
2N/A if (strstr(path_phys, SCSI_VHCI) != NULL) {
2N/A (void) strcat(fp_path, FC_CTLR);
2N/A } else if (strstr(fp_path, DRV_NAME_SSD) ||
2N/A strstr(fp_path, DRV_NAME_ST) ||
2N/A strstr(fp_path, SES_NAME)) {
2N/A if ((charPtr = strrchr(fp_path, '/')) == NULL) {
2N/A return (L_INVALID_PATH);
2N/A }
2N/A *charPtr = '\0';
2N/A /* append devctl to the path */
2N/A (void) strcat(fp_path, FC_CTLR);
2N/A } else {
2N/A if (stat(fp_path, &stbuf) < 0) {
2N/A return (L_LSTAT_ERROR);
2N/A }
2N/A if ((stbuf.st_mode & S_IFMT) == S_IFDIR) {
2N/A /* append devctl to the path */
2N/A (void) strcat(fp_path, FC_CTLR);
2N/A }
2N/A }
2N/A
2N/A if ((map_root = g_dev_map_init(fp_path, &err,
2N/A MAP_XPORT_PROP_ONLY)) == NULL) {
2N/A return (err);
2N/A }
2N/A
2N/A } else { /* FC4_FCA_MASK type path */
2N/A (void) memset(&map, 0, sizeof (struct lilpmap));
2N/A
2N/A if ((err = g_get_nexus_path(path_phys,
2N/A &nexus_path_ptr)) != 0) {
2N/A return (err);
2N/A }
2N/A (void) strcpy(nexus_path, nexus_path_ptr);
2N/A g_destroy_data(nexus_path_ptr);
2N/A
2N/A /* open driver */
2N/A if ((fd = g_object_open(nexus_path,
2N/A O_NDELAY | O_RDONLY)) == -1)
2N/A return (errno);
2N/A
2N/A /*
2N/A * First try using the socal version of the map.
2N/A * If that fails get the expanded vesion.
2N/A */
2N/A if (ioctl(fd, FCIO_GETMAP, &map) != 0) {
2N/A I_DPRINTF(" FCIO_GETMAP ioctl failed.\n");
2N/A if (ioctl(fd, SFIOCGMAP, &exp_map) != 0) {
2N/A I_DPRINTF(" SFIOCGMAP ioctl failed.\n");
2N/A (void) close(fd);
2N/A return (L_SFIOCGMAP_IOCTL_FAIL);
2N/A }
2N/A /* Check for reasonableness. */
2N/A if ((exp_map.sf_count > 126) ||
2N/A (exp_map.sf_count < 0)) {
2N/A (void) close(fd);
2N/A return (L_INVALID_LOOP_MAP);
2N/A }
2N/A for (i = 0; i < exp_map.sf_count; i++) {
2N/A if (exp_map.sf_addr_pair[i].sf_al_pa > 0xef) {
2N/A (void) close(fd);
2N/A return (L_INVALID_LOOP_MAP);
2N/A }
2N/A }
2N/A length = exp_map.sf_count;
2N/A exp_map_flag++;
2N/A } else {
2N/A I_DPRINTF(" g_rdls:"
2N/A " FCIO_GETMAP ioctl returned %d entries.\n",
2N/A map.lilp_length);
2N/A /* Check for reasonableness. */
2N/A if (map.lilp_length > sizeof (map.lilp_list)) {
2N/A (void) close(fd);
2N/A return (L_FCIOGETMAP_INVLD_LEN);
2N/A }
2N/A length = map.lilp_length;
2N/A }
2N/A for (i = 0; i < length; i++) {
2N/A if ((c2 = (struct al_rls *)
2N/A g_zalloc(sizeof (struct al_rls))) == NULL) {
2N/A close(fd);
2N/A return (L_MALLOC_FAILED);
2N/A }
2N/A if (rls == NULL) {
2N/A c1 = rls = c2;
2N/A } else {
2N/A for (c1 = rls; c1->next; c1 = c1->next) {};
2N/A c1 = c1->next = c2;
2N/A }
2N/A (void) strcpy(c1->driver_path, nexus_path);
2N/A if (exp_map_flag) {
2N/A c1->payload.rls_portno = c1->al_ha =
2N/A exp_map.sf_addr_pair[i].sf_al_pa;
2N/A } else {
2N/A c1->payload.rls_portno = c1->al_ha =
2N/A map.lilp_list[i];
2N/A }
2N/A c1->payload.rls_linkfail =
2N/A (uint_t)0xff000000; /* get LESB for this port */
2N/A I_DPRINTF(" g_rdls:"
2N/A " al_pa 0x%x\n", c1->payload.rls_portno);
2N/A
2N/A if (ioctl(fd, FCIO_LINKSTATUS, &c1->payload) != 0) {
2N/A /*
2N/A * The ifp driver will return ENXIO when rls
2N/A * is issued for same initiator on loop when
2N/A * there is more than one on the loop.
2N/A * Rather than completely fail, continue on.
2N/A * Set values in the payload struct to -1 as
2N/A * this is what socal is currently doing for
2N/A * the case of same initiator rls.
2N/A */
2N/A if ((dev_type & FC4_PCI_FCA) &&
2N/A (errno == ENXIO)) {
2N/A c1->payload.rls_linkfail =
2N/A c1->payload.rls_syncfail =
2N/A c1->payload.rls_sigfail =
2N/A c1->payload.rls_primitiverr =
2N/A c1->payload.rls_invalidword =
2N/A c1->payload.rls_invalidcrc =
2N/A (uint_t)0xffffffff;
2N/A } else {
2N/A I_DPRINTF(" FCIO_LINKSTATUS ioctl"
2N/A " failed with errno %d.\n", errno);
2N/A g_free_rls(rls);
2N/A (void) close(fd);
2N/A return (L_FCIO_LINKSTATUS_FAILED);
2N/A }
2N/A }
2N/A I_DPRINTF(" g_rdls: al_pa returned by ioctl 0x%x\n",
2N/A c1->payload.rls_portno);
2N/A }
2N/A *rls_ptr = rls; /* Pass back pointer */
2N/A
2N/A (void) close(fd);
2N/A return (0);
2N/A }
2N/A
2N/A /* Now we need to take care of FC_FCA_MASK case. */
2N/A /* we have map created already via g_dev_map_init. */
2N/A if ((err = g_get_map_topology(map_root, &hba_port_top)) != 0) {
2N/A g_dev_map_fini(map_root);
2N/A return (err);
2N/A }
2N/A
2N/A if ((map_dev = g_get_first_dev(map_root, &err)) == NULL) {
2N/A g_dev_map_fini(map_root);
2N/A if (err != L_NO_SUCH_DEV_FOUND) {
2N/A return (err);
2N/A } else {
2N/A return (L_NO_DEVICES_FOUND);
2N/A }
2N/A }
2N/A
2N/A while (map_dev) {
2N/A if ((err = g_dev_prop_lookup_ints(
2N/A map_dev, PORT_ADDR_PROP, &port_addr)) != 0) {
2N/A g_dev_map_fini(map_root);
2N/A g_free_rls(rls);
2N/A return (err);
2N/A }
2N/A
2N/A if ((c2 = (struct al_rls *)
2N/A g_zalloc(sizeof (struct al_rls))) == NULL) {
2N/A g_dev_map_fini(map_root);
2N/A g_free_rls(rls);
2N/A close(fd);
2N/A return (L_MALLOC_FAILED);
2N/A }
2N/A if (rls == NULL) {
2N/A c1 = rls = c2;
2N/A } else {
2N/A for (c1 = rls; c1->next; c1 = c1->next) {};
2N/A c1 = c1->next = c2;
2N/A }
2N/A /* Set the al_ha here */
2N/A c1->al_ha = rls_req.port_id = *port_addr;
2N/A
2N/A /*
2N/A * fp uses different input/output structures for
2N/A * rls. Load the values returned for the fp ioctl
2N/A * into the structure passed back to the caller
2N/A * Note: There is no reason for the path
2N/A * to be loaded into AL_rls as is done for socal/ifp
2N/A * above.
2N/A */
2N/A if ((hba_port_top == FC_TOP_FABRIC) ||
2N/A (hba_port_top == FC_TOP_PUBLIC_LOOP)) {
2N/A if ((err = g_dev_prop_lookup_bytes(
2N/A map_dev, PORT_WWN_PROP, &count,
2N/A &port_wwn_byte)) != 0) {
2N/A g_dev_map_fini(map_root);
2N/A g_free_rls(rls);
2N/A return (err);
2N/A }
2N/A memcpy(port_wwn.raw_wwn, port_wwn_byte, FC_WWN_SIZE);
2N/A if ((err = g_get_dev_port_state(
2N/A fp_path, port_wwn, &state)) == 0) {
2N/A if (state != PORT_DEVICE_LOGGED_IN) {
2N/A if ((err = g_dev_login(fp_path,
2N/A port_wwn)) != 0) {
2N/A
2N/A c1->payload.rls_linkfail =
2N/A c1->payload.rls_syncfail =
2N/A c1->payload.rls_sigfail =
2N/A c1->payload.rls_primitiverr =
2N/A c1->payload.rls_invalidword =
2N/A c1->payload.rls_invalidcrc =
2N/A (uint_t)0xffffffff;
2N/A if (((map_dev =
2N/A g_get_next_dev(map_dev,
2N/A &err))
2N/A == NULL) &&
2N/A (err !=
2N/A L_NO_SUCH_DEV_FOUND)) {
2N/A g_dev_map_fini(
2N/A map_root);
2N/A g_free_rls(rls);
2N/A return (err);
2N/A }
2N/A continue;
2N/A }
2N/A }
2N/A } /* if g_get_dev_port_state fails proceed. */
2N/A }
2N/A
2N/A fcio.fcio_cmd_flags = FCIO_CFLAGS_RLS_DEST_NPORT;
2N/A if ((fp_fd = g_object_open(fp_path, O_RDONLY | O_EXCL)) < 0) {
2N/A g_dev_map_fini(map_root);
2N/A g_free_rls(rls);
2N/A return (L_OPEN_PATH_FAIL);
2N/A }
2N/A fcio.fcio_cmd = FCIO_LINK_STATUS;
2N/A fcio.fcio_ibuf = (caddr_t)&rls_req;
2N/A fcio.fcio_ilen = sizeof (rls_req);
2N/A fcio.fcio_xfer = FCIO_XFER_RW;
2N/A fcio.fcio_flags = 0;
2N/A fcio.fcio_obuf = (caddr_t)&rls_payload;
2N/A fcio.fcio_olen = sizeof (rls_payload);
2N/A if (g_issue_fcio_ioctl(fp_fd, &fcio, verbose) != 0) {
2N/A c1->payload.rls_linkfail =
2N/A c1->payload.rls_syncfail =
2N/A c1->payload.rls_sigfail =
2N/A c1->payload.rls_primitiverr =
2N/A c1->payload.rls_invalidword =
2N/A c1->payload.rls_invalidcrc = (uint_t)0xffffffff;
2N/A } else {
2N/A /*
2N/A * Load the values into the struct passed
2N/A * back to the caller
2N/A */
2N/A c1->payload.rls_linkfail = rls_payload.rls_link_fail;
2N/A c1->payload.rls_syncfail = rls_payload.rls_sync_loss;
2N/A c1->payload.rls_sigfail = rls_payload.rls_sig_loss;
2N/A c1->payload.rls_primitiverr =
2N/A rls_payload.rls_prim_seq_err;
2N/A c1->payload.rls_invalidword =
2N/A rls_payload.rls_invalid_word;
2N/A c1->payload.rls_invalidcrc =
2N/A rls_payload.rls_invalid_crc;
2N/A }
2N/A (void) close(fp_fd);
2N/A
2N/A if (((map_dev = g_get_next_dev(map_dev, &err)) == NULL) &&
2N/A (err != L_NO_SUCH_DEV_FOUND)) {
2N/A g_dev_map_fini(map_root);
2N/A g_free_rls(rls);
2N/A return (err);
2N/A }
2N/A }
2N/A
2N/A /* for Leadville issue a final call for the initiator */
2N/A
2N/A if ((err = g_dev_prop_lookup_ints(
2N/A map_root, PORT_ADDR_PROP, &port_addr)) != 0) {
2N/A g_dev_map_fini(map_root);
2N/A g_free_rls(rls);
2N/A return (err);
2N/A }
2N/A
2N/A if ((c2 = (struct al_rls *)
2N/A g_zalloc(sizeof (struct al_rls))) == NULL) {
2N/A g_dev_map_fini(map_root);
2N/A g_free_rls(rls);
2N/A return (L_MALLOC_FAILED);
2N/A }
2N/A if (rls == NULL) {
2N/A c1 = rls = c2;
2N/A } else {
2N/A for (c1 = rls; c1->next; c1 = c1->next) {};
2N/A c1 = c1->next = c2;
2N/A }
2N/A
2N/A c1->al_ha = rls_req.port_id = *port_addr;
2N/A
2N/A if ((fp_fd = g_object_open(fp_path, O_RDONLY | O_EXCL)) < 0) {
2N/A g_dev_map_fini(map_root);
2N/A g_free_rls(rls);
2N/A return (L_OPEN_PATH_FAIL);
2N/A }
2N/A
2N/A fcio.fcio_cmd = FCIO_LINK_STATUS;
2N/A fcio.fcio_ibuf = (caddr_t)&rls_req;
2N/A fcio.fcio_ilen = sizeof (rls_req);
2N/A fcio.fcio_xfer = FCIO_XFER_RW;
2N/A fcio.fcio_flags = 0;
2N/A fcio.fcio_cmd_flags = FCIO_CFLAGS_RLS_DEST_NPORT;
2N/A fcio.fcio_obuf = (caddr_t)&rls_payload;
2N/A fcio.fcio_olen = sizeof (rls_payload);
2N/A
2N/A if (g_issue_fcio_ioctl(fp_fd, &fcio, verbose) != 0) {
2N/A c1->payload.rls_linkfail =
2N/A c1->payload.rls_syncfail =
2N/A c1->payload.rls_sigfail =
2N/A c1->payload.rls_primitiverr =
2N/A c1->payload.rls_invalidword =
2N/A c1->payload.rls_invalidcrc = (uint_t)0xffffffff;
2N/A } else {
2N/A /*
2N/A * Load the values into the struct passed
2N/A * back to the caller
2N/A */
2N/A c1->payload.rls_linkfail = rls_payload.rls_link_fail;
2N/A c1->payload.rls_syncfail = rls_payload.rls_sync_loss;
2N/A c1->payload.rls_sigfail = rls_payload.rls_sig_loss;
2N/A c1->payload.rls_primitiverr = rls_payload.rls_prim_seq_err;
2N/A c1->payload.rls_invalidword = rls_payload.rls_invalid_word;
2N/A c1->payload.rls_invalidcrc = rls_payload.rls_invalid_crc;
2N/A (void) close(fp_fd);
2N/A }
2N/A (void) close(fp_fd);
2N/A
2N/A *rls_ptr = rls; /* Pass back pointer */
2N/A
2N/A g_dev_map_fini(map_root);
2N/A return (0);
2N/A}
2N/A
2N/Astatic u_longlong_t wwnConversion(uchar_t *wwn)
2N/A{
2N/A u_longlong_t tmp;
2N/A memcpy(&tmp, wwn, sizeof (u_longlong_t));
2N/A return (tmp);
2N/A}
2N/A
2N/A/*
2N/A * Get device World Wide Name (port and node) for device at path
2N/A * and add all WWNs to the wwn_list_found list.
2N/A *
2N/A * RETURN: 0 O.K.
2N/A *
2N/A * INPUTS:
2N/A * - path_phys must be of a device, either an IB or disk.
2N/A */
2N/Astatic int
2N/Aget_wwns(char *path_phys, uchar_t port_wwn[], uchar_t node_wwn[], int *al_pa,
2N/A struct wwn_list_found_struct **wwn_list_found)
2N/A{
2N/A uint32_t hba_port_top;
2N/A int i, err, count;
2N/A char *char_ptr, *ptr;
2N/A int found = 0, pathcnt, *port_addr;
2N/A unsigned long long pwwn;
2N/A uchar_t *port_wwn_byte, *node_wwn_byte;
2N/A char drvr_path[MAXPATHLEN];
2N/A int p_on = 0, p_st = 0;
2N/A mp_pathlist_t pathlist;
2N/A char pwwn1[WWN_S_LEN];
2N/A gfc_dev_t map_root, map_dev;
2N/A hrtime_t start_time, end_time;
2N/A char *env = NULL;
2N/A
2N/A P_DPRINTF(" g_get_wwn: Getting device WWN"
2N/A " and al_pa for device: %s\n",
2N/A path_phys);
2N/A
2N/A if ((env = getenv("_LUX_T_DEBUG")) != NULL) {
2N/A start_time = gethrtime();
2N/A }
2N/A
2N/A /*
2N/A * Get the loop identifier (switch setting) from the path.
2N/A *
2N/A * This assumes the path looks something like this:
2N/A * /devices/.../SUNW,socal@3,0/SUNW,sf@0,0/SUNW,ssd@x,0
2N/A * or
2N/A * /devices/.../SUNW,qlc@5/SUNW,fp@0,0/SUNW,ssd@x,0
2N/A */
2N/A if ((char_ptr = strrchr(path_phys, '@')) == NULL) {
2N/A return (L_INVLD_PATH_NO_ATSIGN_FND);
2N/A }
2N/A char_ptr++; /* point to the loop identifier or WWN */
2N/A
2N/A (void) strcpy(drvr_path, path_phys);
2N/A /* This function allocs mem for map.dev_addr on success */
2N/A if (strstr(drvr_path, SCSI_VHCI)) {
2N/A if (g_get_pathlist(drvr_path, &pathlist)) {
2N/A return (L_INVALID_PATH);
2N/A }
2N/A pathcnt = pathlist.path_count;
2N/A p_on = p_st = 0;
2N/A for (i = 0; i < pathcnt; i++) {
2N/A if (pathlist.path_info[i].path_state < MAXPATHSTATE) {
2N/A if (pathlist.path_info[i].path_state ==
2N/A MDI_PATHINFO_STATE_ONLINE) {
2N/A p_on = i;
2N/A break;
2N/A } else if (pathlist.path_info[i].path_state ==
2N/A MDI_PATHINFO_STATE_STANDBY) {
2N/A p_st = i;
2N/A }
2N/A }
2N/A }
2N/A if (p_on == i) {
2N/A /* on_line path */
2N/A (void) strcpy(drvr_path,
2N/A pathlist.path_info[p_on].path_hba);
2N/A (void) strncpy(pwwn1,
2N/A pathlist.path_info[p_on].path_addr,
2N/A WWN_S_LEN - 1);
2N/A pwwn1[WWN_S_LEN - 1] = '\0';
2N/A } else {
2N/A /* standby or path0 */
2N/A (void) strcpy(drvr_path,
2N/A pathlist.path_info[p_st].path_hba);
2N/A (void) strncpy(pwwn1,
2N/A pathlist.path_info[p_st].path_addr,
2N/A WWN_S_LEN - 1);
2N/A pwwn1[WWN_S_LEN - 1] = '\0';
2N/A }
2N/A free(pathlist.path_info);
2N/A (void) strcat(drvr_path, FC_CTLR);
2N/A }
2N/A if ((map_root = g_dev_map_init(drvr_path, &err,
2N/A MAP_XPORT_PROP_ONLY)) == NULL) {
2N/A return (err);
2N/A }
2N/A
2N/A if ((err = g_get_map_topology(map_root, &hba_port_top)) != 0) {
2N/A g_dev_map_fini(map_root);
2N/A return (err);
2N/A }
2N/A
2N/A if (strstr(path_phys, SCSI_VHCI)) {
2N/A char_ptr = pwwn1;
2N/A } else {
2N/A /*
2N/A * Format of WWN is
2N/A * ssd@w2200002037000f96,0:a,raw
2N/A */
2N/A if (*char_ptr != 'w') {
2N/A g_dev_map_fini(map_root);
2N/A return (L_INVLD_WWN_FORMAT);
2N/A }
2N/A char_ptr++;
2N/A }
2N/A pwwn = strtoull(char_ptr, &ptr, 16);
2N/A if (ptr == char_ptr) {
2N/A g_dev_map_fini(map_root);
2N/A return (L_NO_WWN_FOUND_IN_PATH);
2N/A }
2N/A P_DPRINTF(" g_get_wwn: Looking for WWN "
2N/A "0x%llx\n", pwwn);
2N/A
2N/A if (((map_dev = g_get_first_dev(map_root, &err)) == NULL) &&
2N/A (err != L_NO_SUCH_DEV_FOUND)) {
2N/A g_dev_map_fini(map_root);
2N/A return (err);
2N/A }
2N/A
2N/A while (map_dev) {
2N/A if ((err = g_dev_prop_lookup_bytes(map_dev,
2N/A PORT_WWN_PROP, &count, &port_wwn_byte)) != 0) {
2N/A g_dev_map_fini(map_root);
2N/A return (err);
2N/A }
2N/A if ((err = g_dev_prop_lookup_bytes(map_dev,
2N/A NODE_WWN_PROP, &count, &node_wwn_byte)) != 0) {
2N/A g_dev_map_fini(map_root);
2N/A return (err);
2N/A }
2N/A
2N/A if (pwwn == wwnConversion(port_wwn_byte) && found != 1) {
2N/A found = 1;
2N/A memcpy(port_wwn, port_wwn_byte, FC_WWN_SIZE);
2N/A memcpy(node_wwn, node_wwn_byte, FC_WWN_SIZE);
2N/A if ((err = g_dev_prop_lookup_ints(
2N/A map_dev, PORT_ADDR_PROP, &port_addr)) != 0) {
2N/A g_dev_map_fini(map_root);
2N/A return (err);
2N/A }
2N/A *al_pa = *port_addr;
2N/A }
2N/A add_wwn_entry(wwn_list_found, port_wwn_byte,
2N/A node_wwn_byte);
2N/A
2N/A if (((map_dev = g_get_next_dev(map_dev, &err)) == NULL) &&
2N/A (err != L_NO_SUCH_DEV_FOUND)) {
2N/A g_dev_map_fini(map_root);
2N/A return (err);
2N/A }
2N/A }
2N/A if (!found) {
2N/A g_dev_map_fini(map_root);
2N/A return (L_NO_LOOP_ADDRS_FOUND);
2N/A }
2N/A
2N/A g_dev_map_fini(map_root);
2N/A if (env != NULL) {
2N/A end_time = gethrtime();
2N/A fprintf(stdout, " get_wwns: "
2N/A "\t\tTime = %lld millisec\n",
2N/A (end_time - start_time)/1000000);
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Get device World Wide Name and AL_PA for device at path
2N/A *
2N/A * RETURN: 0 O.K.
2N/A *
2N/A * INPUTS:
2N/A * - path_phys must be of a device, either an IB or disk.
2N/A */
2N/Aint
2N/Ag_get_wwn(char *path_phys, uchar_t port_wwn[], uchar_t node_wwn[],
2N/A int *al_pa, int verbose)
2N/A{
2N/A struct wwn_list_found_struct *wwn_list_found = NULL;
2N/A int ret;
2N/A
2N/A /* return invalid path if the argument is NULL */
2N/A if (path_phys == NULL) {
2N/A return (L_INVALID_PATH);
2N/A }
2N/A /* return invalid arg if the argument is NULL */
2N/A if ((port_wwn == NULL) || (node_wwn == NULL) || (al_pa == NULL)) {
2N/A return (L_INVALID_ARG);
2N/A }
2N/A
2N/A ret = get_wwns(path_phys, port_wwn, node_wwn, al_pa, &wwn_list_found);
2N/A g_free_wwn_list_found(&wwn_list_found);
2N/A return (ret);
2N/A}
2N/A
2N/Aint
2N/Ag_get_serial_number(char *path, uchar_t *serial_number,
2N/A size_t *serial_number_len)
2N/A{
2N/A int fd, status = 0;
2N/A L_inquiry80 inq80;
2N/A
2N/A /* return invalid path if path is NULL */
2N/A if (path == NULL) {
2N/A return (L_INVALID_PATH);
2N/A }
2N/A /* return invalid arg if serial_number is NULL */
2N/A if (serial_number == NULL) {
2N/A return (L_INVALID_ARG);
2N/A }
2N/A
2N/A P_DPRINTF(" g_get_serial_number: path: %s\n", path);
2N/A if ((fd = g_object_open(path, O_NDELAY | O_RDONLY)) == -1) {
2N/A return (L_OPEN_PATH_FAIL);
2N/A }
2N/A /*
2N/A * Call the inquiry cmd on page 0x80 only if the vendor
2N/A * supports page 0x80.
2N/A */
2N/A if ((g_find_supported_inq_page(fd, 0x80))) {
2N/A /*
2N/A * Let's retrieve the serial number from page 0x80
2N/A * and store it in the inquiry structure
2N/A */
2N/A status = g_scsi_inquiry_cmd80(fd,
2N/A (uchar_t *)&inq80,
2N/A sizeof (struct l_inquiry80_struct));
2N/A if (status == 0) {
2N/A if (*serial_number_len > inq80.inq_page_len)
2N/A *serial_number_len = inq80.inq_page_len;
2N/A strncpy((char *)serial_number, (char *)inq80.inq_serial,
2N/A *serial_number_len);
2N/A } else {
2N/A char unavail[] = "Unavailable";
2N/A status = 0;
2N/A if (*serial_number_len > strlen(unavail))
2N/A *serial_number_len = strlen(unavail);
2N/A strncpy((char *)serial_number, unavail,
2N/A *serial_number_len);
2N/A }
2N/A } else {
2N/A /*
2N/A * page 0x80 is not supported, so print the
2N/A * appropriate message.
2N/A */
2N/A char unsupp[] = "Unsupported";
2N/A if (*serial_number_len > strlen(unsupp))
2N/A *serial_number_len = strlen(unsupp);
2N/A strncpy((char *)serial_number, unsupp,
2N/A *serial_number_len);
2N/A }
2N/A (void) close(fd);
2N/A return (status);
2N/A}
2N/A
2N/Aint
2N/Ag_get_inquiry(char *path, L_inquiry *l_inquiry)
2N/A{
2N/A int fd, status;
2N/A
2N/A /* return invalid path if path is NULL */
2N/A if (path == NULL) {
2N/A return (L_INVALID_PATH);
2N/A }
2N/A /* return invalid arg if l_inquiry is NULL */
2N/A if (l_inquiry == NULL) {
2N/A return (L_INVALID_ARG);
2N/A }
2N/A
2N/A P_DPRINTF(" g_get_inquiry: path: %s\n", path);
2N/A if ((fd = g_object_open(path, O_NDELAY | O_RDONLY)) == -1)
2N/A return (L_OPEN_PATH_FAIL);
2N/A status = g_scsi_inquiry_cmd(fd,
2N/A (uchar_t *)l_inquiry, sizeof (struct l_inquiry_struct));
2N/A
2N/A (void) close(fd);
2N/A return (status);
2N/A}
2N/A
2N/A/*
2N/A * Function to retrieve inquiry page 0x80 from the device
2N/A */
2N/Astatic int
2N/Ag_scsi_inquiry_cmd80(int fd, uchar_t *buf_ptr, int buf_len)
2N/A{
2N/A struct uscsi_cmd ucmd;
2N/A my_cdb_g0 cdb = {SCMD_INQUIRY, 0x1, 0x80, 0, 0x10, 0};
2N/A struct scsi_extended_sense sense;
2N/A
2N/A (void) memset(buf_ptr, 0, buf_len);
2N/A (void) memset((char *)&ucmd, 0, sizeof (ucmd));
2N/A cdb.count = (uchar_t)buf_len;
2N/A ucmd.uscsi_cdb = (caddr_t)&cdb;
2N/A ucmd.uscsi_cdblen = CDB_GROUP0;
2N/A ucmd.uscsi_bufaddr = (caddr_t)buf_ptr;
2N/A ucmd.uscsi_buflen = buf_len;
2N/A ucmd.uscsi_rqbuf = (caddr_t)&sense;
2N/A ucmd.uscsi_rqlen = sizeof (struct scsi_extended_sense);
2N/A ucmd.uscsi_timeout = 60;
2N/A return (cmd(fd, &ucmd, USCSI_READ | USCSI_SILENT));
2N/A}
2N/A
2N/A/*
2N/A * Function to determine if the given page is supported by vendor.
2N/A */
2N/Astatic int
2N/Ag_find_supported_inq_page(int fd, int page_num)
2N/A{
2N/A struct uscsi_cmd ucmd;
2N/A my_cdb_g0 cdb = {SCMD_INQUIRY, 0x1, 0, 0, 0xff, 0};
2N/A struct scsi_extended_sense sense;
2N/A L_inquiry00 inq00;
2N/A uchar_t *data;
2N/A int status = 0;
2N/A int index;
2N/A
2N/A (void) memset((char *)&ucmd, 0, sizeof (ucmd));
2N/A cdb.count = (uchar_t)(sizeof (L_inquiry00));
2N/A ucmd.uscsi_cdb = (caddr_t)&cdb;
2N/A ucmd.uscsi_cdblen = CDB_GROUP0;
2N/A ucmd.uscsi_bufaddr = (caddr_t)&inq00;
2N/A ucmd.uscsi_buflen = sizeof (inq00);
2N/A ucmd.uscsi_rqbuf = (caddr_t)&sense;
2N/A ucmd.uscsi_rqlen = sizeof (struct scsi_extended_sense);
2N/A ucmd.uscsi_timeout = 60;
2N/A status = cmd(fd, &ucmd, USCSI_READ | USCSI_SILENT);
2N/A if (status) {
2N/A return (0);
2N/A }
2N/A data = (uchar_t *)&inq00;
2N/A for (index = 4; (index <= inq00.len+3)&&
2N/A (data[index] <= page_num); index ++) {
2N/A if (data[index] == page_num) {
2N/A return (1);
2N/A }
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Ag_get_perf_statistics(char *path, uchar_t *perf_ptr)
2N/A{
2N/A int fd;
2N/A
2N/A P_DPRINTF(" g_get_perf_statistics: Get Performance Statistics:"
2N/A "\n Path:%s\n",
2N/A path);
2N/A
2N/A /* initialize tables */
2N/A (void) memset(perf_ptr, 0, sizeof (int));
2N/A
2N/A /* open controller */
2N/A if ((fd = g_object_open(path, O_NDELAY | O_RDONLY)) == -1)
2N/A return (L_OPEN_PATH_FAIL);
2N/A
2N/A
2N/A /* update parameters in the performance table */
2N/A
2N/A /* get the period in seconds */
2N/A
2N/A
2N/A (void) close(fd);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A
2N/Aint
2N/Ag_start(char *path)
2N/A{
2N/A int status;
2N/A int fd;
2N/A
2N/A P_DPRINTF(" g_start: Start: Path %s\n", path);
2N/A if ((fd = g_object_open(path, O_NDELAY | O_RDONLY)) == -1)
2N/A return (L_OPEN_PATH_FAIL);
2N/A status = g_scsi_start_cmd(fd);
2N/A (void) close(fd);
2N/A return (status);
2N/A}
2N/A
2N/Aint
2N/Ag_stop(char *path, int immediate_flag)
2N/A{
2N/A int status, fd;
2N/A
2N/A P_DPRINTF(" g_stop: Stop: Path %s\n", path);
2N/A if ((fd = g_object_open(path, O_NDELAY | O_RDONLY)) == -1)
2N/A return (errno);
2N/A status = g_scsi_stop_cmd(fd, immediate_flag);
2N/A (void) close(fd);
2N/A return (status);
2N/A}
2N/A
2N/Aint
2N/Ag_reserve(char *path)
2N/A{
2N/A int fd, status;
2N/A
2N/A P_DPRINTF(" g_reserve: Reserve: Path %s\n", path);
2N/A if ((fd = g_object_open(path, O_NDELAY | O_RDONLY)) == -1)
2N/A return (L_OPEN_PATH_FAIL);
2N/A status = g_scsi_reserve_cmd(fd);
2N/A (void) close(fd);
2N/A return (status);
2N/A}
2N/A
2N/Aint
2N/Ag_release(char *path)
2N/A{
2N/A int fd, status;
2N/A
2N/A P_DPRINTF(" g_release: Release: Path %s\n", path);
2N/A if ((fd = g_object_open(path, O_NDELAY | O_RDONLY)) == -1)
2N/A return (L_OPEN_PATH_FAIL);
2N/A status = g_scsi_release_cmd(fd);
2N/A (void) close(fd);
2N/A return (status);
2N/A}
2N/A
2N/Astatic char
2N/Actoi(char c)
2N/A{
2N/A if ((c >= '0') && (c <= '9'))
2N/A c -= '0';
2N/A else if ((c >= 'A') && (c <= 'F'))
2N/A c = c - 'A' + 10;
2N/A else if ((c >= 'a') && (c <= 'f'))
2N/A c = c - 'a' + 10;
2N/A else
2N/A c = -1;
2N/A return (c);
2N/A}
2N/A
2N/Aint
2N/Ag_string_to_wwn(uchar_t *wwn, uchar_t *wwnp)
2N/A{
2N/A int i;
2N/A char c, c1;
2N/A
2N/A *wwnp++ = 0;
2N/A *wwnp++ = 0;
2N/A for (i = 0; i < WWN_SIZE - 2; i++, wwnp++) {
2N/A c = ctoi(*wwn++);
2N/A c1 = ctoi(*wwn++);
2N/A if (c == -1 || c1 == -1)
2N/A return (-1);
2N/A *wwnp = ((c << 4) + c1);
2N/A }
2N/A
2N/A return (0);
2N/A
2N/A}
2N/A
2N/A/*
2N/A * Converts a string of WWN ASCII characters to a
2N/A * binary representation.
2N/A *
2N/A * Input: string - pointer to uchar_t array
2N/A * WWN in ASCII
2N/A * length: 16 bytes
2N/A * Output: wwn - pointer to uchar_t array
2N/A * containing WWN result
2N/A * length: 8 bytes
2N/A * Returns:
2N/A * non-zero on error
2N/A * zero on success
2N/A */
2N/Aint
2N/Astring_to_wwn(uchar_t *string, uchar_t *wwn)
2N/A{
2N/A int i;
2N/A char c, c1;
2N/A uchar_t *wwnp;
2N/A
2N/A wwnp = wwn;
2N/A
2N/A for (i = 0; i < WWN_SIZE; i++, wwnp++) {
2N/A
2N/A c = ctoi(*string++);
2N/A c1 = ctoi(*string++);
2N/A if (c == -1 || c1 == -1)
2N/A return (-1);
2N/A *wwnp = ((c << 4) + c1);
2N/A }
2N/A
2N/A return (0);
2N/A
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Get multiple paths to a given device port.
2N/A * INPUTS:
2N/A * port WWN string.
2N/A */
2N/Aint
2N/Ag_get_port_multipath(char *port_wwn_s, struct dlist **dlh, int verbose)
2N/A{
2N/A int err;
2N/A WWN_list *wwn_list, *wwn_list_ptr;
2N/A struct dlist *dlt, *dl;
2N/A
2N/A
2N/A /* Initialize list structures. */
2N/A dl = *dlh = dlt = (struct dlist *)NULL;
2N/A wwn_list = wwn_list_ptr = NULL;
2N/A
2N/A H_DPRINTF(" g_get_port_multipath: Looking for multiple paths for"
2N/A " device with\n port WWW:"
2N/A "%s\n", port_wwn_s);
2N/A
2N/A if (err = g_get_wwn_list(&wwn_list, verbose)) {
2N/A return (err);
2N/A }
2N/A
2N/A for (wwn_list_ptr = wwn_list; wwn_list_ptr != NULL;
2N/A wwn_list_ptr = wwn_list_ptr->wwn_next) {
2N/A if (strcmp(port_wwn_s, wwn_list_ptr->port_wwn_s) == 0) {
2N/A if ((dl = (struct dlist *)
2N/A g_zalloc(sizeof (struct dlist))) == NULL) {
2N/A while (*dlh != NULL) {
2N/A dl = (*dlh)->next;
2N/A (void) g_destroy_data(*dlh);
2N/A *dlh = dl;
2N/A }
2N/A (void) g_free_wwn_list(&wwn_list);
2N/A return (L_MALLOC_FAILED);
2N/A }
2N/A H_DPRINTF(" g_get_port_multipath:"
2N/A " Found multipath:\n %s\n",
2N/A wwn_list_ptr->physical_path);
2N/A dl->dev_path = strdup(wwn_list_ptr->physical_path);
2N/A dl->logical_path = strdup(wwn_list_ptr->logical_path);
2N/A if (*dlh == NULL) {
2N/A *dlh = dlt = dl;
2N/A } else {
2N/A dlt->next = dl;
2N/A dl->prev = dlt;
2N/A dlt = dl;
2N/A }
2N/A }
2N/A }
2N/A (void) g_free_wwn_list(&wwn_list);
2N/A return (0);
2N/A}
2N/A
2N/A
2N/A
2N/A/*
2N/A * Get multiple paths to a given disk/tape device.
2N/A * The arg: devpath should be the physical path to device.
2N/A *
2N/A * OUTPUT:
2N/A * multipath_list points to a list of multiple paths to the device.
2N/A * NOTE: The caller must free the allocated list (dlist).
2N/A *
2N/A * RETURNS:
2N/A * 0 if O.K.
2N/A * non-zero otherwise
2N/A */
2N/Aint
2N/Ag_get_multipath(char *devpath, struct dlist **multipath_list,
2N/A struct wwn_list_struct *wwn_list, int verbose)
2N/A{
2N/A int err;
2N/A
2N/A H_DPRINTF(" g_get_multipath: Looking for multiple paths for"
2N/A " device at path: %s\n", devpath);
2N/A
2N/A /* return invalid path if devpath is NULL */
2N/A if (devpath == NULL) {
2N/A return (L_INVALID_PATH);
2N/A }
2N/A /* return invalid arg if argument is NULL */
2N/A if ((multipath_list == NULL) || (wwn_list == NULL)) {
2N/A return (L_INVALID_ARG);
2N/A }
2N/A
2N/A if (strstr(devpath, DRV_NAME_SSD) != NULL) {
2N/A err = get_multipath_disk(devpath, multipath_list, wwn_list);
2N/A } else {
2N/A err = get_multipath(devpath, multipath_list, wwn_list);
2N/A }
2N/A
2N/A return (err);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Returns multipath information for a ssd device.
2N/A * Inputs:
2N/A * devpath: device path to for requested multipath info
2N/A * wwn_list: returned from g_get_wwn_list or devices_get_all
2N/A * Output:
2N/A * multipath_list: dlist list of paths
2N/A * Returns:
2N/A * 0 on success
2N/A * non-zero on failure
2N/A */
2N/Aint
2N/Aget_multipath_disk(char *devpath, struct dlist **multipath_list,
2N/A struct wwn_list_struct *wwn_list)
2N/A{
2N/A WWN_list *wwn_list_ptr;
2N/A struct dlist *dl = NULL, *dlt = NULL;
2N/A ddi_devid_t devid = NULL;
2N/A int err;
2N/A di_node_t root;
2N/A struct mplist_struct *mplistp = NULL, *mplisth = NULL;
2N/A
2N/A if (wwn_list == NULL || multipath_list == NULL || devpath == NULL) {
2N/A return (L_NULL_WWN_LIST);
2N/A }
2N/A
2N/A if ((root = di_init("/", DINFOCPYALL)) == DI_NODE_NIL) {
2N/A return (L_DEV_SNAPSHOT_FAILED);
2N/A }
2N/A
2N/A if ((err = g_devid_get(devpath, &devid, root, SSD_DRVR_NAME)) != 0) {
2N/A di_fini(root);
2N/A return (err);
2N/A }
2N/A
2N/A *multipath_list = (struct dlist *)NULL;
2N/A if ((err = devid_get_all(devid, root, SSD_DRVR_NAME, &mplisth)) != 0) {
2N/A di_fini(root);
2N/A return (err);
2N/A }
2N/A
2N/A if (mplisth == NULL) {
2N/A di_fini(root);
2N/A return (L_NULL_WWN_LIST);
2N/A }
2N/A
2N/A for (wwn_list_ptr = wwn_list; wwn_list_ptr != NULL;
2N/A wwn_list_ptr = wwn_list_ptr->wwn_next) {
2N/A /*
2N/A * When a path is found from the list, load the logical
2N/A * and physical dev path
2N/A */
2N/A for (mplistp = mplisth; mplistp != NULL;
2N/A mplistp = mplistp->next) {
2N/A if (strncmp(mplistp->devpath,
2N/A wwn_list_ptr->physical_path,
2N/A strlen(mplistp->devpath)) == 0) {
2N/A
2N/A /* Load multipath list */
2N/A if ((dl = (struct dlist *)
2N/A calloc(1, sizeof (struct dlist))) == NULL) {
2N/A while (*multipath_list != NULL) {
2N/A dl = dlt->next;
2N/A g_destroy_data(dlt);
2N/A dlt = dl;
2N/A }
2N/A di_fini(root);
2N/A return (L_MALLOC_FAILED);
2N/A }
2N/A H_DPRINTF(
2N/A " g_get_multipath: Found multipath=%s\n",
2N/A wwn_list_ptr->physical_path);
2N/A dl->logical_path =
2N/A strdup(wwn_list_ptr->logical_path);
2N/A dl->dev_path =
2N/A strdup(wwn_list_ptr->physical_path);
2N/A if (*multipath_list == NULL) {
2N/A *multipath_list = dlt = dl;
2N/A } else {
2N/A dlt->next = dl;
2N/A dl->prev = dlt;
2N/A dlt = dl;
2N/A }
2N/A }
2N/A }
2N/A }
2N/A di_fini(root);
2N/A mplist_free(mplisth);
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Aget_multipath(char *devpath, struct dlist **multipath_list,
2N/A struct wwn_list_struct *wwn_list)
2N/A{
2N/A WWN_list *wwn_list_ptr;
2N/A struct dlist *dl, *dlt;
2N/A char path[MAXPATHLEN], m_phys_path[MAXPATHLEN], *ptr;
2N/A int len;
2N/A int lun_a = -1;
2N/A char node_wwn_s[WWN_S_LEN];
2N/A
2N/A if (devpath == NULL) {
2N/A return (L_INVALID_PATH);
2N/A }
2N/A
2N/A /* Strip partition information. */
2N/A if ((ptr = strrchr(devpath, ':')) != NULL) {
2N/A len = strlen(devpath) - strlen(ptr);
2N/A (void) strncpy(path, devpath, len);
2N/A path[len] = '\0';
2N/A } else {
2N/A (void) strcpy(path, devpath);
2N/A }
2N/A
2N/A *multipath_list = dl = dlt = (struct dlist *)NULL;
2N/A
2N/A
2N/A if (wwn_list == NULL) {
2N/A return (L_NULL_WWN_LIST);
2N/A }
2N/A
2N/A for (*node_wwn_s = NULL, wwn_list_ptr = wwn_list;
2N/A wwn_list_ptr != NULL;
2N/A wwn_list_ptr = wwn_list_ptr->wwn_next) {
2N/A
2N/A if ((ptr = strrchr(wwn_list_ptr->physical_path, ':')) != NULL) {
2N/A len = strlen(wwn_list_ptr->physical_path) - strlen(ptr);
2N/A (void) strncpy(m_phys_path, wwn_list_ptr->physical_path,
2N/A len);
2N/A m_phys_path[len] = '\0';
2N/A } else {
2N/A (void) strcpy(m_phys_path, wwn_list_ptr->physical_path);
2N/A }
2N/A
2N/A if (strcasecmp(m_phys_path, path) == 0) {
2N/A (void) strcpy(node_wwn_s, wwn_list_ptr->node_wwn_s);
2N/A break;
2N/A }
2N/A }
2N/A
2N/A if (*node_wwn_s == NULL) {
2N/A H_DPRINTF("node_wwn_s is NULL!\n");
2N/A return (L_NO_NODE_WWN_IN_WWNLIST);
2N/A }
2N/A
2N/A lun_a = g_get_lun_number(wwn_list_ptr->physical_path);
2N/A
2N/A for (wwn_list_ptr = wwn_list; wwn_list_ptr != NULL;
2N/A wwn_list_ptr = wwn_list_ptr->wwn_next) {
2N/A if ((strcmp(node_wwn_s, wwn_list_ptr->node_wwn_s) == 0) &&
2N/A ((lun_a < 0) || (lun_a ==
2N/A g_get_lun_number(wwn_list_ptr->physical_path)))) {
2N/A
2N/A if ((dl = (struct dlist *)
2N/A g_zalloc(sizeof (struct dlist))) == NULL) {
2N/A while (*multipath_list != NULL) {
2N/A dl = dlt->next;
2N/A (void) g_destroy_data(dlt);
2N/A dlt = dl;
2N/A }
2N/A return (L_MALLOC_FAILED);
2N/A }
2N/A H_DPRINTF(" g_get_multipath: Found multipath=%s\n",
2N/A wwn_list_ptr->physical_path);
2N/A dl->dev_path = strdup(wwn_list_ptr->physical_path);
2N/A dl->logical_path = strdup(wwn_list_ptr->logical_path);
2N/A if (*multipath_list == NULL) {
2N/A *multipath_list = dlt = dl;
2N/A } else {
2N/A dlt->next = dl;
2N/A dl->prev = dlt;
2N/A dlt = dl;
2N/A }
2N/A }
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Free a multipath list
2N/A *
2N/A */
2N/Avoid
2N/Ag_free_multipath(struct dlist *dlh)
2N/A{
2N/A struct dlist *dl;
2N/A
2N/A while (dlh != NULL) {
2N/A dl = dlh->next;
2N/A if (dlh->dev_path != NULL)
2N/A (void) g_destroy_data(dlh->dev_path);
2N/A if (dlh->logical_path != NULL)
2N/A (void) g_destroy_data(dlh->logical_path);
2N/A (void) g_destroy_data(dlh);
2N/A dlh = dl;
2N/A }
2N/A}
2N/A
2N/A
2N/A
2N/A/*
2N/A * Get the path to the nexus (HBA) driver.
2N/A * This assumes the path looks something like this:
2N/A * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0/ses@e,0:0
2N/A * or maybe this
2N/A * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@1,0
2N/A * or
2N/A * /devices/sbus@1f,0/SUNW,socal@1,0
2N/A * or
2N/A * /devices/sbus@1f,0/SUNW,socal@1,0:1
2N/A * or
2N/A * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0:devctl
2N/A * (or "qlc" instead of "socal" and "fp" for "sf")
2N/A *
2N/A * Which should resolve to a path like this:
2N/A * /devices/sbus@1f,0/SUNW,socal@1,0:1
2N/A * or
2N/A * /devices/pci@6,2000/pci@2/SUNW,qlc@5
2N/A *
2N/A * or
2N/A * /devices/pci@4,2000/scsi@1/ses@w50800200000000d2,0:0
2N/A * which should resolve to
2N/A * /devices/pci@4,2000/scsi@1:devctl
2N/A */
2N/Aint
2N/Ag_get_nexus_path(char *path_phys, char **nexus_path)
2N/A{
2N/A uchar_t port = 0;
2N/A int port_flag = 0, i = 0, pathcnt = 1;
2N/A char *char_ptr;
2N/A char drvr_path[MAXPATHLEN];
2N/A char buf[MAXPATHLEN];
2N/A char temp_buf[MAXPATHLEN];
2N/A struct stat stbuf;
2N/A uint_t path_type;
2N/A mp_pathlist_t pathlist;
2N/A int p_on = 0, p_st = 0;
2N/A
2N/A /* return invalid path if the path_phys is NULL */
2N/A if (path_phys == NULL) {
2N/A return (L_INVALID_PATH);
2N/A }
2N/A
2N/A *nexus_path = NULL;
2N/A (void) strcpy(drvr_path, path_phys);
2N/A
2N/A if (strstr(path_phys, SCSI_VHCI)) {
2N/A if (g_get_pathlist(drvr_path, &pathlist)) {
2N/A return (L_INVALID_PATH);
2N/A }
2N/A pathcnt = pathlist.path_count;
2N/A p_on = p_st = 0;
2N/A for (i = 0; i < pathcnt; i++) {
2N/A if (pathlist.path_info[i].path_state < MAXPATHSTATE) {
2N/A if (pathlist.path_info[i].path_state ==
2N/A MDI_PATHINFO_STATE_ONLINE) {
2N/A p_on = i;
2N/A break;
2N/A } else if (pathlist.path_info[i].path_state ==
2N/A MDI_PATHINFO_STATE_STANDBY) {
2N/A p_st = i;
2N/A }
2N/A }
2N/A }
2N/A if (pathlist.path_info[p_on].path_state ==
2N/A MDI_PATHINFO_STATE_ONLINE) {
2N/A /* on_line path */
2N/A (void) strcpy(drvr_path,
2N/A pathlist.path_info[p_on].path_hba);
2N/A } else {
2N/A /* standby or path0 */
2N/A (void) strcpy(drvr_path,
2N/A pathlist.path_info[p_st].path_hba);
2N/A }
2N/A free(pathlist.path_info);
2N/A (void) strcat(drvr_path, FC_CTLR);
2N/A } else {
2N/A if (strstr(drvr_path, DRV_NAME_SSD) || strstr(drvr_path,
2N/A DRV_NAME_ST) || strstr(drvr_path, SES_NAME)) {
2N/A if ((char_ptr = strrchr(drvr_path, '/')) == NULL) {
2N/A return (L_INVALID_PATH);
2N/A }
2N/A *char_ptr = '\0'; /* Terminate string */
2N/A }
2N/A
2N/A path_type = g_get_path_type(drvr_path);
2N/A
2N/A if (path_type & FC4_SF_XPORT) {
2N/A
2N/A /* sf driver in path so capture the port # */
2N/A if ((char_ptr = strstr(drvr_path, "sf@")) == NULL) {
2N/A return (L_INVALID_PATH);
2N/A }
2N/A port = atoi(char_ptr + 3);
2N/A if (port > 1) {
2N/A return (L_INVLD_PORT_IN_PATH);
2N/A }
2N/A
2N/A if ((char_ptr = strrchr(drvr_path, '/')) == NULL) {
2N/A return (L_INVALID_PATH);
2N/A }
2N/A *char_ptr = '\0'; /* Terminate string */
2N/A port_flag++;
2N/A
2N/A L_DPRINTF(" g_get_nexus_path:"
2N/A " sf driver in path so use port #%d.\n",
2N/A port);
2N/A } else if (path_type & FC_GEN_XPORT) {
2N/A /*
2N/A * check to see if it 3rd party vendor FCA.
2N/A * if it is return error for this operation since
2N/A * we don't know how they creates FCA port related minor node.
2N/A *
2N/A * As of now there is no supported operation on FCA node so
2N/A * this should be okay.
2N/A */
2N/A if ((path_type & FC_FCA_MASK) == FC_FCA_MASK) {
2N/A return (L_INVALID_PATH_TYPE);
2N/A }
2N/A /*
2N/A * For current Sun FCA driver, appending
2N/A * port # doesn't work. Just remove transport layer from
2N/A * input path.
2N/A */
2N/A if ((char_ptr = strstr(drvr_path, "/fp@")) == NULL) {
2N/A return (L_INVALID_PATH);
2N/A }
2N/A *char_ptr = '\0'; /* Terminate string */
2N/A }
2N/A
2N/A if (stat(drvr_path, &stbuf) != 0) {
2N/A return (L_LSTAT_ERROR);
2N/A }
2N/A
2N/A if ((stbuf.st_mode & S_IFMT) == S_IFDIR) {
2N/A /*
2N/A * Found a directory.
2N/A * Now append a port number or devctl to the path.
2N/A */
2N/A if (port_flag) {
2N/A /* append port */
2N/A (void) sprintf(buf, ":%d", port);
2N/A } else {
2N/A /* Try adding port 0 and see if node exists. */
2N/A (void) sprintf(temp_buf, "%s:0", drvr_path);
2N/A if (stat(temp_buf, &stbuf) != 0) {
2N/A /*
2N/A * Path we guessed at does not
2N/A * exist so it may be a driver
2N/A * that ends in :devctl.
2N/A */
2N/A (void) sprintf(buf, ":devctl");
2N/A } else {
2N/A /*
2N/A * The path that was entered
2N/A * did not include a port number
2N/A * so the port was set to zero, and
2N/A * then checked. The default path
2N/A * did exist.
2N/A */
2N/A ER_DPRINTF("Since a complete path"
2N/A " was not supplied "
2N/A "a default path is being"
2N/A " used:\n %s\n",
2N/A temp_buf);
2N/A (void) sprintf(buf, ":0");
2N/A }
2N/A }
2N/A
2N/A (void) strcat(drvr_path, buf);
2N/A }
2N/A
2N/A }
2N/A *nexus_path = g_alloc_string(drvr_path);
2N/A L_DPRINTF(" g_get_nexus_path: Nexus path = %s\n", drvr_path);
2N/A return (0);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Get the FC topology for the input device or nexus(HBA) path.
2N/A *
2N/A * The routine calls g_get_path_type to determine the stack of
2N/A * the input path.
2N/A *
2N/A * If it a socal path
2N/A * it returns FC_TOP_PRIVATE_LOOP
2N/A * else
2N/A * calls fc_get_topology ioctl to
2N/A * get the fp topolgy from the driver.
2N/A *
2N/A * INPUTS:
2N/A * path - a string of device path, transport path.
2N/A * NOTE: "path" SHOULD NOT BE OPEN BEFORE CALLING
2N/A * THIS FUNCTION BECAUSE THIS FUNCTION DOES
2N/A * AN "O_EXCL" OPEN.
2N/A * port_top - a pointer to the toplogy type.
2N/A *
2N/A * RETURNS:
2N/A * 0 if there is no error.
2N/A * error code.
2N/A *
2N/A * The input path is expected to be something like below:
2N/A * 1)
2N/A * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0/ses@e,0:0
2N/A * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0/ssd@..
2N/A * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@1,0
2N/A * /devices/sbus@1f,0/SUNW,socal@1,0
2N/A * /devices/sbus@1f,0/SUNW,socal@1,0:1
2N/A * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0:devctl
2N/A * (or "qlc" instead of "socal" and "fp" for "sf")
2N/A *
2N/A * Which should resolve to a path like this:
2N/A * /devices/sbus@1f,0/SUNW,socal@1,0:1
2N/A * /devices/pci@6,2000/pci@2/SUNW,qlc@5
2N/A *
2N/A * 2)
2N/A * /devices/pci@4,2000/scsi@1/ses@w50800200000000d2,0:0
2N/A * which should resolve to
2N/A * /devices/pci@4,2000/scsi@1:devctl
2N/A *
2N/A * 3) The nexus(hba or nexus) path will get an error only for qlc
2N/A * since the routine need to open fp :devctl node for fcio ioctl.
2N/A * /devices/sbus@1f,0/SUNW,socal@1,0
2N/A * /devices/sbus@1f,0/SUNW,socal@1,0:1
2N/A * /devices/pci@6,2000/pci@2/SUNW,qlc@5 => error
2N/A */
2N/Aint
2N/Ag_get_fca_port_topology(char *path, uint32_t *port_top, int verbose)
2N/A{
2N/A fcio_t fcio;
2N/A int fd, i = 0, pathcnt = 1;
2N/A char drvr_path[MAXPATHLEN];
2N/A char *char_ptr;
2N/A struct stat stbuf;
2N/A uint_t dev_type;
2N/A mp_pathlist_t pathlist;
2N/A int p_on = 0, p_st = 0;
2N/A
2N/A /* return invalid path if the path is NULL */
2N/A if (path == NULL) {
2N/A return (L_INVALID_PATH);
2N/A }
2N/A /* return invalid arg if the argument is NULL */
2N/A if (port_top == NULL) {
2N/A return (L_INVALID_ARG);
2N/A }
2N/A
2N/A (void) strcpy(drvr_path, path);
2N/A if (strstr(path, SCSI_VHCI)) {
2N/A if (g_get_pathlist(drvr_path, &pathlist)) {
2N/A return (L_INVALID_PATH);
2N/A }
2N/A pathcnt = pathlist.path_count;
2N/A p_on = p_st = 0;
2N/A for (i = 0; i < pathcnt; i++) {
2N/A if (pathlist.path_info[i].path_state < MAXPATHSTATE) {
2N/A if (pathlist.path_info[i].path_state ==
2N/A MDI_PATHINFO_STATE_ONLINE) {
2N/A p_on = i;
2N/A break;
2N/A } else if (pathlist.path_info[i].path_state ==
2N/A MDI_PATHINFO_STATE_STANDBY) {
2N/A p_st = i;
2N/A }
2N/A }
2N/A }
2N/A if (pathlist.path_info[p_on].path_state ==
2N/A MDI_PATHINFO_STATE_ONLINE) {
2N/A /* on_line path */
2N/A (void) strcpy(drvr_path,
2N/A pathlist.path_info[p_on].path_hba);
2N/A } else {
2N/A /* standby or path0 */
2N/A (void) strcpy(drvr_path,
2N/A pathlist.path_info[p_st].path_hba);
2N/A }
2N/A free(pathlist.path_info);
2N/A (void) strcat(drvr_path, FC_CTLR);
2N/A } else {
2N/A /*
2N/A * Get the path to the :devctl driver
2N/A *
2N/A * This assumes the path looks something like this:
2N/A * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0/ses@e,0:0
2N/A * or
2N/A * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0
2N/A * or
2N/A * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0:devctl
2N/A * or
2N/A * a 1 level PCI type driver but still :devctl
2N/A * (or "qlc" in the place of "socal" and "fp" for "sf")
2N/A *
2N/A * The dir below doesn't have corresponding :devctl node.
2N/A * /devices/pci@6,2000/pci@2/SUNW,qlc@5
2N/A * /devices/sbus@2,0/SUNW,socal@1,0
2N/A *
2N/A */
2N/A if ((strstr(drvr_path, DRV_NAME_SSD) ||
2N/A strstr(drvr_path, SES_NAME)) ||
2N/A strstr(drvr_path, DRV_NAME_ST)) {
2N/A if ((char_ptr = strrchr(drvr_path, '/')) == NULL) {
2N/A return (L_INVALID_PATH);
2N/A }
2N/A *char_ptr = '\0'; /* Terminate sting */
2N/A /* append controller */
2N/A (void) strcat(drvr_path, FC_CTLR);
2N/A } else {
2N/A if (stat(drvr_path, &stbuf) < 0) {
2N/A return (L_LSTAT_ERROR);
2N/A }
2N/A if ((stbuf.st_mode & S_IFMT) == S_IFDIR) {
2N/A /* append controller */
2N/A (void) strcat(drvr_path, FC_CTLR);
2N/A }
2N/A }
2N/A }
2N/A
2N/A if ((dev_type = g_get_path_type(drvr_path)) == 0) {
2N/A return (L_INVALID_PATH);
2N/A }
2N/A
2N/A if ((dev_type & FC4_XPORT_MASK) || (dev_type & FC4_FCA_MASK)) {
2N/A *port_top = FC_TOP_PRIVATE_LOOP;
2N/A return (0);
2N/A }
2N/A
2N/A /* To contiue the path type should be fp :devctl node */
2N/A if (!(dev_type & FC_XPORT_MASK)) {
2N/A return (L_INVALID_PATH);
2N/A }
2N/A
2N/A if ((fd = g_object_open(drvr_path, O_NDELAY | O_RDONLY)) == -1)
2N/A return (errno);
2N/A
2N/A P_DPRINTF(" g_get_fca_port_topology: Geting topology from:"
2N/A " %s\n", drvr_path);
2N/A
2N/A fcio.fcio_cmd = FCIO_GET_TOPOLOGY;
2N/A fcio.fcio_olen = sizeof (uint32_t);
2N/A fcio.fcio_xfer = FCIO_XFER_READ;
2N/A fcio.fcio_obuf = (caddr_t)port_top;
2N/A if (g_issue_fcio_ioctl(fd, &fcio, verbose) != 0) {
2N/A I_DPRINTF(" FCIO_GET_TOPOLOGY ioctl failed.\n");
2N/A close(fd);
2N/A return (L_FCIO_GET_TOPOLOGY_FAIL);
2N/A }
2N/A close(fd);
2N/A return (0);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * This functions enables or disables a FCA port depending on the
2N/A * argument, cmd, passed to it. If cmd is PORT_OFFLINE, the function
2N/A * tries to disable the port specified by the argument 'phys_path'. If
2N/A * cmd is PORT_ONLINE, the function tries to enable the port specified
2N/A * by the argument 'phys_path'.
2N/A * INPUTS :
2N/A * nexus_port_ptr - Pointer to the nexus path of the FCA port to
2N/A * operate on
2N/A * cmd - PORT_OFFLINE or PORT_ONLINE
2N/A * RETURNS :
2N/A * 0 on success and non-zero otherwise
2N/A */
2N/Astatic int
2N/Ag_set_port_state(char *nexus_port_ptr, int cmd)
2N/A{
2N/A int path_type, fd;
2N/A
2N/A if ((path_type = g_get_path_type(nexus_port_ptr)) == 0) {
2N/A return (L_INVALID_PATH);
2N/A }
2N/A
2N/A if ((fd = g_object_open(nexus_port_ptr, O_NDELAY|O_RDONLY)) == -1) {
2N/A return (L_OPEN_PATH_FAIL);
2N/A }
2N/A
2N/A switch (cmd) {
2N/A case PORT_OFFLINE:
2N/A if (path_type & FC4_SOCAL_FCA) {
2N/A /*
2N/A * Socal/sf drivers -
2N/A * The socal driver currently returns EFAULT
2N/A * even if the ioctl has completed successfully.
2N/A */
2N/A if (ioctl(fd, FCIO_LOOPBACK_INTERNAL,
2N/A NULL) == -1) {
2N/A close(fd);
2N/A return (L_PORT_OFFLINE_FAIL);
2N/A }
2N/A } else {
2N/A /*
2N/A * QLogic card -
2N/A * Can't do much here since the driver currently
2N/A * doesn't support this feature. We'll just fail
2N/A * for now. Support can be added when the driver
2N/A * is enabled with the feature at a later date.
2N/A */
2N/A close(fd);
2N/A return (L_PORT_OFFLINE_UNSUPPORTED);
2N/A }
2N/A break;
2N/A case PORT_ONLINE:
2N/A if (path_type & FC4_SOCAL_FCA) {
2N/A /*
2N/A * Socal/sf drivers
2N/A * The socal driver currently returns EFAULT
2N/A * even if the ioctl has completed successfully.
2N/A */
2N/A if (ioctl(fd, FCIO_NO_LOOPBACK, NULL) == -1) {
2N/A close(fd);
2N/A return (L_PORT_ONLINE_FAIL);
2N/A }
2N/A } else {
2N/A /*
2N/A * QLogic card -
2N/A * Can't do much here since the driver currently
2N/A * doesn't support this feature. We'll just fail
2N/A * for now. Support can be added when the driver
2N/A * is enabled with the feature at a later date.
2N/A */
2N/A close(fd);
2N/A return (L_PORT_ONLINE_UNSUPPORTED);
2N/A }
2N/A break;
2N/A default:
2N/A close(fd);
2N/A return (-1);
2N/A }
2N/A close(fd);
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * The interfaces defined below (g_port_offline() and g_port_online())
2N/A * are what will be exposed to applications. We will hide g_set_port_state().
2N/A * They have to be functions (as against macros) because making them
2N/A * macros will mean exposing g_set_port_state() and we dont want to do that
2N/A */
2N/A
2N/Aint
2N/Ag_port_offline(char *path)
2N/A{
2N/A return (g_set_port_state(path, PORT_OFFLINE));
2N/A}
2N/A
2N/Aint
2N/Ag_port_online(char *path)
2N/A{
2N/A return (g_set_port_state(path, PORT_ONLINE));
2N/A}
2N/A
2N/A/*
2N/A * This function sets the loopback mode for a port on a HBA
2N/A * INPUTS :
2N/A * portpath - Pointer to the path of the FCA port on which to
2N/A * set the loopback mode
2N/A * cmd - EXT_LPBACK
2N/A * INT_LPBACK
2N/A * NO_LPBACK
2N/A * RETURNS :
2N/A * 0 on success and non-zero otherwise
2N/A */
2N/Aint
2N/Ag_loopback_mode(char *portpath, int cmd)
2N/A{
2N/A int path_type, fd;
2N/A
2N/A if ((path_type = g_get_path_type(portpath)) == 0) {
2N/A return (L_INVALID_PATH);
2N/A }
2N/A
2N/A if ((fd = g_object_open(portpath, O_NDELAY|O_RDONLY|O_EXCL)) == -1) {
2N/A return (L_OPEN_PATH_FAIL);
2N/A }
2N/A
2N/A /*
2N/A * The loopback calls are currently not fully supported
2N/A * via fp.
2N/A *
2N/A * A fp based general solution is required to support Leadville FCAs
2N/A * including Qlgc and 3rd party FCA. As of now qlgc provides
2N/A * some diag functions like echo through qlc private ioctl
2N/A * which is not supproted by luxadm and libraries.
2N/A */
2N/A switch (cmd) {
2N/A case EXT_LPBACK:
2N/A if (path_type & FC4_SOCAL_FCA) {
2N/A if (ioctl(fd, FCIO_LOOPBACK_MANUAL,
2N/A NULL) == -1) {
2N/A /* Check for previous mode set */
2N/A if (errno != EALREADY) {
2N/A close(fd);
2N/A return (L_LOOPBACK_FAILED);
2N/A }
2N/A }
2N/A } else {
2N/A /*
2N/A * Well, it wasn't one of the above cards so..
2N/A */
2N/A close(fd);
2N/A return (L_LOOPBACK_UNSUPPORTED);
2N/A }
2N/A break;
2N/A case NO_LPBACK:
2N/A if (path_type & FC4_SOCAL_FCA) {
2N/A if (ioctl(fd, FCIO_NO_LOOPBACK, NULL) == -1) {
2N/A close(fd);
2N/A return (L_LOOPBACK_FAILED);
2N/A }
2N/A } else {
2N/A /*
2N/A * Well, it wasn't one of the above cards so..
2N/A */
2N/A close(fd);
2N/A return (L_LOOPBACK_UNSUPPORTED);
2N/A }
2N/A break;
2N/A case INT_LPBACK:
2N/A if (path_type & FC4_SOCAL_FCA) {
2N/A if (ioctl(fd, FCIO_LOOPBACK_INTERNAL,
2N/A NULL) == -1) {
2N/A /* Check for previous mode set */
2N/A if (errno != EALREADY) {
2N/A close(fd);
2N/A return (L_LOOPBACK_FAILED);
2N/A }
2N/A }
2N/A } else {
2N/A /*
2N/A * Well, it wasn't one of the above cards so..
2N/A */
2N/A close(fd);
2N/A return (L_LOOPBACK_UNSUPPORTED);
2N/A }
2N/A break;
2N/A default:
2N/A close(fd);
2N/A return (L_LOOPBACK_UNSUPPORTED);
2N/A }
2N/A close(fd);
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * g_get_port_state(char *portpath, int port_state)
2N/A * Purpose: Get port state for a path
2N/A * Input: portpath
2N/A * set to path of port
2N/A * Output: port_state
2N/A * Set to one of the following:
2N/A * PORT_CONNECTED
2N/A * PORT_NOTCONNECTED
2N/A * Returns: 0 on success
2N/A * non-zero on failure
2N/A */
2N/Aint
2N/Ag_get_port_state(char *portpath, int *portstate, int verbose)
2N/A{
2N/A int fd, err, num_devices = 0;
2N/A struct lilpmap map;
2N/A uint_t dev_type;
2N/A gfc_dev_t map_root;
2N/A
2N/A
2N/A (void) memset(&map, 0, sizeof (struct lilpmap));
2N/A
2N/A /* return invalid path if portpath is NULL */
2N/A if (portpath == NULL) {
2N/A return (L_INVALID_PATH);
2N/A }
2N/A /* return invalid arg if argument is NULL */
2N/A if ((portpath == NULL) || (portstate == NULL)) {
2N/A return (L_INVALID_ARG);
2N/A }
2N/A
2N/A if ((dev_type = g_get_path_type(portpath)) == 0) {
2N/A return (L_INVALID_PATH);
2N/A }
2N/A
2N/A /*
2N/A * FCIO_GETMAP returns error when there are * no devices attached.
2N/A * ENOMEM is returned when no devices are attached.
2N/A * g_get_first_dev returns NULL without error when there is no
2N/A * devices are attached.
2N/A */
2N/A if (dev_type & FC_FCA_MASK) {
2N/A if ((map_root = g_dev_map_init(portpath, &err,
2N/A MAP_XPORT_PROP_ONLY)) == NULL) {
2N/A return (err);
2N/A }
2N/A
2N/A if (g_get_first_dev(map_root, &err) == NULL) {
2N/A /* no device is found if err == 0 */
2N/A if (err == L_NO_SUCH_DEV_FOUND) {
2N/A *portstate = PORT_NOTCONNECTED;
2N/A }
2N/A g_dev_map_fini(map_root);
2N/A return (0);
2N/A } else {
2N/A /* Device found okay */
2N/A *portstate = PORT_CONNECTED;
2N/A g_dev_map_fini(map_root);
2N/A }
2N/A
2N/A } else {
2N/A /* open controller */
2N/A if ((fd = g_object_open(portpath, O_NDELAY | O_RDONLY)) == -1) {
2N/A return (errno);
2N/A }
2N/A
2N/A /*
2N/A * Note: There is only one error returned by this ioctl. ENOMEM.
2N/A * Hence the lack of return on error.
2N/A */
2N/A if (ioctl(fd, FCIO_GETMAP, &map) != 0) {
2N/A map.lilp_length = 0;
2N/A }
2N/A num_devices = map.lilp_length;
2N/A
2N/A /* Non-Leadville stacks report the FCA in the count */
2N/A *portstate = (num_devices > 1) ? PORT_CONNECTED :
2N/A PORT_NOTCONNECTED;
2N/A (void) close(fd);
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * g_dev_login(char *port_path, la_wwn_t port_wwn)
2N/A * Purpose: port login via g_dev_log_in_out()
2N/A * Input: port_path
2N/A * fc transport port with fabric/public loop topology
2N/A * port_wwn
2N/A * port wwn of device node to login
2N/A *
2N/A * Returns: return code from g_dev_log_in_out()
2N/A */
2N/Aint
2N/Ag_dev_login(char *port_path, la_wwn_t port_wwn)
2N/A{
2N/A return (g_dev_log_in_out(port_path, port_wwn, FCIO_DEV_LOGIN));
2N/A}
2N/A
2N/A
2N/A/*
2N/A * g_dev_logout(char *port_path, la_wwn_t port_wwn)
2N/A * Purpose: port login via g_dev_log_in_out()
2N/A * Input: port_path
2N/A * fc transport port with fabric/public loop topology
2N/A * port_wwn
2N/A * port wwn of device node to logout
2N/A *
2N/A * Returns: return code from g_dev_log_in_out()
2N/A */
2N/Aint
2N/Ag_dev_logout(char *port_path, la_wwn_t port_wwn)
2N/A{
2N/A return (g_dev_log_in_out(port_path, port_wwn, FCIO_DEV_LOGOUT));
2N/A}
2N/A
2N/A
2N/A/*
2N/A * g_dev_log_in_out(char *port_path, la_wwn_t port_wwn, uint16_t cmd)
2N/A * Purpose: port login via FCIO_DEV_LOGOUT and port logout via FCIO_DEV_LOGOUT
2N/A * IOCTL requires EXCLUSIVE open.
2N/A * Input: port_path
2N/A * fc transport port with fabric/public loop topology
2N/A * port_wwn
2N/A * port wwn of device node to logout
2N/A * cmd
2N/A * FCIO_DEV_LOGON or FCIO_DEV_LOGOUT
2N/A *
2N/A * Returns: 0 on success
2N/A * non-zero on failure
2N/A */
2N/Astatic int
2N/Ag_dev_log_in_out(char *port_path, la_wwn_t port_wwn, uint16_t cmd)
2N/A{
2N/A int fd, err;
2N/A uint32_t hba_port_top;
2N/A fcio_t fcio;
2N/A int verbose = 0;
2N/A
2N/A if ((err = g_get_fca_port_topology(port_path,
2N/A &hba_port_top, verbose)) != 0) {
2N/A return (err);
2N/A }
2N/A
2N/A if (!((hba_port_top == FC_TOP_PUBLIC_LOOP) ||
2N/A (hba_port_top == FC_TOP_FABRIC))) {
2N/A return (L_OPNOSUPP_ON_TOPOLOGY);
2N/A }
2N/A
2N/A /* open controller */
2N/A if ((fd = g_object_open(port_path, O_NDELAY | O_RDONLY | O_EXCL)) == -1)
2N/A return (L_OPEN_PATH_FAIL);
2N/A
2N/A /*
2N/A * stores port_wwn to la_wwn_t raw_wwn field
2N/A * and construct fcio structures for FCIO_DEV_LOGIN.
2N/A */
2N/A fcio.fcio_cmd = cmd;
2N/A fcio.fcio_ilen = sizeof (port_wwn);
2N/A fcio.fcio_ibuf = (caddr_t)&port_wwn;
2N/A fcio.fcio_xfer = FCIO_XFER_WRITE;
2N/A fcio.fcio_olen = fcio.fcio_alen = 0;
2N/A fcio.fcio_obuf = fcio.fcio_abuf = NULL;
2N/A if (g_issue_fcio_ioctl(fd, &fcio, verbose) != 0) {
2N/A I_DPRINTF((cmd == FCIO_DEV_LOGIN) ?
2N/A " FCIO_DEV_LOGIN ioctl failed.\n"
2N/A : " FCIO_DEV_LOGOUT ioctl failed.\n");
2N/A (void) close(fd);
2N/A return ((cmd == FCIO_DEV_LOGIN) ?
2N/A L_FCIO_DEV_LOGIN_FAIL
2N/A : L_FCIO_DEV_LOGOUT_FAIL);
2N/A } else {
2N/A (void) close(fd);
2N/A return (0);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * This function will verify if a FC device (represented by input WWN
2N/A * is connected on a FCA port by searching the device list from
2N/A * g_get_dev_list() for a WWN match.
2N/A *
2N/A * input:
2N/A * fca_path: pointer to the physical path string, path to a fp node.
2N/A * possible forms are
2N/A * /devices/pci@1f,2000/pci@1/SUNW,qlc@5/fp@0,0:devctl
2N/A * dev_wwn: WWN string
2N/A * flag: indicate that the input WWN is node or port
2N/A *
2N/A * returned values
2N/A * 0: if a match is found.
2N/A * L_WWN_NOT_FOUND_IN_DEV_LIST: if no match found
2N/A * L_UNEXPECTED_FC_TOPOLOGY: existing error code for an error
2N/A * from the topology checking of the input fca path.
2N/A * L_MALLOC_FAILED: existing error code for allocation eror from the
2N/A * g_get_dev_list().
2N/A * L_FCIO_GETMAP_IOCTL_FAIL: existing error code for an error from the
2N/A * FCIO ioctl called by the g_get_dev_list()
2N/A * -1: other failure
2N/A *
2N/A */
2N/Aint
2N/Ag_wwn_in_dev_list(char *fca_path, la_wwn_t dev_wwn, int flag)
2N/A{
2N/A uint_t dev_type;
2N/A int i, err;
2N/A fc_port_dev_t *dev_list;
2N/A fc_port_dev_t *dev_list_save;
2N/A int num_devices = 0;
2N/A
2N/A if ((dev_type = g_get_path_type(fca_path)) == 0) {
2N/A return (L_INVALID_PATH);
2N/A }
2N/A
2N/A if (!(dev_type & FC_XPORT_MASK)) {
2N/A return (L_INVALID_PATH_TYPE);
2N/A }
2N/A
2N/A if (((err = g_get_dev_list(fca_path, &dev_list, &num_devices))
2N/A != 0) && (err != L_GET_DEV_LIST_ULP_FAILURE)) {
2N/A return (err);
2N/A }
2N/A
2N/A dev_list_save = dev_list;
2N/A
2N/A switch (flag) {
2N/A case MATCH_NODE_WWN:
2N/A for (i = 0; i < num_devices; i++, dev_list++) {
2N/A if (memcmp(dev_list->dev_nwwn.raw_wwn,
2N/A dev_wwn.raw_wwn, FC_WWN_SIZE) == 0) {
2N/A (void) free(dev_list_save);
2N/A return (0);
2N/A }
2N/A }
2N/A (void) free(dev_list_save);
2N/A /* consider a new error code for not found. */
2N/A return (L_WWN_NOT_FOUND_IN_DEV_LIST);
2N/A
2N/A case MATCH_PORT_WWN:
2N/A for (i = 0; i < num_devices; i++, dev_list++) {
2N/A if (memcmp(dev_list->dev_pwwn.raw_wwn,
2N/A dev_wwn.raw_wwn, FC_WWN_SIZE) == 0) {
2N/A (void) free(dev_list_save);
2N/A return (0);
2N/A }
2N/A }
2N/A (void) free(dev_list_save);
2N/A /* consider a new error code for not found. */
2N/A return (L_WWN_NOT_FOUND_IN_DEV_LIST);
2N/A }
2N/A (void) free(dev_list_save);
2N/A return (-1);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * g_get_dev_port_state(char *fca_path, la_wwn_t port_wwn, uint32_t *state)
2N/A * Purpose: get the state of device port login via FCIO_GET_STATE ioctl.
2N/A *
2N/A * Input: fca_path
2N/A * fc transport port with fabric/public loop topology
2N/A * port_wwn
2N/A * port wwn of device node to logout
2N/A * state
2N/A * port login or not
2N/A *
2N/A * Returns: 0 on success
2N/A * non-zero on failure
2N/A */
2N/Astatic int
2N/Ag_get_dev_port_state(char *fca_path, la_wwn_t port_wwn, uint32_t *state)
2N/A{
2N/A int fd;
2N/A int dev_type;
2N/A fcio_t fcio;
2N/A int verbose = 0;
2N/A
2N/A if ((dev_type = g_get_path_type(fca_path)) == 0) {
2N/A return (L_INVALID_PATH);
2N/A }
2N/A
2N/A if (!(dev_type & FC_XPORT_MASK)) {
2N/A return (L_INVALID_PATH_TYPE);
2N/A }
2N/A
2N/A /* open controller */
2N/A if ((fd = g_object_open(fca_path, O_NDELAY | O_RDONLY)) == -1)
2N/A return (L_OPEN_PATH_FAIL);
2N/A
2N/A /*
2N/A * stores port_wwn to la_wwn_t raw_wwn field
2N/A * and construct fcio structures for FCIO_DEV_LOGIN.
2N/A */
2N/A fcio.fcio_cmd = FCIO_GET_STATE;
2N/A fcio.fcio_ilen = sizeof (port_wwn);
2N/A fcio.fcio_ibuf = (caddr_t)&port_wwn;
2N/A fcio.fcio_xfer = FCIO_XFER_READ | FCIO_XFER_WRITE;
2N/A fcio.fcio_olen = sizeof (uint32_t);
2N/A fcio.fcio_obuf = (caddr_t)state;
2N/A fcio.fcio_alen = 0;
2N/A fcio.fcio_abuf = NULL;
2N/A if (g_issue_fcio_ioctl(fd, &fcio, verbose) != 0) {
2N/A I_DPRINTF(" FCIO_GET_STATE ioctl failed.\n");
2N/A (void) close(fd);
2N/A return (L_FCIO_GET_STATE_FAIL);
2N/A } else {
2N/A (void) close(fd);
2N/A return (0);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Name: lilp_map_cmp
2N/A *
2N/A * Description: This function is used to compare the physical location
2N/A * of to fc devices in a gfc_map_t.dev_addr arrary.
2N/A *
2N/A * Params:
2N/A * First device to compare
2N/A * Second device to compare
2N/A *
2N/A * Return:
2N/A * 0 = Devices at equal phyiscal location, How did this happen?
2N/A * >0 = First device have a higher physical location than second
2N/A * <0 = Second device have a higher physical location than first
2N/A */
2N/Astatic int lilp_map_cmp(const void* dev1, const void* dev2) {
2N/A int i_dev1 = ((fc_port_dev_t *)dev1)->dev_did.priv_lilp_posit;
2N/A int i_dev2 = ((fc_port_dev_t *)dev2)->dev_did.priv_lilp_posit;
2N/A
2N/A if (i_dev1 > i_dev2)
2N/A return (1);
2N/A if (i_dev1 < i_dev2)
2N/A return (-1);
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Description:
2N/A * Retrieves multiple paths to a device based on devid
2N/A * Caller must use mplist_free to free mplist structure
2N/A * This currently only supports ssd devices.
2N/A * The st driver does not register a device id.
2N/A *
2N/A * Input Values:
2N/A *
2N/A * devid: ptr to valid ddi_devid_t struct
2N/A * root: root handle to device tree snapshot
2N/A * drvr_name: driver name to start the node tree search
2N/A *
2N/A * Return Value:
2N/A * 0 on success
2N/A * non-zero on failure
2N/A */
2N/A
2N/Astatic int
2N/Adevid_get_all(ddi_devid_t devid, di_node_t root, char *drvr_name,
2N/A struct mplist_struct **mplistp)
2N/A{
2N/A ddi_devid_t mydevid;
2N/A di_node_t node;
2N/A char *devfs_path = NULL;
2N/A struct mplist_struct *mpl, *mpln;
2N/A
2N/A if (devid == NULL || root == NULL || drvr_name == NULL ||
2N/A mplistp == NULL ||
2N/A (strncmp(drvr_name, SSD_DRVR_NAME, strlen(SSD_DRVR_NAME))
2N/A != 0)) {
2N/A return (EINVAL);
2N/A }
2N/A
2N/A *mplistp = mpl = mpln = (struct mplist_struct *)NULL;
2N/A
2N/A /* point to first node which matches portdrvr */
2N/A node = di_drv_first_node(drvr_name, root);
2N/A if (node == DI_NODE_NIL) {
2N/A return (L_NO_DRIVER_NODES_FOUND);
2N/A }
2N/A
2N/A while (node != DI_NODE_NIL) {
2N/A if ((mydevid = di_devid(node)) != NULL) {
2N/A if (((devid_compare(mydevid, devid)) == 0)) {
2N/A /* Load multipath list */
2N/A if ((mpl = (struct mplist_struct *)
2N/A calloc(1, sizeof (struct mplist_struct)))
2N/A == NULL) {
2N/A mplist_free(*mplistp);
2N/A return (L_MALLOC_FAILED);
2N/A }
2N/A if ((devfs_path = my_devfs_path(node)) ==
2N/A NULL) {
2N/A node = di_drv_next_node(node);
2N/A S_FREE(mpl);
2N/A continue;
2N/A }
2N/A mpl->devpath = (char *)calloc(1,
2N/A strlen(devfs_path) +
2N/A strlen(SSD_MINOR_NAME) + 1);
2N/A if (mpl->devpath == NULL) {
2N/A S_FREE(mpl);
2N/A mplist_free(*mplistp);
2N/A my_devfs_path_free(devfs_path);
2N/A return (L_MALLOC_FAILED);
2N/A }
2N/A sprintf(mpl->devpath, "%s%s", devfs_path,
2N/A SSD_MINOR_NAME);
2N/A if (*mplistp == NULL) {
2N/A *mplistp = mpln = mpl;
2N/A } else {
2N/A mpln->next = mpl;
2N/A mpln = mpl;
2N/A }
2N/A my_devfs_path_free(devfs_path);
2N/A }
2N/A }
2N/A node = di_drv_next_node(node);
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Frees a previously allocated mplist_struct
2N/A */
2N/Astatic void
2N/Amplist_free(struct mplist_struct *mplistp)
2N/A{
2N/A struct mplist_struct *mplistn;
2N/A
2N/A while (mplistp != NULL) {
2N/A mplistn = mplistp->next;
2N/A if (mplistp->devpath != NULL) {
2N/A free(mplistp->devpath);
2N/A mplistp->devpath = NULL;
2N/A }
2N/A free(mplistp);
2N/A mplistp = mplistn;
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Description
2N/A * Retrieves all device nodes based on drvr_name
2N/A * Currently supports SSD_DRVR_NAME, ST_DRVR_NAME
2N/A * There will be a device node in the libdevinfo
2N/A * snapshot only if there is at least one node bound.
2N/A *
2N/A * Input values:
2N/A * root valid snapshot handle from di_init(3DEVINFO)
2N/A * drvr_name name of driver to start node search
2N/A * wwn_list_ptr ptr to ptr to WWN_list struct
2N/A *
2N/A *
2N/A */
2N/Astatic int
2N/Adevices_get_all(di_node_t root, char *drvr_name, char *minor_name,
2N/A struct wwn_list_struct **wwn_list_ptr)
2N/A{
2N/A di_node_t node;
2N/A char *devfs_path;
2N/A char devicepath[MAXPATHLEN];
2N/A uchar_t *nwwn = NULL, *pwwn = NULL;
2N/A uchar_t node_wwn[WWN_SIZE], port_wwn[WWN_SIZE];
2N/A WWN_list *wwn_list, *l1, *l2;
2N/A int scsi_vhci = 0;
2N/A int err, devtype;
2N/A
2N/A if (root == DI_NODE_NIL || drvr_name == NULL ||
2N/A wwn_list_ptr == NULL) {
2N/A return (EINVAL);
2N/A }
2N/A
2N/A wwn_list = *wwn_list_ptr = NULL;
2N/A
2N/A memset(port_wwn, 0, sizeof (port_wwn));
2N/A memset(node_wwn, 0, sizeof (node_wwn));
2N/A
2N/A if (strcmp(drvr_name, SSD_DRVR_NAME) == 0) {
2N/A devtype = DTYPE_DIRECT;
2N/A } else if (strcmp(drvr_name, ST_DRVR_NAME) == 0) {
2N/A devtype = DTYPE_SEQUENTIAL;
2N/A } else {
2N/A /*
2N/A * An unsupported driver name was passed in
2N/A */
2N/A return (L_DRIVER_NOTSUPP);
2N/A }
2N/A
2N/A /* point to first node which matches portdrvr */
2N/A node = di_drv_first_node(drvr_name, root);
2N/A if (node == DI_NODE_NIL) {
2N/A return (L_NO_DEVICES_FOUND);
2N/A }
2N/A
2N/A while (node != DI_NODE_NIL) {
2N/A
2N/A if ((devfs_path = my_devfs_path(node)) != NULL) {
2N/A
2N/A /*
2N/A * Check for offline state
2N/A */
2N/A if ((di_state(node) &
2N/A DI_DEVICE_OFFLINE) == DI_DEVICE_OFFLINE) {
2N/A my_devfs_path_free(devfs_path);
2N/A node = di_drv_next_node(node);
2N/A continue;
2N/A }
2N/A
2N/A /*
2N/A * Only support st, ssd nodes
2N/A */
2N/A if (!strstr(devfs_path, SLSH_DRV_NAME_SSD) &&
2N/A !strstr(devfs_path, SLSH_DRV_NAME_ST)) {
2N/A my_devfs_path_free(devfs_path);
2N/A node = di_drv_next_node(node);
2N/A continue;
2N/A }
2N/A
2N/A devicepath[0] = '\0';
2N/A
2N/A /*
2N/A * form device path
2N/A */
2N/A sprintf(devicepath, "%s%s", devfs_path, minor_name);
2N/A
2N/A if ((strstr(devicepath, SCSI_VHCI) == NULL)) {
2N/A if ((err = get_wwn_data(node, &nwwn, &pwwn)) !=
2N/A 0) {
2N/A my_devfs_path_free(devfs_path);
2N/A return (err);
2N/A } else {
2N/A memcpy(node_wwn, nwwn,
2N/A sizeof (node_wwn));
2N/A memcpy(port_wwn, pwwn,
2N/A sizeof (port_wwn));
2N/A }
2N/A } else {
2N/A /*
2N/A * Clear values for SCSI VHCI devices.
2N/A * node wwn, port wwn are irrevelant at
2N/A * the SCSI VHCI level
2N/A */
2N/A scsi_vhci++;
2N/A memset(port_wwn, 0, sizeof (port_wwn));
2N/A memset(node_wwn, 0, sizeof (node_wwn));
2N/A }
2N/A
2N/A /* Got wwns, load data in list */
2N/A if ((l2 = (struct wwn_list_struct *)
2N/A calloc(1, sizeof (struct wwn_list_struct))) ==
2N/A NULL) {
2N/A my_devfs_path_free(devfs_path);
2N/A return (L_MALLOC_FAILED);
2N/A }
2N/A if ((l2->physical_path = (char *)
2N/A calloc(1, strlen(devicepath) +1)) == NULL) {
2N/A my_devfs_path_free(devfs_path);
2N/A return (L_MALLOC_FAILED);
2N/A }
2N/A
2N/A memcpy(l2->w_node_wwn, node_wwn, WWN_SIZE);
2N/A
2N/A if (scsi_vhci) {
2N/A strcpy(l2->node_wwn_s, MSGSTR(12000, "N/A"));
2N/A } else {
2N/A copy_wwn_data_to_str(l2->node_wwn_s, node_wwn);
2N/A copy_wwn_data_to_str(l2->port_wwn_s, port_wwn);
2N/A }
2N/A
2N/A strcpy(l2->physical_path, devicepath);
2N/A
2N/A l2->device_type = devtype;
2N/A if (wwn_list == NULL) {
2N/A l1 = wwn_list = l2;
2N/A } else {
2N/A l2->wwn_prev = l1;
2N/A l1 = l1->wwn_next = l2;
2N/A }
2N/A my_devfs_path_free(devfs_path);
2N/A scsi_vhci = 0;
2N/A }
2N/A node = di_drv_next_node(node);
2N/A }
2N/A
2N/A *wwn_list_ptr = wwn_list; /* pass back ptr to list */
2N/A
2N/A if (*wwn_list_ptr == NULL) {
2N/A return (L_NO_DEVICES_FOUND);
2N/A } else {
2N/A /*
2N/A * Now load the /dev/ paths
2N/A */
2N/A if (strcmp(drvr_name, SSD_DRVR_NAME) == 0) {
2N/A if ((err = get_dev_path(wwn_list_ptr, DEV_RDIR,
2N/A DIR_MATCH_SSD)) != 0) {
2N/A g_free_wwn_list(wwn_list_ptr);
2N/A return (err);
2N/A }
2N/A } else if (strcmp(drvr_name, ST_DRVR_NAME) == 0) {
2N/A if ((err = get_dev_path(wwn_list_ptr, DEV_TAPE_DIR,
2N/A DIR_MATCH_ST)) != 0) {
2N/A g_free_wwn_list(wwn_list_ptr);
2N/A return (err);
2N/A }
2N/A }
2N/A return (0);
2N/A }
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Access the properties for the node to get the node-wwn, port-wwn property
2N/A * On error, contents of nwwn, pwwn are unspecified.
2N/A * On successful return nwwn and pwwn are WWN_SIZE bytes.
2N/A */
2N/Astatic int
2N/Aget_wwn_data(di_node_t node, uchar_t **nwwn, uchar_t **pwwn)
2N/A{
2N/A if (di_prop_lookup_bytes(DDI_DEV_T_ANY, node, NODE_WWN_PROP,
2N/A nwwn) != WWN_SIZE) {
2N/A /* If we didn't get back the right count, return error */
2N/A return (L_NO_WWN_PROP_FOUND);
2N/A }
2N/A if (di_prop_lookup_bytes(DDI_DEV_T_ANY, node, PORT_WWN_PROP,
2N/A pwwn) != WWN_SIZE) {
2N/A /* If we didn't get back the right count, return error */
2N/A return (L_NO_WWN_PROP_FOUND);
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Description
2N/A * retrieves the /dev logical path for a WWN_list of devices.
2N/A * Input values
2N/A * wwn_list_ptr ptr to list returned by devices_get_all
2N/A * dir_name /dev/ directory to search
2N/A *
2N/A */
2N/Astatic int
2N/Aget_dev_path(struct wwn_list_struct **wwn_list_ptr, char *dir_name,
2N/A char *pattern_match)
2N/A{
2N/A DIR *dirp;
2N/A struct dirent *entp;
2N/A char namebuf[MAXPATHLEN];
2N/A char *result = NULL;
2N/A WWN_list *wwn_list, *wwn_list_save;
2N/A char *env;
2N/A hrtime_t start_time, end_time;
2N/A
2N/A if (wwn_list_ptr == NULL || *wwn_list_ptr == NULL ||
2N/A dir_name == NULL || pattern_match == NULL) {
2N/A return (EINVAL);
2N/A }
2N/A
2N/A if ((env = getenv("_LUX_T_DEBUG")) != NULL) {
2N/A start_time = gethrtime();
2N/A }
2N/A
2N/A wwn_list = *wwn_list_ptr;
2N/A
2N/A if ((dirp = opendir(dir_name)) == NULL) {
2N/A P_DPRINTF(" get_dev_path: No devices found\n");
2N/A return (L_NO_DEVICES_FOUND);
2N/A }
2N/A
2N/A while ((entp = readdir(dirp)) != NULL) {
2N/A /*
2N/A * Ignore current directory and parent directory
2N/A * entries.
2N/A */
2N/A if ((strcmp(entp->d_name, ".") == 0) ||
2N/A (strcmp(entp->d_name, "..") == 0) ||
2N/A (fnmatch(pattern_match, entp->d_name, 0) != 0))
2N/A continue;
2N/A
2N/A memset(namebuf, 0, sizeof (namebuf));
2N/A sprintf(namebuf, "%s/%s", dir_name, entp->d_name);
2N/A
2N/A if ((result = g_get_physical_name_from_link(namebuf)) == NULL) {
2N/A ER_DPRINTF(" Warning: Get physical name from"
2N/A " link failed. Link=%s\n", namebuf);
2N/A continue;
2N/A }
2N/A for (wwn_list = *wwn_list_ptr; wwn_list != NULL;
2N/A wwn_list = wwn_list->wwn_next) {
2N/A if (strcmp(wwn_list->physical_path, result) == 0) {
2N/A /*
2N/A * Add information to the list.
2N/A */
2N/A if ((wwn_list->logical_path = (char *)
2N/A calloc(1, strlen(namebuf) + 1)) == NULL) {
2N/A free(result);
2N/A return (L_MALLOC_FAILED);
2N/A }
2N/A strcpy(wwn_list->logical_path, namebuf);
2N/A break;
2N/A }
2N/A }
2N/A free(result);
2N/A }
2N/A closedir(dirp);
2N/A
2N/A /*
2N/A * Did we load all of the paths?
2N/A * Note: if there is a missing entry in /dev then
2N/A * the user probably did a cleanup of /dev.
2N/A * Whatever the case, remove the entry as it
2N/A * is invalid.
2N/A */
2N/A wwn_list = *wwn_list_ptr;
2N/A while (wwn_list != NULL) {
2N/A if (wwn_list->logical_path == NULL) {
2N/A free(wwn_list->physical_path);
2N/A wwn_list_save = wwn_list;
2N/A if (wwn_list->wwn_prev != NULL) {
2N/A wwn_list->wwn_prev->wwn_next =
2N/A wwn_list->wwn_next;
2N/A } else {
2N/A /*
2N/A * No previous entries
2N/A */
2N/A *wwn_list_ptr = wwn_list->wwn_next;
2N/A }
2N/A if (wwn_list->wwn_next != NULL) {
2N/A wwn_list->wwn_next->wwn_prev =
2N/A wwn_list->wwn_prev;
2N/A }
2N/A wwn_list = wwn_list->wwn_next;
2N/A free(wwn_list_save);
2N/A } else {
2N/A wwn_list = wwn_list->wwn_next;
2N/A }
2N/A }
2N/A
2N/A if (env != NULL) {
2N/A end_time = gethrtime();
2N/A fprintf(stdout,
2N/A " get_dev_path %s: "
2N/A "\t\tTime = %lld millisec\n",
2N/A dir_name, (end_time - start_time)/1000000);
2N/A }
2N/A
2N/A if (*wwn_list_ptr == NULL) {
2N/A return (L_NO_DEVICES_FOUND);
2N/A } else {
2N/A return (0);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * This functions calls di_devfs_path and gets the path associated with a
2N/A * given devinfo node. If the path returned does not have a '@' in it, it
2N/A * checks if the driver is detached and creates a path after looking at the
2N/A * driver properties.
2N/A *
2N/A * di_devfs_path_free is called internally.
2N/A *
2N/A * The argument 'path' points to the final value upon return.
2N/A * Caller must use my_devfs_path_free on returned char *
2N/A * Note: Only support FC/SCSI_VHCI devices,
2N/A * for FC check for initiator-interconnect-type prop
2N/A *
2N/A */
2N/Astatic char *
2N/Amy_devfs_path(di_node_t node)
2N/A{
2N/A uchar_t *pwwn = NULL;
2N/A char *interconnect = NULL;
2N/A char pwwns[WWN_SIZE*2+1];
2N/A char *mypath;
2N/A int scsi_vhci = 0;
2N/A int rval;
2N/A char *tptr = NULL, *lun_guid = NULL;
2N/A int *lunnump = NULL;
2N/A di_node_t parentnode;
2N/A
2N/A /* sanity check */
2N/A if (node == DI_NODE_NIL) {
2N/A return (NULL);
2N/A }
2N/A
2N/A /* Now go get the path for this node */
2N/A if ((tptr = di_devfs_path(node)) == NULL) {
2N/A return (NULL);
2N/A }
2N/A
2N/A parentnode = di_parent_node(node);
2N/A
2N/A if ((mypath = (char *)calloc(1, MAXPATHLEN + 1)) == NULL) {
2N/A di_devfs_path_free(tptr);
2N/A return (NULL);
2N/A }
2N/A
2N/A /* Prepend "/devices" to libdevinfo-returned paths */
2N/A sprintf(mypath, "%s%s", DEVICES_DIR, tptr);
2N/A
2N/A di_devfs_path_free(tptr);
2N/A
2N/A /*
2N/A * Is this a FC device?
2N/A * Check initiator-interconnect-type property
2N/A */
2N/A if (strstr(mypath, SCSI_VHCI) == NULL) {
2N/A rval = di_prop_lookup_strings(DDI_DEV_T_ANY, parentnode,
2N/A "initiator-interconnect-type", &interconnect);
2N/A /* Check for INTERCONNECT_FABRIC_STR & INTERCONNECT_FIBRE_STR */
2N/A if ((rval <= 0) ||
2N/A ((strcmp(interconnect, "FABRIC") != 0) &&
2N/A (strcmp(interconnect, "FIBRE") != 0))) {
2N/A /* Not a FC device. Free path and return */
2N/A free(mypath);
2N/A return (NULL);
2N/A }
2N/A } else {
2N/A scsi_vhci++;
2N/A }
2N/A
2N/A if ((tptr = strrchr(mypath, '/')) == NULL) {
2N/A free(mypath);
2N/A return (NULL);
2N/A }
2N/A
2N/A if (strchr(tptr, '@') != NULL) {
2N/A return (mypath);
2N/A }
2N/A
2N/A /*
2N/A * No '@' in path. This can happen when driver is detached.
2N/A * We'll check if the state is detached and if it is, we'll construct
2N/A * the path by looking at the properties.
2N/A */
2N/A
2N/A if ((di_state(node) & DI_DRIVER_DETACHED) != DI_DRIVER_DETACHED) {
2N/A /*
2N/A * Driver is not detached and no '@' in path.
2N/A * Can't handle it.
2N/A */
2N/A free(mypath);
2N/A return (NULL);
2N/A }
2N/A
2N/A if (!scsi_vhci) {
2N/A rval = di_prop_lookup_bytes(DDI_DEV_T_ANY, node, PORT_WWN_PROP,
2N/A &pwwn);
2N/A if (rval <= 0) {
2N/A /* Something unexpected happened. */
2N/A free(mypath);
2N/A return (NULL);
2N/A }
2N/A copy_wwn_data_to_str(pwwns, pwwn);
2N/A di_prop_lookup_ints(DDI_DEV_T_ANY, node, LUN_PROP, &lunnump);
2N/A sprintf(&mypath[strlen(mypath)], "@w%s,%x", pwwns, *lunnump);
2N/A } else {
2N/A di_prop_lookup_strings(DDI_DEV_T_ANY, node,
2N/A LUN_GUID_PROP, &lun_guid);
2N/A sprintf(&mypath[strlen(mypath)], "@g%s", lun_guid);
2N/A }
2N/A return (mypath);
2N/A}
2N/A
2N/Astatic void
2N/Amy_devfs_path_free(char *path)
2N/A{
2N/A if (path != NULL) {
2N/A free(path);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * from_ptr: ptr to uchar_t array of size WWN_SIZE
2N/A * to_ptr: char ptr to string of size WWN_SIZE*2+1
2N/A */
2N/Astatic void
2N/Acopy_wwn_data_to_str(char *to_ptr, const uchar_t *from_ptr)
2N/A{
2N/A if ((to_ptr == NULL) || (from_ptr == NULL))
2N/A return;
2N/A
2N/A sprintf(to_ptr, "%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x",
2N/A from_ptr[0], from_ptr[1], from_ptr[2], from_ptr[3],
2N/A from_ptr[4], from_ptr[5], from_ptr[6], from_ptr[7]);
2N/A}
2N/A
2N/A/*
2N/A * Open the requested directory and get one valid open.
2N/A * If a device is busy, return.
2N/A * Only need to open one device since
2N/A * that implies there will be a node returned from
2N/A * di_drv_first_node()
2N/A * dir_name: logical device name directory
2N/A * (DEV_TAPE_DIR, DEV_RDIR)
2N/A * pattern_match: used by fnmatch on directory entry
2N/A * (DIR_MATCH_SSD, DIR_MATCH_ST)
2N/A * drvr_path: path type to verify ("/ssd@", "/st@")
2N/A * (SLSH_DRV_NAME_ST, SLSH_DRV_NAME_SSD)
2N/A *
2N/A * Returns: None
2N/A */
2N/Astatic void
2N/Ainit_drv(char *dir_name, char *pattern_match, char *drvr_path)
2N/A{
2N/A DIR *dirp;
2N/A struct dirent *entp;
2N/A char namebuf[MAXPATHLEN];
2N/A char *result = NULL;
2N/A int fd;
2N/A
2N/A if ((dirp = opendir(dir_name)) == NULL) {
2N/A return;
2N/A }
2N/A
2N/A while ((entp = readdir(dirp)) != NULL) {
2N/A /*
2N/A * Ignore current directory and parent directory
2N/A * entries.
2N/A */
2N/A if ((strcmp(entp->d_name, ".") == 0) ||
2N/A (strcmp(entp->d_name, "..") == 0) ||
2N/A (fnmatch(pattern_match, entp->d_name, 0) != 0)) {
2N/A continue;
2N/A }
2N/A
2N/A memset(namebuf, 0, sizeof (namebuf));
2N/A sprintf(namebuf, "%s/%s", dir_name, entp->d_name);
2N/A
2N/A if ((result = g_get_physical_name_from_link(namebuf)) == NULL) {
2N/A ER_DPRINTF(" Warning: Get physical name from"
2N/A " link failed. Link=%s\n", namebuf);
2N/A continue;
2N/A }
2N/A
2N/A if (strstr(result, drvr_path) == NULL) {
2N/A free(result);
2N/A result = NULL;
2N/A continue;
2N/A }
2N/A
2N/A if ((fd = g_object_open(result, O_NDELAY | O_RDONLY)) != -1) {
2N/A close(fd);
2N/A break;
2N/A } else if (errno != EBUSY) {
2N/A free(result);
2N/A result = NULL;
2N/A continue;
2N/A } else {
2N/A break;
2N/A }
2N/A }
2N/A free(result);
2N/A closedir(dirp);
2N/A}