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