/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /*LINTLIBRARY*/ /* * I18N message number ranges * This file: 9000 - 9499 * Shared common messages: 1 - 1999 */ /* * This module is part of the photon library */ /* Includes */ #include <stdlib.h> #include <stdio.h> #include <sys/file.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/param.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <assert.h> #include <sys/scsi/scsi.h> #include <dirent.h> /* for DIR */ #include <sys/vtoc.h> #include <sys/dkio.h> #include <nl_types.h> #include <strings.h> #include <sys/ddi.h> /* for max */ #include <l_common.h> #include <stgcom.h> #include <l_error.h> #include <rom.h> #include <exec.h> #include <a_state.h> #include <a5k.h> /* Defines */ #define PLNDEF "SUNW,pln" /* check if box name starts with 'c' */ #define DOWNLOAD_RETRIES 60*5 /* 5 minutes */ #define IBFIRMWARE_FILE "/usr/lib/locale/C/LC_MESSAGES/ibfirmware" /* Global variables */ extern uchar_t g_switch_to_alpa[]; extern uchar_t g_sf_alpa_to_switch[]; /* Forward declarations */ static int pwr_up_down(char *, L_state *, int, int, int, int); static int load_flds_if_enc_disk(char *, struct path_struct **); static int copy_config_page(struct l_state_struct *, uchar_t *); static void copy_page_7(struct l_state_struct *, uchar_t *); static int l_get_node_status(char *, struct l_disk_state_struct *, int *, WWN_list *, int); static int check_file(int, int, uchar_t **, int); static int check_dpm_file(int); static int ib_download_code_cmd(int, int, int, uchar_t *, int, int); static int dak_download_code_cmd(int, uchar_t *, int); static void free_mp_dev_map(struct gfc_map_mp **); static int get_mp_dev_map(char *, struct gfc_map_mp **, int); /* * l_get_mode_pg() - Read all mode pages. * * RETURNS: * 0 O.K. * non-zero otherwise * * INPUTS: * path pointer to device path * pg_buf ptr to mode pages * */ /*ARGSUSED*/ int l_get_mode_pg(char *path, uchar_t **pg_buf, int verbose) { Mode_header_10 *mode_header_ptr; int status, size, fd; P_DPRINTF(" l_get_mode_pg: Reading Mode Sense pages.\n"); /* do not do mode sense if this is a tape device */ /* mode sense will rewind the tape */ if (strstr(path, SLSH_DRV_NAME_ST)) { return (-1); } /* open controller */ if ((fd = g_object_open(path, O_NDELAY | O_RDWR)) == -1) return (L_OPEN_PATH_FAIL); /* * Read the first part of the page to get the page size */ size = 20; if ((*pg_buf = (uchar_t *)g_zalloc(size)) == NULL) { (void) close(fd); return (L_MALLOC_FAILED); } /* read page */ if (status = g_scsi_mode_sense_cmd(fd, *pg_buf, size, 0, MODEPAGE_ALLPAGES)) { (void) close(fd); (void) g_destroy_data((char *)*pg_buf); return (status); } /* Now get the size for all pages */ mode_header_ptr = (struct mode_header_10_struct *)(void *)*pg_buf; size = mode_header_ptr->length + sizeof (mode_header_ptr->length); (void) g_destroy_data((char *)*pg_buf); if ((*pg_buf = (uchar_t *)g_zalloc(size)) == NULL) { (void) close(fd); return (L_MALLOC_FAILED); } /* read all pages */ if (status = g_scsi_mode_sense_cmd(fd, *pg_buf, size, 0, MODEPAGE_ALLPAGES)) { (void) close(fd); (void) g_destroy_data((char *)*pg_buf); return (status); } (void) close(fd); return (0); } /* * Format QLA21xx status * * INPUTS: message buffer * Count * status * * OUTPUT: Message of this format in message buffer * "status type: 0xstatus count" */ int l_format_ifp_status_msg(char *status_msg_buf, int count, int status) { if (status_msg_buf == NULL) { return (0); } switch (status) { case IFP_CMD_CMPLT: (void) sprintf(status_msg_buf, MSGSTR(9000, "O.K. 0x%-2x" " %d"), status, count); break; case IFP_CMD_INCOMPLETE: (void) sprintf(status_msg_buf, MSGSTR(9001, "Cmd incomplete 0x%-2x" " %d"), status, count); break; case IFP_CMD_DMA_DERR: (void) sprintf(status_msg_buf, MSGSTR(9002, "DMA direction error 0x%-2x" " %d"), status, count); break; case IFP_CMD_TRAN_ERR: (void) sprintf(status_msg_buf, MSGSTR(9003, "Unspecified transport error 0x%-2x" " %d"), status, count); break; case IFP_CMD_RESET: (void) sprintf(status_msg_buf, MSGSTR(9004, "Reset aborted transport 0x%-2x" " %d"), status, count); break; case IFP_CMD_ABORTED: (void) sprintf(status_msg_buf, MSGSTR(9005, "Cmd aborted 0x%-2x" " %d"), status, count); break; case IFP_CMD_TIMEOUT: (void) sprintf(status_msg_buf, MSGSTR(9006, "Cmd Timeout 0x%-2x" " %d"), status, count); break; case IFP_CMD_DATA_OVR: (void) sprintf(status_msg_buf, MSGSTR(9007, "Data Overrun 0x%-2x" " %d"), status, count); break; case IFP_CMD_ABORT_REJECTED: (void) sprintf(status_msg_buf, MSGSTR(9008, "Target rejected abort msg 0x%-2x" " %d"), status, count); break; case IFP_CMD_RESET_REJECTED: (void) sprintf(status_msg_buf, MSGSTR(9009, "Target rejected reset msg 0x%-2x" " %d"), status, count); break; case IFP_CMD_DATA_UNDER: (void) sprintf(status_msg_buf, MSGSTR(9010, "Data underrun 0x%-2x" " %d"), status, count); break; case IFP_CMD_QUEUE_FULL: (void) sprintf(status_msg_buf, MSGSTR(9011, "Queue full SCSI status 0x%-2x" " %d"), status, count); break; case IFP_CMD_PORT_UNAVAIL: (void) sprintf(status_msg_buf, MSGSTR(9012, "Port unavailable 0x%-2x" " %d"), status, count); break; case IFP_CMD_PORT_LOGGED_OUT: (void) sprintf(status_msg_buf, MSGSTR(9013, "Port loged out 0x%-2x" " %d"), status, count); break; case IFP_CMD_PORT_CONFIG_CHANGED: /* Not enough packets for given request */ (void) sprintf(status_msg_buf, MSGSTR(9014, "Port name changed 0x%-2x" " %d"), status, count); break; default: (void) sprintf(status_msg_buf, "%s 0x%-2x" " %d", MSGSTR(4, "Unknown status"), status, count); } /* End of switch() */ return (0); } /* * Format Fibre Channel status * * INPUTS: message buffer * Count * status * * OUTPUT: Message of this format in message buffer * "status type: 0xstatus count" */ int l_format_fc_status_msg(char *status_msg_buf, int count, int status) { if (status_msg_buf == NULL) { return (0); } switch (status) { case FCAL_STATUS_OK: (void) sprintf(status_msg_buf, MSGSTR(9015, "O.K. 0x%-2x" " %d"), status, count); break; case FCAL_STATUS_P_RJT: (void) sprintf(status_msg_buf, MSGSTR(9016, "P_RJT (Frame Rejected) 0x%-2x" " %d"), status, count); break; case FCAL_STATUS_F_RJT: (void) sprintf(status_msg_buf, MSGSTR(9017, "F_RJT (Frame Rejected) 0x%-2x" " %d"), status, count); break; case FCAL_STATUS_P_BSY: (void) sprintf(status_msg_buf, MSGSTR(9018, "P_BSY (Port Busy) 0x%-2x" " %d"), status, count); break; case FCAL_STATUS_F_BSY: (void) sprintf(status_msg_buf, MSGSTR(9019, "F_BSY (Port Busy) 0x%-2x" " %d"), status, count); break; case FCAL_STATUS_OLDPORT_ONLINE: /* Should not happen. */ (void) sprintf(status_msg_buf, MSGSTR(9020, "Old port Online 0x%-2x" " %d"), status, count); break; case FCAL_STATUS_ERR_OFFLINE: (void) sprintf(status_msg_buf, MSGSTR(9021, "Link Offline 0x%-2x" " %d"), status, count); break; case FCAL_STATUS_TIMEOUT: /* Should not happen. */ (void) sprintf(status_msg_buf, MSGSTR(9022, "Sequence Timeout 0x%-2x" " %d"), status, count); break; case FCAL_STATUS_ERR_OVERRUN: (void) sprintf(status_msg_buf, MSGSTR(9023, "Sequence Payload Overrun 0x%-2x" " %d"), status, count); break; case FCAL_STATUS_LOOP_ONLINE: (void) sprintf(status_msg_buf, MSGSTR(9060, "Loop Online 0x%-2x" " %d"), status, count); break; case FCAL_STATUS_OLD_PORT: (void) sprintf(status_msg_buf, MSGSTR(9061, "Old port 0x%-2x" " %d"), status, count); break; case FCAL_STATUS_AL_PORT: (void) sprintf(status_msg_buf, MSGSTR(9062, "AL port 0x%-2x" " %d"), status, count); break; case FCAL_STATUS_UNKNOWN_CQ_TYPE: (void) sprintf(status_msg_buf, MSGSTR(9024, "Unknown request type 0x%-2x" " %d"), status, count); break; case FCAL_STATUS_BAD_SEG_CNT: (void) sprintf(status_msg_buf, MSGSTR(9025, "Bad segment count 0x%-2x" " %d"), status, count); break; case FCAL_STATUS_MAX_XCHG_EXCEEDED: (void) sprintf(status_msg_buf, MSGSTR(9026, "Maximum exchanges exceeded 0x%-2x" " %d"), status, count); break; case FCAL_STATUS_BAD_XID: (void) sprintf(status_msg_buf, MSGSTR(9027, "Bad exchange identifier 0x%-2x" " %d"), status, count); break; case FCAL_STATUS_XCHG_BUSY: (void) sprintf(status_msg_buf, MSGSTR(9028, "Duplicate exchange request 0x%-2x" " %d"), status, count); break; case FCAL_STATUS_BAD_POOL_ID: (void) sprintf(status_msg_buf, MSGSTR(9029, "Bad memory pool ID 0x%-2x" " %d"), status, count); break; case FCAL_STATUS_INSUFFICIENT_CQES: /* Not enough packets for given request */ (void) sprintf(status_msg_buf, MSGSTR(9030, "Invalid # of segments for req 0x%-2x" " %d"), status, count); break; case FCAL_STATUS_ALLOC_FAIL: (void) sprintf(status_msg_buf, MSGSTR(9031, "Resource allocation failure 0x%-2x" " %d"), status, count); break; case FCAL_STATUS_BAD_SID: (void) sprintf(status_msg_buf, MSGSTR(9032, "Bad Source Identifier(S_ID) 0x%-2x" " %d"), status, count); break; case FCAL_STATUS_NO_SEQ_INIT: (void) sprintf(status_msg_buf, MSGSTR(9033, "No sequence initiative 0x%-2x" " %d"), status, count); break; case FCAL_STATUS_BAD_DID: (void) sprintf(status_msg_buf, MSGSTR(9034, "Bad Destination ID(D_ID) 0x%-2x" " %d"), status, count); break; case FCAL_STATUS_ABORTED: (void) sprintf(status_msg_buf, MSGSTR(9035, "Received BA_ACC from abort 0x%-2x" " %d"), status, count); break; case FCAL_STATUS_ABORT_FAILED: (void) sprintf(status_msg_buf, MSGSTR(9036, "Received BA_RJT from abort 0x%-2x" " %d"), status, count); break; case FCAL_STATUS_DIAG_BUSY: (void) sprintf(status_msg_buf, MSGSTR(9037, "Diagnostics currently busy 0x%-2x" " %d"), status, count); break; case FCAL_STATUS_DIAG_INVALID: (void) sprintf(status_msg_buf, MSGSTR(9038, "Diagnostics illegal request 0x%-2x" " %d"), status, count); break; case FCAL_STATUS_INCOMPLETE_DMA_ERR: (void) sprintf(status_msg_buf, MSGSTR(9039, "SBus DMA did not complete 0x%-2x" " %d"), status, count); break; case FCAL_STATUS_CRC_ERR: (void) sprintf(status_msg_buf, MSGSTR(9040, "CRC error detected 0x%-2x" " %d"), status, count); break; case FCAL_STATUS_OPEN_FAIL: (void) sprintf(status_msg_buf, MSGSTR(9063, "Open failure 0x%-2x" " %d"), status, count); break; case FCAL_STATUS_ERROR: (void) sprintf(status_msg_buf, MSGSTR(9041, "Invalid status error 0x%-2x" " %d"), status, count); break; case FCAL_STATUS_ONLINE_TIMEOUT: (void) sprintf(status_msg_buf, MSGSTR(9042, "Timed out before ONLINE 0x%-2x" " %d"), status, count); break; default: (void) sprintf(status_msg_buf, "%s 0x%-2x" " %d", MSGSTR(4, "Unknown status"), status, count); } /* End of switch() */ return (0); } /* * Get the indexes to the disk device elements in page 2, * based on the locations found in page 1. * * RETURNS: * 0 O.K. * non-zero otherwise */ int l_get_disk_element_index(struct l_state_struct *l_state, int *front_index, int *rear_index) { int index = 0, front_flag = 0, local_front = 0, local_rear = 0; int i, rear_flag = 0; if ((l_state == NULL) || (front_index == NULL) || (rear_index == NULL)) { return (L_INVALID_PATH_FORMAT); } *front_index = *rear_index = 0; /* Get the indexes to the disk device elements */ for (i = 0; i < (int)l_state->ib_tbl.config.enc_num_elem; i++) { if (l_state->ib_tbl.config.type_hdr[i].type == ELM_TYP_DD) { if (front_flag) { local_rear = index; rear_flag = 1; break; } else { local_front = index; front_flag = 1; } } index += l_state->ib_tbl.config.type_hdr[i].num; index++; /* for global element */ } D_DPRINTF(" l_get_disk_element_index:" " Index to front disk elements 0x%x\n" " l_get_disk_element_index:" " Index to rear disk elements 0x%x\n", local_front, local_rear); if (!front_flag && !rear_flag) { /* neither is found */ return (L_RD_NO_DISK_ELEM); } *front_index = local_front; *rear_index = local_rear; return (0); } /* * l_led() manage the device led's * * RETURNS: * 0 O.K. * non-zero otherwise */ int l_led(struct path_struct *path_struct, int led_action, struct device_element *status, int verbose) { gfc_map_t map; char ses_path[MAXPATHLEN]; uchar_t *page_buf; int err, write, fd, front_index, rear_index, offset; unsigned short page_len; struct device_element *elem; L_state *l_state; int enc_type; if ((path_struct == NULL) || (status == NULL)) { return (L_INVALID_PATH_FORMAT); } /* * Need to get a valid location, front/rear & slot. * * The path_struct will return a valid slot * and the IB path or a disk path. */ map.dev_addr = (gfc_port_dev_info_t *)NULL; if (!path_struct->ib_path_flag) { if ((err = g_get_dev_map(path_struct->p_physical_path, &map, verbose)) != 0) return (err); if ((err = l_get_ses_path(path_struct->p_physical_path, ses_path, &map, verbose)) != 0) { free((void *)map.dev_addr); return (err); } } else { (void) strcpy(ses_path, path_struct->p_physical_path); } if ((l_state = (L_state *)calloc(1, sizeof (L_state))) == NULL) { free((void *)map.dev_addr); return (L_MALLOC_FAILED); } if (!path_struct->slot_valid) { if ((map.dev_addr != NULL) && (err = g_get_dev_map(path_struct->p_physical_path, &map, verbose)) != 0) { (void) l_free_lstate(&l_state); return (err); } if ((err = l_get_ses_path(path_struct->p_physical_path, ses_path, &map, verbose)) != 0) { (void) l_free_lstate(&l_state); free((void *)map.dev_addr); return (err); } if ((err = l_get_status(ses_path, l_state, verbose)) != 0) { (void) l_free_lstate(&l_state); free((void *)map.dev_addr); return (err); } /* We are passing the disks path */ if (err = l_get_slot(path_struct, l_state, verbose)) { (void) l_free_lstate(&l_state); free((void *)map.dev_addr); return (err); } } if (map.dev_addr != NULL) free((void *)map.dev_addr); /* Not used anymore */ if ((page_buf = (uchar_t *)calloc(1, MAX_REC_DIAG_LENGTH)) == NULL) { (void) l_free_lstate(&l_state); return (L_MALLOC_FAILED); } if ((fd = g_object_open(ses_path, O_NDELAY | O_RDWR)) == -1) { (void) l_free_lstate(&l_state); (void) g_destroy_data(page_buf); return (L_OPEN_PATH_FAIL); } if (err = l_get_envsen_page(fd, page_buf, MAX_REC_DIAG_LENGTH, L_PAGE_2, verbose)) { (void) l_free_lstate(&l_state); (void) close(fd); (void) g_destroy_data(page_buf); return (err); } page_len = (page_buf[2] << 8 | page_buf[3]) + HEADER_LEN; /* Get index to the disk we are interested in */ if (err = l_get_status(ses_path, l_state, verbose)) { (void) l_free_lstate(&l_state); (void) close(fd); (void) g_destroy_data(page_buf); return (err); } /* find enclosure type */ if ((strncmp((char *)l_state->ib_tbl.config.prod_id, DAK_OFF_NAME, strlen(DAK_OFF_NAME)) == 0) || (strncmp((char *)l_state->ib_tbl.config.prod_id, DAK_PROD_STR, strlen(DAK_PROD_STR)) == 0)) { enc_type = DAK_ENC_TYPE; } else { enc_type = SENA_ENC_TYPE; } /* Double check slot. */ if (path_struct->slot >= l_state->total_num_drv/2) { (void) l_free_lstate(&l_state); return (L_INVALID_SLOT); } if (err = l_get_disk_element_index(l_state, &front_index, &rear_index)) { (void) l_free_lstate(&l_state); return (err); } /* Skip global element */ front_index++; if (enc_type == DAK_ENC_TYPE) { rear_index += l_state->total_num_drv/2 + 1; } else { rear_index++; } if (path_struct->f_flag) { offset = (8 + (front_index + path_struct->slot)*4); } else { offset = (8 + (rear_index + path_struct->slot)*4); } elem = (struct device_element *)(page_buf + offset); /* * now do requested action. */ bcopy((const void *)elem, (void *)status, sizeof (struct device_element)); /* save status */ bzero(elem, sizeof (struct device_element)); elem->select = 1; elem->dev_off = status->dev_off; elem->en_bypass_a = status->en_bypass_a; elem->en_bypass_b = status->en_bypass_b; write = 1; switch (led_action) { case L_LED_STATUS: write = 0; break; case L_LED_RQST_IDENTIFY: elem->ident = 1; if (verbose) { if (enc_type == DAK_ENC_TYPE) { (void) fprintf(stdout, MSGSTR(9043, " Blinking LED for slot %d in enclosure" " %s\n"), path_struct->f_flag ? path_struct->slot : path_struct->slot + (MAX_DRIVES_DAK/2), l_state->ib_tbl.enclosure_name); } else { (void) fprintf(stdout, MSGSTR(9043, " Blinking LED for slot %d in enclosure" " %s\n"), path_struct->slot, l_state->ib_tbl.enclosure_name); } } break; case L_LED_OFF: if (verbose) { if (enc_type == DAK_ENC_TYPE) { (void) fprintf(stdout, MSGSTR(9044, " Turning off LED for slot %d in enclosure" " %s\n"), path_struct->f_flag ? path_struct->slot : path_struct->slot + (MAX_DRIVES_DAK/2), l_state->ib_tbl.enclosure_name); } else { (void) fprintf(stdout, MSGSTR(9044, " Turning off LED for slot %d in enclosure" " %s\n"), path_struct->slot, l_state->ib_tbl.enclosure_name); } } break; default: (void) l_free_lstate(&l_state); return (L_INVALID_LED_RQST); } /* End of switch */ if (write) { if (getenv("_LUX_D_DEBUG") != NULL) { g_dump(" l_led: Updating led state: " "Device Status Element ", (uchar_t *)elem, sizeof (struct device_element), HEX_ONLY); } if (err = g_scsi_send_diag_cmd(fd, (uchar_t *)page_buf, page_len)) { (void) close(fd); (void) g_destroy_data(page_buf); (void) l_free_lstate(&l_state); return (err); } bzero(page_buf, MAX_REC_DIAG_LENGTH); if (err = l_get_envsen_page(fd, page_buf, MAX_REC_DIAG_LENGTH, L_PAGE_2, verbose)) { (void) g_destroy_data(page_buf); (void) close(fd); (void) l_free_lstate(&l_state); return (err); } elem = (struct device_element *)(page_buf + offset); bcopy((const void *)elem, (void *)status, sizeof (struct device_element)); } if (getenv("_LUX_D_DEBUG") != NULL) { g_dump(" l_led: Device Status Element ", (uchar_t *)status, sizeof (struct device_element), HEX_ONLY); } (void) l_free_lstate(&l_state); (void) close(fd); (void) g_destroy_data(page_buf); return (0); } /* * frees the previously alloced l_state * structure. * * RETURNS: * 0 O.K. * non-zero otherwise */ int l_free_lstate(L_state **l_state) { int i; if ((l_state == NULL) || (*l_state == NULL)) return (0); for (i = 0; i < (int)(*l_state)->total_num_drv/2; i++) { if ((*l_state)->drv_front[i].g_disk_state.multipath_list != NULL) (void) g_free_multipath( (*l_state)->drv_front[i].g_disk_state.multipath_list); if ((*l_state)->drv_rear[i].g_disk_state.multipath_list != NULL) (void) g_free_multipath( (*l_state)->drv_rear[i].g_disk_state.multipath_list); } (void) g_destroy_data (*l_state); l_state = NULL; return (0); } /* * Set the state of an individual disk * in the Photon enclosure the powered * up/down mode. The path must point to * a disk or the ib_path_flag must be set. * * RETURNS: * 0 O.K. * non-zero otherwise */ int l_dev_pwr_up_down(char *path_phys, struct path_struct *path_struct, int power_off_flag, int verbose, int force_flag) /*ARGSUSED*/ { gfc_map_t map; char ses_path[MAXPATHLEN], dev_path[MAXPATHLEN]; int slot, err = 0; L_state *l_state = NULL; struct l_disk_state_struct *drive; struct dlist *dl, *dl1; devctl_hdl_t devhdl; WWN_list *wwn_list = NULL; L_inquiry inq; if (path_struct == NULL) { return (L_INVALID_PATH_FORMAT); } dl = (struct dlist *)NULL; map.dev_addr = (gfc_port_dev_info_t *)NULL; if (err = g_get_dev_map(path_struct->p_physical_path, &map, verbose)) return (err); if (err = l_get_ses_path(path_struct->p_physical_path, ses_path, &map, verbose)) { free((void *)map.dev_addr); return (err); } free((void *)map.dev_addr); /* Not used anymore */ /* * Check to see if we have a photon, and if not, don't allow * this operation */ if (err = g_get_inquiry(ses_path, &inq)) { return (err); } if (l_get_enc_type(inq) != SENA_ENC_TYPE) { return (L_ENCL_INVALID_PATH); } /* * OK, so we have a photon... we can continue */ if ((l_state = (L_state *)calloc(1, sizeof (L_state))) == NULL) { return (L_MALLOC_FAILED); } if (err = l_get_status(ses_path, l_state, verbose)) { (void) l_free_lstate(&l_state); return (err); } if (!path_struct->slot_valid) { /* We are passing the disks path */ if (err = l_get_slot(path_struct, l_state, verbose)) { (void) l_free_lstate(&l_state); return (err); } } slot = path_struct->slot; (void) strcpy(dev_path, path_struct->p_physical_path); /* * Either front or rear drive */ if (path_struct->f_flag) { drive = &l_state->drv_front[slot]; } else { drive = &l_state->drv_rear[slot]; } /* * Check for drive presence always */ if (drive->ib_status.code == S_NOT_INSTALLED) { (void) l_free_lstate(&l_state); return (L_SLOT_EMPTY); } /* * Check disk state * before the power off. * */ if (power_off_flag && !force_flag) { goto pre_pwr_dwn; } else { goto pwr_up_dwn; } pre_pwr_dwn: /* * Check whether disk * is reserved by another * host */ if ((drive->g_disk_state.d_state_flags[PORT_A] & L_RESERVED) || (drive->g_disk_state.d_state_flags[PORT_B] & L_RESERVED)) { (void) l_free_lstate(&l_state); return (L_DEVICE_RESERVED); } if ((dl = (struct dlist *)g_zalloc(sizeof (struct dlist))) == NULL) { (void) l_free_lstate(&l_state); return (L_MALLOC_FAILED); } /* * NOTE: It is not necessary to get the multipath list here as ------ * we alread have it after getting the status earlier. * - REWRITE - */ /* * Get path to all the FC disk and tape devices. * * I get this now and pass down for performance * reasons. * If for some reason the list can become invalid, * i.e. device being offlined, then the list * must be re-gotten. */ if (err = g_get_wwn_list(&wwn_list, verbose)) { (void) g_destroy_data(dl); (void) l_free_lstate(&l_state); return (err); /* Failure */ } dl->dev_path = dev_path; if ((err = g_get_multipath(dev_path, &(dl->multipath), wwn_list, verbose)) != 0) { (void) g_destroy_data(dl); (void) g_free_wwn_list(&wwn_list); (void) l_free_lstate(&l_state); return (err); } for (dl1 = dl->multipath; dl1 != NULL; dl1 = dl1->next) { if ((devhdl = devctl_device_acquire(dl1->dev_path, DC_EXCL)) == NULL) { if (errno != EBUSY) { ER_DPRINTF("%s could not acquire" " the device: %s\n\n", strerror(errno), dl1->dev_path); continue; } } if (devctl_device_offline(devhdl) != 0) { (void) devctl_release(devhdl); (void) g_free_multipath(dl->multipath); (void) g_destroy_data(dl); (void) g_free_wwn_list(&wwn_list); (void) l_free_lstate(&l_state); return (L_POWER_OFF_FAIL_BUSY); } (void) devctl_release(devhdl); } pwr_up_dwn: err = pwr_up_down(ses_path, l_state, path_struct->f_flag, path_struct->slot, power_off_flag, verbose); if (dl != NULL) { (void) g_free_multipath(dl->multipath); (void) g_destroy_data(dl); } (void) g_free_wwn_list(&wwn_list); (void) l_free_lstate(&l_state); if (err) { return (err); } return (0); } /* * l_pho_pwr_up_down() Set the state of the Photon enclosure * the powered up/down mode. * The path must point to an IB. * * RETURNS: * 0 O.K. * non-zero otherwise */ int l_pho_pwr_up_down(char *dev_name, char *path_phys, int power_off_flag, int verbose, int force_flag) { L_state *l_state = NULL; int i, err = 0; struct dlist *dl, *dl1; char dev_path[MAXPATHLEN]; devctl_hdl_t devhdl; WWN_list *wwn_list = NULL; if (path_phys == NULL) { return (L_INVALID_PATH_FORMAT); } dl = (struct dlist *)NULL; if ((l_state = (L_state *)calloc(1, sizeof (L_state))) == NULL) { return (L_MALLOC_FAILED); } if (err = l_get_status(path_phys, l_state, verbose)) { (void) l_free_lstate(&l_state); return (err); } if (power_off_flag && !force_flag) { goto pre_pwr_dwn; } else { goto pwr_up_dwn; } pre_pwr_dwn: /* * Check if any disk in this enclosure * is reserved by another host before * the power off. */ for (i = 0; i < l_state->total_num_drv/2; i++) { if ((l_state->drv_front[i].g_disk_state.d_state_flags[PORT_A] & L_RESERVED) || (l_state->drv_front[i].g_disk_state.d_state_flags[PORT_B] & L_RESERVED) || (l_state->drv_rear[i].g_disk_state.d_state_flags[PORT_A] & L_RESERVED) || (l_state->drv_rear[i].g_disk_state.d_state_flags[PORT_B] & L_RESERVED)) { return (L_DISKS_RESERVED); } } /* * Check if any disk in this enclosure * Get path to all the FC disk and tape devices. * * I get this now and pass down for performance * reasons. * If for some reason the list can become invalid, * i.e. device being offlined, then the list * must be re-gotten. */ if (err = g_get_wwn_list(&wwn_list, verbose)) { (void) l_free_lstate(&l_state); return (err); /* Failure */ } for (i = 0; i < l_state->total_num_drv/2; i++) { if (*l_state->drv_front[i].g_disk_state.physical_path) { (void) memset(dev_path, 0, MAXPATHLEN); (void) strcpy(dev_path, (char *)&l_state->drv_front[i].g_disk_state.physical_path); if ((dl = (struct dlist *) g_zalloc(sizeof (struct dlist))) == NULL) { (void) g_free_wwn_list(&wwn_list); (void) l_free_lstate(&l_state); return (L_MALLOC_FAILED); } dl->dev_path = dev_path; if (g_get_multipath(dev_path, &(dl->multipath), wwn_list, verbose) != 0) { (void) g_destroy_data(dl); continue; } for (dl1 = dl->multipath; dl1 != NULL; dl1 = dl1->next) { /* attempt to acquire the device */ if ((devhdl = devctl_device_acquire( dl1->dev_path, DC_EXCL)) == NULL) { if (errno != EBUSY) { ER_DPRINTF("%s: Could not " "acquire the device: %s\n\n", strerror(errno), dl1->dev_path); continue; } } /* attempt to offline the device */ if (devctl_device_offline(devhdl) != 0) { (void) devctl_release(devhdl); (void) g_free_multipath( dl->multipath); (void) g_destroy_data(dl); (void) g_free_wwn_list(&wwn_list); (void) l_free_lstate(&l_state); return (L_POWER_OFF_FAIL_BUSY); } /* release handle acquired above */ (void) devctl_release(devhdl); } (void) g_free_multipath(dl->multipath); (void) g_destroy_data(dl); } if (*l_state->drv_rear[i].g_disk_state.physical_path) { (void) memset(dev_path, 0, MAXPATHLEN); (void) strcpy(dev_path, (char *)&l_state->drv_rear[i].g_disk_state.physical_path); if ((dl = (struct dlist *) g_zalloc(sizeof (struct dlist))) == NULL) { (void) g_free_wwn_list(&wwn_list); (void) l_free_lstate(&l_state); return (L_MALLOC_FAILED); } dl->dev_path = dev_path; if (g_get_multipath(dev_path, &(dl->multipath), wwn_list, verbose) != 0) { (void) g_destroy_data(dl); continue; } for (dl1 = dl->multipath; dl1 != NULL; dl1 = dl1->next) { /* attempt to acquire the device */ if ((devhdl = devctl_device_acquire( dl1->dev_path, DC_EXCL)) == NULL) { if (errno != EBUSY) { ER_DPRINTF("%s: Could not " "acquire the device: %s\n\n", strerror(errno), dl1->dev_path); continue; } } /* attempt to offline the device */ if (devctl_device_offline(devhdl) != 0) { (void) devctl_release(devhdl); (void) g_free_multipath( dl->multipath); (void) g_destroy_data(dl); (void) g_free_wwn_list(&wwn_list); (void) l_free_lstate(&l_state); return (L_POWER_OFF_FAIL_BUSY); } /* release handle acquired above */ (void) devctl_release(devhdl); } (void) g_free_multipath(dl->multipath); (void) g_destroy_data(dl); } } pwr_up_dwn: (void) g_free_wwn_list(&wwn_list); if ((err = pwr_up_down(path_phys, l_state, 0, -1, power_off_flag, verbose)) != 0) { (void) l_free_lstate(&l_state); return (err); } (void) l_free_lstate(&l_state); return (0); } /* * Set the state of the Photon enclosure or disk * powered up/down mode. * The path must point to an IB. * slot == -1 implies entire enclosure. * * RETURNS: * 0 O.K. * non-zero otherwise */ static int pwr_up_down(char *path_phys, L_state *l_state, int front, int slot, int power_off_flag, int verbose) { L_inquiry inq; int fd, status, err; uchar_t *page_buf; int front_index, rear_index, front_offset, rear_offset; unsigned short page_len; struct device_element *front_elem, *rear_elem; (void) memset(&inq, 0, sizeof (inq)); if ((fd = g_object_open(path_phys, O_NDELAY | O_RDONLY)) == -1) { return (L_OPEN_PATH_FAIL); } /* Verify it is a Photon */ if (status = g_scsi_inquiry_cmd(fd, (uchar_t *)&inq, sizeof (struct l_inquiry_struct))) { (void) close(fd); return (status); } if ((strstr((char *)inq.inq_pid, ENCLOSURE_PROD_ID) == 0) && (!(strncmp((char *)inq.inq_vid, "SUN ", sizeof (inq.inq_vid)) && ((inq.inq_dtype & DTYPE_MASK) == DTYPE_ESI)))) { (void) close(fd); return (L_ENCL_INVALID_PATH); } /* * To power up/down a Photon we use the Driver Off * bit in the global device control element. */ if ((page_buf = (uchar_t *)malloc(MAX_REC_DIAG_LENGTH)) == NULL) { return (L_MALLOC_FAILED); } if (err = l_get_envsen_page(fd, page_buf, MAX_REC_DIAG_LENGTH, L_PAGE_2, verbose)) { (void) close(fd); (void) g_destroy_data(page_buf); return (err); } page_len = (page_buf[2] << 8 | page_buf[3]) + HEADER_LEN; /* Double check slot as convert_name only does gross check */ if (slot >= l_state->total_num_drv/2) { (void) close(fd); (void) g_destroy_data(page_buf); return (L_INVALID_SLOT); } if (err = l_get_disk_element_index(l_state, &front_index, &rear_index)) { (void) close(fd); (void) g_destroy_data(page_buf); return (err); } /* Skip global element */ front_index++; rear_index++; front_offset = (8 + (front_index + slot)*4); rear_offset = (8 + (rear_index + slot)*4); front_elem = (struct device_element *)(page_buf + front_offset); rear_elem = (struct device_element *)(page_buf + rear_offset); if (front || slot == -1) { /* * now do requested action. */ bzero(front_elem, sizeof (struct device_element)); /* Set/reset power off bit */ front_elem->dev_off = power_off_flag; front_elem->select = 1; } if (!front || slot == -1) { /* Now do rear */ bzero(rear_elem, sizeof (struct device_element)); /* Set/reset power off bit */ rear_elem->dev_off = power_off_flag; rear_elem->select = 1; } if (getenv("_LUX_D_DEBUG") != NULL) { if (front || slot == -1) { g_dump(" pwr_up_down: " "Front Device Status Element ", (uchar_t *)front_elem, sizeof (struct device_element), HEX_ONLY); } if (!front || slot == -1) { g_dump(" pwr_up_down: " "Rear Device Status Element ", (uchar_t *)rear_elem, sizeof (struct device_element), HEX_ONLY); } } if (err = g_scsi_send_diag_cmd(fd, (uchar_t *)page_buf, page_len)) { (void) close(fd); (void) g_destroy_data(page_buf); return (err); } (void) close(fd); (void) g_destroy_data(page_buf); return (0); } /* * Set the password of the FPM by sending the password * in page 4 of the Send Diagnostic command. * * The path must point to an IB. * * The size of the password string must be <= 8 bytes. * The string can also be NULL. This is the way the user * chooses to not have a password. * * I then tell the photon by giving him 4 NULL bytes. * * RETURNS: * 0 O.K. * non-zero otherwise */ int l_new_password(char *path_phys, char *password) { Page4_name page4; L_inquiry inq; int fd, status; (void) memset(&inq, 0, sizeof (inq)); (void) memset(&page4, 0, sizeof (page4)); if ((fd = g_object_open(path_phys, O_NDELAY | O_RDONLY)) == -1) { return (L_OPEN_PATH_FAIL); } /* Verify it is a Photon */ if (status = g_scsi_inquiry_cmd(fd, (uchar_t *)&inq, sizeof (struct l_inquiry_struct))) { (void) close(fd); return (status); } if ((strstr((char *)inq.inq_pid, ENCLOSURE_PROD_ID) == 0) && (!(strncmp((char *)inq.inq_vid, "SUN ", sizeof (inq.inq_vid)) && ((inq.inq_dtype & DTYPE_MASK) == DTYPE_ESI)))) { (void) close(fd); return (L_ENCL_INVALID_PATH); } page4.page_code = L_PAGE_4; page4.page_len = (ushort_t)max((strlen(password) + 4), 8); /* Double check */ if (strlen(password) > 8) { return (L_INVALID_PASSWORD_LEN); } page4.string_code = L_PASSWORD; page4.enable = 1; (void) strcpy((char *)page4.name, password); if (status = g_scsi_send_diag_cmd(fd, (uchar_t *)&page4, page4.page_len + HEADER_LEN)) { (void) close(fd); return (status); } (void) close(fd); return (0); } /* * Set the name of the enclosure by sending the name * in page 4 of the Send Diagnostic command. * * The path must point to an IB. * * RETURNS: * 0 O.K. * non-zero otherwise */ int l_new_name(char *path_phys, char *name) { Page4_name page4; L_inquiry inq; int fd, status; if ((path_phys == NULL) || (name == NULL)) { return (L_INVALID_PATH_FORMAT); } (void) memset(&inq, 0, sizeof (inq)); (void) memset(&page4, 0, sizeof (page4)); if ((fd = g_object_open(path_phys, O_NDELAY | O_RDONLY)) == -1) { return (L_OPEN_PATH_FAIL); } /* Verify it is a Photon */ if (status = g_scsi_inquiry_cmd(fd, (uchar_t *)&inq, sizeof (struct l_inquiry_struct))) { (void) close(fd); return (status); } if ((strstr((char *)inq.inq_pid, ENCLOSURE_PROD_ID) == 0) && (!(strncmp((char *)inq.inq_vid, "SUN ", sizeof (inq.inq_vid)) && ((inq.inq_dtype & DTYPE_MASK) == DTYPE_ESI)))) { (void) close(fd); return (L_ENCL_INVALID_PATH); } page4.page_code = L_PAGE_4; page4.page_len = (ushort_t)((sizeof (struct page4_name) - 4)); page4.string_code = L_ENCL_NAME; page4.enable = 1; strncpy((char *)page4.name, name, sizeof (page4.name)); if (status = g_scsi_send_diag_cmd(fd, (uchar_t *)&page4, sizeof (page4))) { (void) close(fd); return (status); } /* * Check the name really changed. */ if (status = g_scsi_inquiry_cmd(fd, (uchar_t *)&inq, sizeof (struct l_inquiry_struct))) { (void) close(fd); return (status); } if (strncmp((char *)inq.inq_box_name, name, sizeof (page4.name)) != 0) { char name_buf[MAXNAMELEN]; (void) close(fd); strncpy((char *)name_buf, (char *)inq.inq_box_name, sizeof (inq.inq_box_name)); return (L_ENCL_NAME_CHANGE_FAIL); } (void) close(fd); return (0); } /* * Issue a Loop Port enable Primitive sequence * to the device specified by the pathname. */ int l_enable(char *path, int verbose) /*ARGSUSED*/ { return (0); } /* * Issue a Loop Port Bypass Primitive sequence * to the device specified by the pathname. This requests the * device to set its L_Port into the bypass mode. */ int l_bypass(char *path, int verbose) /*ARGSUSED*/ { return (0); } /* * Create a linked list of all the Photon enclosures that * are attached to this host. * * RETURN VALUES: 0 O.K. * * box_list pointer: * NULL: No enclosures found. * !NULL: Enclosures found * box_list points to a linked list of boxes. */ int l_get_box_list(struct box_list_struct **box_list_ptr, int verbose) { char *dev_name; DIR *dirp; struct dirent *entp; char namebuf[MAXPATHLEN]; struct stat sb; char *result = NULL; int fd, status; L_inquiry inq; Box_list *box_list, *l1, *l2; IB_page_config page1; uchar_t node_wwn[WWN_SIZE], port_wwn[WWN_SIZE]; int al_pa; if (box_list_ptr == NULL) { return (L_INVALID_PATH_FORMAT); } box_list = *box_list_ptr = NULL; if ((dev_name = (char *)g_zalloc(sizeof ("/dev/es"))) == NULL) { return (L_MALLOC_FAILED); } (void) sprintf((char *)dev_name, "/dev/es"); if (verbose) { (void) fprintf(stdout, MSGSTR(9045, " Searching directory %s for links to enclosures\n"), dev_name); } if ((dirp = opendir(dev_name)) == NULL) { (void) g_destroy_data(dev_name); /* No Photons found */ B_DPRINTF(" l_get_box_list: No Photons found\n"); return (0); } while ((entp = readdir(dirp)) != NULL) { if (strcmp(entp->d_name, ".") == 0 || strcmp(entp->d_name, "..") == 0) continue; (void) sprintf(namebuf, "%s/%s", dev_name, entp->d_name); if ((lstat(namebuf, &sb)) < 0) { ER_DPRINTF("Warning: Cannot stat %s\n", namebuf); continue; } if (!S_ISLNK(sb.st_mode)) { ER_DPRINTF("Warning: %s is not a symbolic link\n", namebuf); continue; } if ((result = g_get_physical_name_from_link(namebuf)) == NULL) { ER_DPRINTF(" Warning: Get physical name from" " link failed. Link=%s\n", namebuf); continue; } /* Found a SES card. */ B_DPRINTF(" l_get_box_list: Link to SES Card found: %s/%s\n", dev_name, entp->d_name); if ((fd = g_object_open(result, O_NDELAY | O_RDONLY)) == -1) { g_destroy_data(result); continue; /* Ignore errors */ } /* Get the box name */ if (status = g_scsi_inquiry_cmd(fd, (uchar_t *)&inq, sizeof (struct l_inquiry_struct))) { (void) close(fd); g_destroy_data(result); continue; /* Ignore errors */ } if ((strstr((char *)inq.inq_pid, ENCLOSURE_PROD_ID) != NULL) || (((inq.inq_dtype & DTYPE_MASK) == DTYPE_ESI) && (l_get_enc_type(inq) == DAK_ENC_TYPE))) { /* * Found Photon/Daktari */ /* Get the port WWN from the IB, page 1 */ if ((status = l_get_envsen_page(fd, (uchar_t *)&page1, sizeof (page1), 1, 0)) != NULL) { (void) close(fd); g_destroy_data(result); (void) g_destroy_data(dev_name); closedir(dirp); return (status); } /* * Build list of names. */ if ((l2 = (struct box_list_struct *) g_zalloc(sizeof (struct box_list_struct))) == NULL) { (void) close(fd); g_destroy_data(result); g_destroy_data(dev_name); closedir(dirp); return (L_MALLOC_FAILED); } /* Fill in structure */ (void) strcpy((char *)l2->b_physical_path, (char *)result); (void) strcpy((char *)l2->logical_path, (char *)namebuf); bcopy((void *)page1.enc_node_wwn, (void *)l2->b_node_wwn, WWN_SIZE); (void) sprintf(l2->b_node_wwn_s, "%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x", page1.enc_node_wwn[0], page1.enc_node_wwn[1], page1.enc_node_wwn[2], page1.enc_node_wwn[3], page1.enc_node_wwn[4], page1.enc_node_wwn[5], page1.enc_node_wwn[6], page1.enc_node_wwn[7]); strncpy((char *)l2->prod_id_s, (char *)inq.inq_pid, sizeof (inq.inq_pid)); strncpy((char *)l2->b_name, (char *)inq.inq_box_name, sizeof (inq.inq_box_name)); /* make sure null terminated */ l2->b_name[sizeof (l2->b_name) - 1] = NULL; /* * Now get the port WWN for the port * we are connected to. */ status = g_get_wwn(result, port_wwn, node_wwn, &al_pa, verbose); if (status == 0) { (void) sprintf(l2->b_port_wwn_s, "%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x", port_wwn[0], port_wwn[1], port_wwn[2], port_wwn[3], port_wwn[4], port_wwn[5], port_wwn[6], port_wwn[7]); bcopy((void *)port_wwn, (void *)l2->b_port_wwn, WWN_SIZE); B_DPRINTF(" l_get_box_list:" " Found enclosure named:%s\n", l2->b_name); if (box_list == NULL) { l1 = box_list = l2; } else { l2->box_prev = l1; l1 = l1->box_next = l2; } } else { (void) close(fd); g_destroy_data(result); (void) g_destroy_data(dev_name); (void) g_destroy_data(l2); closedir(dirp); return (status); } } g_destroy_data(result); (void) close(fd); *box_list_ptr = box_list; /* pass back ptr to list */ } (void) g_destroy_data(dev_name); closedir(dirp); return (0); } void l_free_box_list(struct box_list_struct **box_list) { Box_list *next = NULL; if (box_list == NULL) { return; } for (; *box_list != NULL; *box_list = next) { next = (*box_list)->box_next; (void) g_destroy_data(*box_list); } *box_list = NULL; } /* * Finds out if there are any other boxes * with the same name as "name". * * RETURNS: * 0 There are no other boxes with the same name. * >0 if duplicate names found */ /*ARGSUSED*/ int l_duplicate_names(Box_list *b_list, char wwn[], char *name, int verbose) { int dup_flag = 0; Box_list *box_list_ptr = NULL; if ((name == NULL) || (wwn == NULL)) return (0); box_list_ptr = b_list; while (box_list_ptr != NULL) { if ((strcmp(name, (const char *)box_list_ptr->b_name) == 0) && (strcmp(box_list_ptr->b_node_wwn_s, wwn) != 0)) { dup_flag++; break; } box_list_ptr = box_list_ptr->box_next; } return (dup_flag); } /* * Checks for a name conflict with an SSA cN type name. */ int l_get_conflict(char *name, char **result, int verbose) { char s[MAXPATHLEN]; char *p = NULL; char *pp = NULL; Box_list *box_list = NULL; int found_box = 0, err = 0; (void) strcpy(s, name); if ((*result = g_get_physical_name(s)) == NULL) { return (0); } if ((strstr((const char *)*result, PLNDEF)) == NULL) { (void) g_destroy_data(*result); *result = NULL; return (0); } P_DPRINTF(" l_get_conflict: Found " "SSA path using %s\n", s); /* Find path to IB */ if ((err = l_get_box_list(&box_list, verbose)) != 0) { return (err); /* Failure */ } /* * Valid cN type name found. */ while (box_list != NULL) { if ((strcmp((char *)s, (char *)box_list->b_name)) == 0) { found_box = 1; if (p == NULL) { if ((p = g_zalloc(strlen( box_list->b_physical_path) + 2)) == NULL) { (void) l_free_box_list(&box_list); return (errno); } } else { if ((pp = g_zalloc(strlen( box_list->b_physical_path) + strlen(p) + 2)) == NULL) { (void) l_free_box_list(&box_list); return (errno); } (void) strcpy(pp, p); (void) g_destroy_data(p); p = pp; } (void) strcat(p, box_list->b_physical_path); (void) strcat(p, "\n"); } box_list = box_list->box_next; } if (found_box) { D_DPRINTF("There is a conflict between the " "enclosure\nwith this name, %s, " "and a SSA name of the same form.\n" "Please use one of the following physical " "pathnames:\n%s\n%s\n", s, *result, p); (void) l_free_box_list(&box_list); (void) g_destroy_data(p); return (L_SSA_CONFLICT); /* failure */ } (void) l_free_box_list(&box_list); return (0); } /* * This function sets the "slot", "slot_valid" and "f_flag" fields of the * path_struct that is passed in IFF the device path passed in ("phys_path") * is a disk in an A5K or a Daktari. This is achieved by calling l_get_slot(). * * INPUT : * phys_path - physical path to a device * path_sturct - Pointer to pointer to a path_struct data structure * * OUTPUT : * if phys_path is that of an A5K/Daktari disk * path_struct->slot is set to the slot position in enclosure * path_struct->slot_valid is set to 1 * path_struct->f_flag is set to 1 if in the front of an A5k * or if among the first 6 disks on a Daktari * else * they are left as they were * RETURNS: * 0 on SUCCESS * non-zero otherwise */ static int load_flds_if_enc_disk(char *phys_path, struct path_struct **path_struct) { int err = 0, verbose = 0; char ses_path[MAXPATHLEN]; gfc_map_t map; L_inquiry inq; L_state *l_state = NULL; if ((path_struct == NULL) || (*path_struct == NULL) || (phys_path == NULL) || (*phys_path == NULL)) { return (L_INVALID_PATH_FORMAT); } if ((strstr(phys_path, SLSH_DRV_NAME_SSD) == NULL) || (g_get_path_type(phys_path) == 0)) { /* * Don't proceed when not a disk device or if it is not a * valid FC device on which g_get_dev_map() can be done * (for example, g_get_dev_map() will fail on SSAs). * * Just return success */ return (0); } if ((*path_struct)->ib_path_flag) { /* * If this flag is set, l_get_slot() should not be called * So, no point in proceeding. Just return success. */ return (0); } if ((err = g_get_dev_map(phys_path, &map, verbose)) != 0) { return (err); } if ((err = l_get_ses_path(phys_path, ses_path, &map, verbose)) != 0) { (void) free(map.dev_addr); if (err == L_NO_SES_PATH) { /* * This is not an error since this could be a device * which does not have SES nodes */ return (0); } return (err); } /* * There is a SES path on the same FCA as the given disk. But if the * SES node is not of a photon/Daktari, we dont proceed */ if ((err = g_get_inquiry(ses_path, &inq)) != 0) { (void) free(map.dev_addr); return (err); } /* * only want to continue if this is a photon or a Daktari * * if product ID is not SENA or VID is not "SUN" (checks for photon) * and if enclosure type is not a Daktari, then I return */ if (((strstr((char *)inq.inq_pid, ENCLOSURE_PROD_ID) == 0) || (strncmp((char *)inq.inq_vid, "SUN ", sizeof (inq.inq_vid)) != 0)) && ((l_get_enc_type(inq) != DAK_ENC_TYPE))) { /* Not a photon/Daktari */ (void) free(map.dev_addr); return (0); } /* Now, set some fields that l_get_slot() uses and then call it */ if ((l_state = (L_state *)g_zalloc(sizeof (L_state))) == NULL) { (void) free(map.dev_addr); return (L_MALLOC_FAILED); } if ((err = l_get_ib_status(ses_path, l_state, verbose)) != 0) { (void) free(map.dev_addr); (void) l_free_lstate(&l_state); return (err); } if ((err = l_get_slot(*path_struct, l_state, verbose)) != 0) { (void) free(map.dev_addr); (void) l_free_lstate(&l_state); return (err); } (void) free(map.dev_addr); (void) l_free_lstate(&l_state); return (0); } /* * convert box name or WWN or logical path to physical path. * * OUTPUT: * path_struct: * - This structure is used to return more detailed * information about the path. * - *p_physical_path * Normally this is the requested physical path. * If the requested path is not found then iff the * ib_path_flag is set this is the IB path. * - *argv * This is the argument variable input. e.g. Bob,f1 * - slot_valid * - slot * This is the slot number that was entered when using * the box,[fr]slot format. It is only valid if the * slot_valid flag is set. * - f_flag * Front flag - If set, the requested device is located in the * front of the enclosure. * - ib_path_flag * If this flag is set it means a devices path was requested * but could not be found but an IB's path was found and * the p_physical_path points to that path. * - **phys_path * physical path to the device. * RETURNS: * - 0 if O.K. * - error otherwise. */ int l_convert_name(char *name, char **phys_path, struct path_struct **path_struct, int verbose) { char tmp_name[MAXPATHLEN], ses_path[MAXPATHLEN]; char *char_ptr, *ptr = NULL; char *result = NULL; char *env = NULL; char save_frd; /* which designator was it? */ int slot = 0, slot_flag = 0, found_box = 0, found_comma = 0; int err = 0, enc_type = 0; hrtime_t start_time, end_time; Box_list *box_list = NULL, *box_list_ptr = NULL; L_inquiry inq; L_state *l_state = NULL; Path_struct *path_ptr = NULL; WWN_list *wwn_list, *wwn_list_ptr; if ((name == NULL) || (phys_path == NULL) || (path_struct == NULL)) { return (L_INVALID_PATH_FORMAT); } if ((env = getenv("_LUX_T_DEBUG")) != NULL) { start_time = gethrtime(); } if ((*path_struct = path_ptr = (struct path_struct *) g_zalloc(sizeof (struct path_struct))) == NULL) { return (L_MALLOC_FAILED); } *phys_path = NULL; /* * If the path contains a "/" then assume * it is a logical or physical path as the * box name or wwn can not contain "/"s. */ if (strchr(name, '/') != NULL) { if ((result = g_get_physical_name(name)) == NULL) { return (L_NO_PHYS_PATH); } path_ptr->p_physical_path = result; /* * Make sure it's a disk or tape path */ if (strstr(name, DEV_RDIR) || strstr(name, SLSH_DRV_NAME_SSD) || strstr(name, DEV_TAPE_DIR) || strstr(name, SLSH_DRV_NAME_ST)) { if ((err = g_get_inquiry(result, &inq)) != 0) { (void) free(result); return (L_SCSI_ERROR); } /* * Check to see if it is not a * A5K/v880/v890 disk * */ if (!g_enclDiskChk((char *)inq.inq_vid, (char *)inq.inq_pid)) { path_ptr->argv = name; *phys_path = result; return (0); } } if (err = load_flds_if_enc_disk(result, path_struct)) { (void) free(result); return (err); } goto done; } (void) strcpy(tmp_name, name); if ((tmp_name[0] == 'c') && ((int)strlen(tmp_name) > 1) && ((int)strlen(tmp_name) < 5)) { if ((err = l_get_conflict(tmp_name, &result, verbose)) != 0) { if (result != NULL) { (void) g_destroy_data(result); } return (err); } if (result != NULL) { path_ptr->p_physical_path = result; if ((err = g_get_inquiry(result, &inq)) != 0) { (void) free(result); return (L_SCSI_ERROR); } /* * Check to see if it is a supported * A5K/v880/v890 storage subsystem disk */ if (g_enclDiskChk((char *)inq.inq_vid, (char *)inq.inq_pid)) { if (err = load_flds_if_enc_disk( result, path_struct)) { (void) free(result); return (err); } } goto done; } } /* * Check to see if we have a box or WWN name. * * If it contains a , then the format must be * box_name,f1 where f is front and 1 is the slot number * or it is a format like * ssd@w2200002037049adf,0:h,raw * or * SUNW,pln@a0000000,77791d:ctlr */ if (((char_ptr = strstr(tmp_name, ",")) != NULL) && ((*(char_ptr + 1) == 'f') || (*(char_ptr + 1) == 'r') || (*(char_ptr + 1) == 's'))) { char_ptr++; /* point to f/r */ if ((*char_ptr == 'f') || (*char_ptr == 's')) { path_ptr->f_flag = 1; } else if (*char_ptr != 'r') { return (L_INVALID_PATH_FORMAT); } save_frd = (char)*char_ptr; /* save it */ char_ptr++; slot = strtol(char_ptr, &ptr, 10); /* * NOTE: Need to double check the slot when we get * the number of the devices actually in the box. */ if ((slot < 0) || (ptr == char_ptr) || ((save_frd == 's' && slot >= MAX_DRIVES_DAK) || ((save_frd != 's' && slot >= (MAX_DRIVES_PER_BOX/2))))) { return (L_INVALID_SLOT); } /* Say slot valid. */ slot_flag = path_ptr->slot_valid = 1; if (save_frd == 's' && slot >= (MAX_DRIVES_DAK/2)) { path_ptr->slot = slot = slot % (MAX_DRIVES_DAK/2); path_ptr->f_flag = 0; } else path_ptr->slot = slot; } if (((char_ptr = strstr(tmp_name, ",")) != NULL) && ((*(char_ptr + 1) == 'f') || (*(char_ptr + 1) == 'r') || (*(char_ptr + 1) == 's'))) { *char_ptr = NULL; /* make just box name */ found_comma = 1; } /* Find path to IB */ if ((err = l_get_box_list(&box_list, verbose)) != 0) { (void) l_free_box_list(&box_list); return (err); } box_list_ptr = box_list; /* Look for box name. */ while (box_list != NULL) { if ((strcmp((char *)tmp_name, (char *)box_list->b_name)) == 0) { result = g_alloc_string(box_list->b_physical_path); L_DPRINTF(" l_convert_name:" " Found subsystem: name %s WWN %s\n", box_list->b_name, box_list->b_node_wwn_s); /* * Check for another box with this name. */ if (l_duplicate_names(box_list_ptr, box_list->b_node_wwn_s, (char *)box_list->b_name, verbose)) { (void) l_free_box_list(&box_list_ptr); (void) g_destroy_data(result); return (L_DUPLICATE_ENCLOSURES); } found_box = 1; break; } box_list = box_list->box_next; } /* * Check to see if we must get individual disks path. */ if (found_box && slot_flag) { if ((l_state = (L_state *)g_zalloc(sizeof (L_state))) == NULL) { (void) g_destroy_data(result); (void) l_free_box_list(&box_list_ptr); return (L_MALLOC_FAILED); } (void) strcpy(ses_path, result); if ((err = l_get_status(ses_path, l_state, verbose)) != 0) { (void) g_destroy_data(result); (void) g_destroy_data(l_state); (void) l_free_box_list(&box_list_ptr); return (err); } /* * Now double check the slot number. */ if (slot >= l_state->total_num_drv/2) { path_ptr->slot_valid = 0; (void) g_destroy_data(result); (void) l_free_box_list(&box_list_ptr); (void) l_free_lstate(&l_state); return (L_INVALID_SLOT); } /* Only allow the single slot version for Daktari */ if (g_get_inquiry(ses_path, &inq)) { return (L_SCSI_ERROR); } enc_type = l_get_enc_type(inq); if (((enc_type == DAK_ENC_TYPE) && (save_frd != 's')) || ((enc_type != DAK_ENC_TYPE) && (save_frd == 's'))) { path_ptr->slot_valid = 0; (void) g_destroy_data(result); (void) l_free_box_list(&box_list_ptr); (void) l_free_lstate(&l_state); return (L_INVALID_SLOT); } if (path_ptr->f_flag) { if (*l_state->drv_front[slot].g_disk_state.physical_path) { result = g_alloc_string(l_state->drv_front[slot].g_disk_state.physical_path); } else { /* Result is the IB path */ path_ptr->ib_path_flag = 1; path_ptr->p_physical_path = g_alloc_string(result); (void) g_destroy_data(result); result = NULL; } } else { if (*l_state->drv_rear[slot].g_disk_state.physical_path) { result = g_alloc_string(l_state->drv_rear[slot].g_disk_state.physical_path); } else { /* Result is the IB path */ path_ptr->ib_path_flag = 1; path_ptr->p_physical_path = g_alloc_string(result); (void) g_destroy_data(result); result = NULL; } } (void) l_free_lstate(&l_state); goto done; } if (found_box || found_comma) { goto done; } /* * No luck with the box name. * * Try WWN's */ /* Look for the SES's WWN */ box_list = box_list_ptr; while (box_list != NULL) { if (((strcasecmp((char *)tmp_name, (char *)box_list->b_port_wwn_s)) == 0) || ((strcasecmp((char *)tmp_name, (char *)box_list->b_node_wwn_s)) == 0)) { result = g_alloc_string(box_list->b_physical_path); L_DPRINTF(" l_convert_name:" " Found subsystem using the WWN" ": name %s WWN %s\n", box_list->b_name, box_list->b_node_wwn_s); goto done; } box_list = box_list->box_next; } /* Look for a device's WWN */ if (strlen(tmp_name) <= L_WWN_LENGTH) { if ((err = g_get_wwn_list(&wwn_list, verbose)) != 0) { (void) l_free_box_list(&box_list_ptr); return (err); } for (wwn_list_ptr = wwn_list; wwn_list_ptr != NULL; wwn_list_ptr = wwn_list_ptr->wwn_next) { if (((strcasecmp((char *)tmp_name, (char *)wwn_list_ptr->node_wwn_s)) == 0) || ((strcasecmp((char *)tmp_name, (char *)wwn_list_ptr->port_wwn_s)) == 0)) { /* * Found the device's WWN in the global WWN list. * It MAY be in a photon/Daktari. If it is, we'll set * additional fields in path_struct. */ result = g_alloc_string(wwn_list_ptr->physical_path); L_DPRINTF(" l_convert_name:" " Found device: WWN %s Path %s\n", tmp_name, wwn_list_ptr->logical_path); (void) g_free_wwn_list(&wwn_list); /* * Now check if it is a disk in an A5K and set * path_struct fields */ path_ptr->p_physical_path = result; if ((err = g_get_inquiry(result, &inq)) != 0) { (void) free(result); return (L_SCSI_ERROR); } /* * Check to see if it is a supported * A5K/v880/v890 storage subsystem disk */ if (g_enclDiskChk((char *)inq.inq_vid, (char *)inq.inq_pid)) { if (err = load_flds_if_enc_disk( result, path_struct)) { (void) free(result); return (err); } } goto done; } } } /* * Try again in case we were in the /dev * or /devices directory. */ result = g_get_physical_name(name); done: (void) l_free_box_list(&box_list_ptr); path_ptr->argv = name; if (result == NULL) { if (!path_ptr->ib_path_flag) return (-1); } else { path_ptr->p_physical_path = result; } L_DPRINTF(" l_convert_name: path_struct:\n\tphysical_path:\n\t %s\n" "\targv:\t\t%s" "\n\tslot_valid\t%d" "\n\tslot\t\t%d" "\n\tf_flag\t\t%d" "\n\tib_path_flag\t%d\n", path_ptr->p_physical_path, path_ptr->argv, path_ptr->slot_valid, path_ptr->slot, path_ptr->f_flag, path_ptr->ib_path_flag); if (env != NULL) { end_time = gethrtime(); (void) fprintf(stdout, " l_convert_name: " "Time = %lld millisec\n", (end_time - start_time)/1000000); } if (path_ptr->ib_path_flag) return (-1); *phys_path = result; return (0); } /* * Gets envsen information of an enclosure from IB * * RETURNS: * 0 O.K. * non-zero otherwise */ int l_get_envsen_page(int fd, uchar_t *buf, int buf_size, uchar_t page_code, int verbose) { Rec_diag_hdr hdr; uchar_t *pg; int size, new_size, status; if (buf == NULL) { return (L_INVALID_BUF_LEN); } if (verbose) { (void) fprintf(stdout, MSGSTR(9046, " Reading SES page %x\n"), page_code); } (void) memset(&hdr, 0, sizeof (struct rec_diag_hdr)); if (status = g_scsi_rec_diag_cmd(fd, (uchar_t *)&hdr, sizeof (struct rec_diag_hdr), page_code)) { return (status); } /* Check */ if ((hdr.page_code != page_code) || (hdr.page_len == 0)) { return (L_RD_PG_INVLD_CODE); } size = HEADER_LEN + hdr.page_len; /* * Because of a hardware restriction in the soc+ chip * the transfers must be word aligned. */ while (size & 0x03) { size++; if (size > buf_size) { return (L_RD_PG_MIN_BUFF); } P_DPRINTF(" l_get_envsen_page: Adjusting size of the " "g_scsi_rec_diag_cmd buffer.\n"); } if ((pg = (uchar_t *)g_zalloc(size)) == NULL) { return (L_MALLOC_FAILED); } P_DPRINTF(" l_get_envsen_page: Reading page %x of size 0x%x\n", page_code, size); if (status = g_scsi_rec_diag_cmd(fd, pg, size, page_code)) { (void) g_destroy_data((char *)pg); return (status); } new_size = MIN(size, buf_size); bcopy((const void *)pg, (void *)buf, (size_t)new_size); (void) g_destroy_data(pg); return (0); } /* * Get consolidated copy of all environmental information * into buf structure. * * RETURNS: * 0 O.K. * non-zero otherwise */ int l_get_envsen(char *path_phys, uchar_t *buf, int size, int verbose) { int fd, rval; uchar_t *page_list_ptr, page_code, *local_buf_ptr = buf; Rec_diag_hdr *hdr = (struct rec_diag_hdr *)(void *)buf; ushort_t num_pages; if ((path_phys == NULL) || (buf == NULL)) { return (L_INVALID_PATH_FORMAT); } page_code = L_PAGE_PAGE_LIST; /* open IB */ if ((fd = g_object_open(path_phys, O_NDELAY | O_RDONLY)) == -1) return (L_OPEN_PATH_FAIL); P_DPRINTF(" l_get_envsen: Getting list of supported" " pages from IB\n"); if (verbose) { (void) fprintf(stdout, MSGSTR(9047, " Getting list of supported pages from IB\n")); } /* Get page 0 */ if ((rval = l_get_envsen_page(fd, local_buf_ptr, size, page_code, verbose)) != NULL) { (void) close(fd); return (rval); } page_list_ptr = buf + HEADER_LEN + 1; /* +1 to skip page 0 */ num_pages = hdr->page_len - 1; /* * check whether the number of pages received * from IB are valid. SENA enclosure * supports only 8 pages of sense information. * According to SES specification dpANS X3.xxx-1997 * X3T10/Project 1212-D/Rev 8a, the enclosure supported * pages can go upto L_MAX_POSSIBLE_PAGES (0xFF). * Return an error if no. of pages exceeds L_MAX_POSSIBLE_PAGES. * See if (num_pages >= L_MAX_POSSIBLE_PAGES) since 1 page (page 0) * was already subtracted from the total number of pages before. */ if (num_pages < 1 || num_pages >= L_MAX_POSSIBLE_PAGES) { return (L_INVALID_NO_OF_ENVSEN_PAGES); } /* * Buffer size of MAX_REC_DIAG_LENGTH can be small if the * number of pages exceed more than L_MAX_SENAIB_PAGES * but less than L_MAX_POSSIBLE_PAGES. */ if (size == MAX_REC_DIAG_LENGTH && num_pages >= L_MAX_SENAIB_PAGES) { return (L_INVALID_BUF_LEN); } /* Align buffer */ while (hdr->page_len & 0x03) { hdr->page_len++; } local_buf_ptr += HEADER_LEN + hdr->page_len; /* * Getting all pages and appending to buf */ for (; num_pages--; page_list_ptr++) { /* * The fifth byte of page 0 is the start * of the list of pages not including page 0. */ page_code = *page_list_ptr; if ((rval = l_get_envsen_page(fd, local_buf_ptr, size, page_code, verbose)) != NULL) { (void) close(fd); return (rval); } hdr = (struct rec_diag_hdr *)(void *)local_buf_ptr; local_buf_ptr += HEADER_LEN + hdr->page_len; } (void) close(fd); return (0); } /* * Get the individual disk status. * Path must be physical and point to a disk. * * This function updates the d_state_flags, port WWN's * and num_blocks for all accessiable ports * in l_disk_state->g_disk_state structure. * * RETURNS: * 0 O.K. * non-zero otherwise */ int l_get_disk_status(char *path, struct l_disk_state_struct *l_disk_state, WWN_list *wwn_list, int verbose) { struct dlist *ml; char path_a[MAXPATHLEN], path_b[MAXPATHLEN], ses_path[MAXPATHLEN]; gfc_map_t map; int path_a_found = 0, path_b_found = 0, local_port_a_flag; uchar_t node_wwn[WWN_SIZE], port_wwn[WWN_SIZE]; int al_pa, err, pathcnt = 1; int i = 0; char temppath[MAXPATHLEN]; mp_pathlist_t pathlist; char pwwn[WWN_S_LEN]; struct stat sbuf; if ((path == NULL) || (l_disk_state == NULL)) { return (L_INVALID_PATH_FORMAT); } /* Check device name */ if (stat(path, &sbuf) || (sbuf.st_rdev == NODEV)) { G_DPRINTF(" l_get_disk_status: invalid device %s\n", path); return (L_INVALID_PATH); } /* Initialize */ *path_a = *path_b = NULL; l_disk_state->g_disk_state.num_blocks = 0; /* Get paths. */ g_get_multipath(path, &(l_disk_state->g_disk_state.multipath_list), wwn_list, verbose); ml = l_disk_state->g_disk_state.multipath_list; if (ml == NULL) { l_disk_state->l_state_flag = L_NO_PATH_FOUND; G_DPRINTF(" l_get_disk_status: Error finding a " "multipath to the disk.\n"); return (0); } if (strstr(path, SCSI_VHCI) != NULL) { /* * It is an MPXIO Path */ (void) strcpy(temppath, path); if (g_get_pathlist(temppath, &pathlist)) { return (0); } pathcnt = pathlist.path_count; for (i = 0; i < pathcnt; i++) { /* * Skip inactive paths. * A path that is not in either * MDI_PATHINFO_STATE_ONLINE or * MDI_PATHINFO_STATE_STANDBY state is not * an active path. * * When a disk port is bypassed and mpxio is * enabled, the path_state for that path goes to the * offline state */ if (pathlist.path_info[i].path_state != MDI_PATHINFO_STATE_ONLINE && pathlist.path_info[i].path_state != MDI_PATHINFO_STATE_STANDBY) { continue; } (void) strncpy(pwwn, pathlist.path_info[i].path_addr, L_WWN_LENGTH); pwwn[L_WWN_LENGTH] = '\0'; if (!(path_a_found || path_b_found)) { if (pwwn[1] == '1') { local_port_a_flag = 1; } else { local_port_a_flag = 0; } } else if (path_a_found && (strstr(l_disk_state->g_disk_state.port_a_wwn_s, pwwn) == NULL)) { /* do port b */ local_port_a_flag = 0; } else if (path_b_found && (strstr(l_disk_state->g_disk_state.port_b_wwn_s, pwwn) == NULL)) { /* do port a */ local_port_a_flag = 1; } if (err = l_get_disk_port_status(path, l_disk_state, local_port_a_flag, verbose)) { return (err); } if (local_port_a_flag && (!path_a_found)) { (void) strcpy(l_disk_state-> g_disk_state.port_a_wwn_s, pwwn); l_disk_state->g_disk_state.port_a_valid++; path_a_found++; } if ((!local_port_a_flag) && (!path_b_found)) { (void) strcpy(l_disk_state-> g_disk_state.port_b_wwn_s, pwwn); l_disk_state->g_disk_state.port_b_valid++; path_b_found++; } } free(pathlist.path_info); return (0); } while (ml && (!(path_a_found && path_b_found))) { if (err = g_get_dev_map(ml->dev_path, &map, verbose)) { (void) g_free_multipath(ml); return (err); } if ((err = l_get_ses_path(ml->dev_path, ses_path, &map, verbose)) != 0) { (void) g_free_multipath(ml); free((void *)map.dev_addr); return (err); } free((void *)map.dev_addr); /* Not used anymore */ /* * Get the port, A or B, of the disk, * by passing the IB path. */ if (err = l_get_port(ses_path, &local_port_a_flag, verbose)) { (void) g_free_multipath(ml); return (err); } if (local_port_a_flag && (!path_a_found)) { G_DPRINTF(" l_get_disk_status: Path to Port A " "found: %s\n", ml->dev_path); if (err = l_get_disk_port_status(ml->dev_path, l_disk_state, local_port_a_flag, verbose)) { (void) g_free_multipath(ml); return (err); } if (err = g_get_wwn(ml->dev_path, port_wwn, node_wwn, &al_pa, verbose)) { (void) g_free_multipath(ml); return (err); } (void) sprintf(l_disk_state->g_disk_state.port_a_wwn_s, "%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x", port_wwn[0], port_wwn[1], port_wwn[2], port_wwn[3], port_wwn[4], port_wwn[5], port_wwn[6], port_wwn[7]); l_disk_state->g_disk_state.port_a_valid++; path_a_found++; } if ((!local_port_a_flag) && (!path_b_found)) { G_DPRINTF(" l_get_disk_status: Path to Port B " "found: %s\n", ml->dev_path); if (err = l_get_disk_port_status(ml->dev_path, l_disk_state, local_port_a_flag, verbose)) { return (err); } if (err = g_get_wwn(ml->dev_path, port_wwn, node_wwn, &al_pa, verbose)) { (void) g_free_multipath(ml); return (err); } (void) sprintf(l_disk_state->g_disk_state.port_b_wwn_s, "%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x", port_wwn[0], port_wwn[1], port_wwn[2], port_wwn[3], port_wwn[4], port_wwn[5], port_wwn[6], port_wwn[7]); l_disk_state->g_disk_state.port_b_valid++; path_b_found++; } ml = ml->next; } return (0); } /* * Check for Persistent Reservations. */ int l_persistent_check(int fd, struct l_disk_state_struct *l_disk_state, int verbose) { int status; Read_keys read_key_buf; Read_reserv read_reserv_buf; (void) memset(&read_key_buf, 0, sizeof (struct read_keys_struct)); if ((status = g_scsi_persistent_reserve_in_cmd(fd, (uchar_t *)&read_key_buf, sizeof (struct read_keys_struct), ACTION_READ_KEYS))) { return (status); } /* This means persistent reservations are supported by the disk. */ l_disk_state->g_disk_state.persistent_reserv_flag = 1; if (read_key_buf.rk_length) { l_disk_state->g_disk_state.persistent_registered = 1; } (void) memset(&read_reserv_buf, 0, sizeof (struct read_reserv_struct)); if ((status = g_scsi_persistent_reserve_in_cmd(fd, (uchar_t *)&read_reserv_buf, sizeof (struct read_reserv_struct), ACTION_READ_RESERV))) { return (status); } if (read_reserv_buf.rr_length) { l_disk_state->g_disk_state.persistent_active = 1; } if (verbose) { (void) fprintf(stdout, MSGSTR(9048, " Checking for Persistent " "Reservations:")); if (l_disk_state->g_disk_state.persistent_reserv_flag) { if (l_disk_state->g_disk_state.persistent_active != NULL) { (void) fprintf(stdout, MSGSTR(39, "Active")); } else { (void) fprintf(stdout, MSGSTR(9049, "Registered")); } } else { (void) fprintf(stdout, MSGSTR(87, "Not being used")); } (void) fprintf(stdout, "\n"); } return (0); } /* * Gets the disk status and * updates the l_disk_state_struct structure. * Checks for open fail, Reservation Conflicts, * Not Ready and so on. * * RETURNS: * 0 O.K. * non-zero otherwise */ int l_get_disk_port_status(char *path, struct l_disk_state_struct *l_disk_state, int port_a_flag, int verbose) { int fd, status = 0, local_state = 0; Read_capacity_data capacity; /* local read capacity buffer */ struct vtoc vtoc; if ((path == NULL) || (l_disk_state == NULL)) { return (L_INVALID_PATH_FORMAT); } /* * Try to open drive. */ if ((fd = g_object_open(path, O_RDONLY)) == -1) { if ((fd = g_object_open(path, O_RDONLY | O_NDELAY)) == -1) { G_DPRINTF(" l_get_disk_port_status: Error " "opening drive %s\n", path); local_state = L_OPEN_FAIL; } else { /* See if drive ready */ if (status = g_scsi_tur(fd)) { if ((status & L_SCSI_ERROR) && ((status & ~L_SCSI_ERROR) == STATUS_CHECK)) { /* * TBD * This is where I should figure out * if the device is Not Ready or whatever. */ local_state = L_NOT_READY; } else if ((status & L_SCSI_ERROR) && ((status & ~L_SCSI_ERROR) == STATUS_RESERVATION_CONFLICT)) { /* mark reserved */ local_state = L_RESERVED; } else { local_state = L_SCSI_ERR; } /* * There may not be a label on the drive - check */ } else if (ioctl(fd, DKIOCGVTOC, &vtoc) == 0) { /* * Sanity-check the vtoc */ if (vtoc.v_sanity != VTOC_SANE || vtoc.v_sectorsz != DEV_BSIZE) { local_state = L_NO_LABEL; G_DPRINTF(" l_get_disk_port_status: " "Checking vtoc - No Label found.\n"); } } else if (errno != ENOTSUP) { I_DPRINTF("\t- DKIOCGVTOC ioctl failed: " " invalid geometry\n"); local_state = L_NO_LABEL; } } } /* * Need an extra check for tape devices * read capacity should not be run on tape devices. * It will always return Not Readable */ if (((local_state == 0) || (local_state == L_NO_LABEL)) && ! (strstr(path, SLSH_DRV_NAME_ST))) { if (status = g_scsi_read_capacity_cmd(fd, (uchar_t *)&capacity, sizeof (capacity))) { G_DPRINTF(" l_get_disk_port_status: " "Read Capacity failed.\n"); if (status & L_SCSI_ERROR) { if ((status & ~L_SCSI_ERROR) == STATUS_RESERVATION_CONFLICT) { /* mark reserved */ local_state |= L_RESERVED; } else /* mark bad */ local_state |= L_NOT_READABLE; } else { /* * TBD * Need a more complete state definition here. */ l_disk_state->g_disk_state.d_state_flags[port_a_flag] = L_SCSI_ERR; (void) close(fd); return (0); } } else { /* save capacity */ l_disk_state->g_disk_state.num_blocks = capacity.last_block_addr + 1; } } (void) close(fd); l_disk_state->g_disk_state.d_state_flags[port_a_flag] = local_state; G_DPRINTF(" l_get_disk_port_status: Individual Disk" " Status: 0x%x for" " port %s for path:" " %s\n", local_state, port_a_flag ? "A" : "B", path); return (0); } /* * Copy and format page 1 from big buffer to state structure. * * RETURNS: * 0 O.K. * non-zero otherwise */ static int copy_config_page(struct l_state_struct *l_state, uchar_t *from_ptr) { IB_page_config *encl_ptr; int size, i; encl_ptr = (struct ib_page_config *)(void *)from_ptr; /* Sanity check. */ if ((encl_ptr->enc_len > MAX_VEND_SPECIFIC_ENC) || (encl_ptr->enc_len == 0)) { return (L_REC_DIAG_PG1); } if ((encl_ptr->enc_num_elem > MAX_IB_ELEMENTS) || (encl_ptr->enc_num_elem == 0)) { return (L_REC_DIAG_PG1); } size = HEADER_LEN + 4 + HEADER_LEN + encl_ptr->enc_len; bcopy((void *)(from_ptr), (void *)&l_state->ib_tbl.config, (size_t)size); /* * Copy Type Descriptors seperately to get aligned. */ from_ptr += size; size = (sizeof (struct type_desc_hdr))*encl_ptr->enc_num_elem; bcopy((void *)(from_ptr), (void *)&l_state->ib_tbl.config.type_hdr, (size_t)size); /* * Copy Text Descriptors seperately to get aligned. * * Must use the text size from the Type Descriptors. */ from_ptr += size; for (i = 0; i < (int)l_state->ib_tbl.config.enc_num_elem; i++) { size = l_state->ib_tbl.config.type_hdr[i].text_len; bcopy((void *)(from_ptr), (void *)&l_state->ib_tbl.config.text[i], (size_t)size); from_ptr += size; } return (0); } /* * Copy page 7 (Element Descriptor page) to state structure. * Copy header then copy each element descriptor * seperately. * * RETURNS: * 0 O.K. * non-zero otherwise */ static void copy_page_7(struct l_state_struct *l_state, uchar_t *from_ptr) { uchar_t *my_from_ptr; int size, j, k, p7_index; size = HEADER_LEN + sizeof (l_state->ib_tbl.p7_s.gen_code); bcopy((void *)(from_ptr), (void *)&l_state->ib_tbl.p7_s, (size_t)size); my_from_ptr = from_ptr + size; if (getenv("_LUX_D_DEBUG") != NULL) { g_dump(" copy_page_7: Page 7 header: ", (uchar_t *)&l_state->ib_tbl.p7_s, size, HEX_ASCII); (void) fprintf(stdout, " copy_page_7: Elements being stored " "in state table\n" " "); } /* I am assuming page 1 has been read. */ for (j = 0, p7_index = 0; j < (int)l_state->ib_tbl.config.enc_num_elem; j++) { /* Copy global element */ size = HEADER_LEN + ((*(my_from_ptr + 2) << 8) | *(my_from_ptr + 3)); bcopy((void *)(my_from_ptr), (void *)&l_state->ib_tbl.p7_s.element_desc[p7_index++], (size_t)size); my_from_ptr += size; for (k = 0; k < (int)l_state->ib_tbl.config.type_hdr[j].num; k++) { /* Copy individual elements */ size = HEADER_LEN + ((*(my_from_ptr + 2) << 8) | *(my_from_ptr + 3)); bcopy((void *)(my_from_ptr), (void *)&l_state->ib_tbl.p7_s.element_desc[p7_index++], (size_t)size); my_from_ptr += size; D_DPRINTF("."); } } D_DPRINTF("\n"); } /* * Gets IB diagnostic pages on a given pathname from l_get_envsen(). * It also fills up the individual device element of l_state_struct using * diagnostics pages. * Gets IB diagnostic pages on a given pathname from l_get_envsen(). * It also fills up the individual device element of l_state_struct using * diagnostics pages. * * The path must be of the ses driver. * e.g. * /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/ses@WWN,0:0 * * * RETURNS: * 0 O.K. * non-zero otherwise */ int l_get_ib_status(char *path, struct l_state_struct *l_state, int verbose) { L_inquiry inq; uchar_t *ib_buf, *from_ptr; int num_pages, i, size, err; IB_page_2 *encl_ptr; int front_index, rear_index; int enc_type = 0; if ((path == NULL) || (l_state == NULL)) { return (L_INVALID_PATH_FORMAT); } /* * get big buffer */ if ((ib_buf = (uchar_t *)calloc(1, MAX_REC_DIAG_LENGTH)) == NULL) { return (L_MALLOC_FAILED); } /* * Get IB information * Even if there are 2 IB's in this box on this loop don't bother * talking to the other one as both IB's in a box * are supposed to report the same information. */ if (err = l_get_envsen(path, ib_buf, MAX_REC_DIAG_LENGTH, verbose)) { (void) g_destroy_data(ib_buf); return (err); } /* * Set up state structure */ bcopy((void *)ib_buf, (void *)&l_state->ib_tbl.p0, (size_t)sizeof (struct ib_page_0)); num_pages = l_state->ib_tbl.p0.page_len; from_ptr = ib_buf + HEADER_LEN + l_state->ib_tbl.p0.page_len; for (i = 1; i < num_pages; i++) { if (l_state->ib_tbl.p0.sup_page_codes[i] == L_PAGE_1) { if (err = copy_config_page(l_state, from_ptr)) { return (err); } } else if (l_state->ib_tbl.p0.sup_page_codes[i] == L_PAGE_2) { encl_ptr = (struct ib_page_2 *)(void *)from_ptr; size = HEADER_LEN + encl_ptr->page_len; bcopy((void *)(from_ptr), (void *)&l_state->ib_tbl.p2_s, (size_t)size); if (getenv("_LUX_D_DEBUG") != NULL) { g_dump(" l_get_ib_status: Page 2: ", (uchar_t *)&l_state->ib_tbl.p2_s, size, HEX_ONLY); } } else if (l_state->ib_tbl.p0.sup_page_codes[i] == L_PAGE_7) { (void) copy_page_7(l_state, from_ptr); } from_ptr += ((*(from_ptr + 2) << 8) | *(from_ptr + 3)); from_ptr += HEADER_LEN; } (void) g_destroy_data(ib_buf); G_DPRINTF(" l_get_ib_status: Read %d Receive Diagnostic pages " "from the IB.\n", num_pages); if (err = g_get_inquiry(path, &inq)) { return (err); } enc_type = l_get_enc_type(inq); /* * Get the total number of drives per box. * This assumes front & rear are the same. */ l_state->total_num_drv = 0; /* default to use as a flag */ for (i = 0; i < (int)l_state->ib_tbl.config.enc_num_elem; i++) { if (l_state->ib_tbl.config.type_hdr[i].type == ELM_TYP_DD) { if (l_state->total_num_drv) { if (l_state->total_num_drv != (l_state->ib_tbl.config.type_hdr[i].num * 2)) { return (L_INVALID_NUM_DISKS_ENCL); } } else { if (enc_type == DAK_ENC_TYPE) { l_state->total_num_drv = l_state->ib_tbl.config.type_hdr[i].num; } else { l_state->total_num_drv = l_state->ib_tbl.config.type_hdr[i].num * 2; } } } } /* * transfer the individual drive Device Element information * from IB state to drive state. */ if (err = l_get_disk_element_index(l_state, &front_index, &rear_index)) { return (err); } /* Skip global element */ front_index++; if (enc_type == DAK_ENC_TYPE) { rear_index += l_state->total_num_drv/2 + 1; } else { rear_index++; } for (i = 0; i < l_state->total_num_drv/2; i++) { bcopy((void *)&l_state->ib_tbl.p2_s.element[front_index + i], (void *)&l_state->drv_front[i].ib_status, (size_t)sizeof (struct device_element)); bcopy((void *)&l_state->ib_tbl.p2_s.element[rear_index + i], (void *)&l_state->drv_rear[i].ib_status, (size_t)sizeof (struct device_element)); } if (getenv("_LUX_G_DEBUG") != NULL) { g_dump(" l_get_ib_status: disk elements: ", (uchar_t *)&l_state->ib_tbl.p2_s.element[front_index], ((sizeof (struct device_element)) * (l_state->total_num_drv)), HEX_ONLY); } return (0); } /* * Given an IB path get the port, A or B. * * OUTPUT: * port_a: sets to 1 for port A * and 0 for port B. * RETURNS: * err: 0 O.k. * non-zero otherwise */ int l_get_port(char *ses_path, int *port_a, int verbose) { L_state *ib_state = NULL; Ctlr_elem_st ctlr; int i, err, elem_index = 0; if ((ses_path == NULL) || (port_a == NULL)) { return (L_NO_SES_PATH); } if ((ib_state = (L_state *)calloc(1, sizeof (L_state))) == NULL) { return (L_MALLOC_FAILED); } bzero(&ctlr, sizeof (ctlr)); if (err = l_get_ib_status(ses_path, ib_state, verbose)) { (void) l_free_lstate(&ib_state); return (err); } for (i = 0; i < (int)ib_state->ib_tbl.config.enc_num_elem; i++) { elem_index++; /* skip global */ if (ib_state->ib_tbl.config.type_hdr[i].type == ELM_TYP_IB) { bcopy((const void *) &ib_state->ib_tbl.p2_s.element[elem_index], (void *)&ctlr, sizeof (ctlr)); break; } elem_index += ib_state->ib_tbl.config.type_hdr[i].num; } *port_a = ctlr.report; G_DPRINTF(" l_get_port: Found ses is the %s card.\n", ctlr.report ? "A" : "B"); (void) l_free_lstate(&ib_state); return (0); } /* * This function expects a pointer to a device path ending in the form * .../ses@w<NODEWWN>,<something> or .../ssd@w<NODEWWN>,<something> * * No validity checking of the path is done by the function. * * It gets the wwn (node wwn) out of the passed string, searches the passed * map for a match, gets the corresponding phys addr (port id) for that entry * and stores in the pointer the caller has passed as an argument (pid) * * This function is to be called only for public/fabric topologies * * If this interface is going to get exported, one point to be * considered is if a call to g_get_path_type() has to be made. * * INPUT: * path - pointer to the enclosure/disk device path * map - pointer to the map * * OUTPUT: * pid - the physical address associated for the node WWN that was found * in the map * * RETURNS: * 0 - on success * non-zero - otherwise */ int l_get_pid_from_path(const char *path, const gfc_map_t *map, int *pid) { int i; unsigned long long ll_wwn; char *char_ptr, wwn_str[WWN_SIZE * 2 + 1]; char *byte_ptr, *temp_ptr; gfc_port_dev_info_t *dev_addr_ptr; mp_pathlist_t pathlist; char path0[MAXPATHLEN], pwwn0[WWN_S_LEN]; /* if mpxio device */ if (strstr(path, SCSI_VHCI) != NULL) { (void) strcpy(path0, path); if (g_get_pathlist(path0, &pathlist)) { return (L_INVALID_PATH); } else { (void) strncpy(pwwn0, pathlist.path_info[0]. path_addr, L_WWN_LENGTH); pwwn0[L_WWN_LENGTH] = '\0'; free(pathlist.path_info); char_ptr = pwwn0; } } else { /* First a quick check on the path */ if (((char_ptr = strrchr(path, '@')) == NULL) || (*++char_ptr != 'w')) { return (L_INVALID_PATH); } else { char_ptr++; } } if (strlen(char_ptr) < (WWN_SIZE * 2)) { return (L_INVALID_PATH); } (void) strncpy(wwn_str, char_ptr, WWN_SIZE * 2); wwn_str[WWN_SIZE * 2] = '\0'; errno = 0; /* For error checking */ ll_wwn = strtoull(wwn_str, &temp_ptr, L_WWN_LENGTH); if (errno || (temp_ptr != (wwn_str + (WWN_SIZE * 2)))) { return (L_INVALID_PATH); } byte_ptr = (char *)&ll_wwn; /* * Search for the ses's node wwn in map to get the area and * domain ids from the corresponding port id (phys address). */ for (dev_addr_ptr = map->dev_addr, i = 0; i < map->count; dev_addr_ptr++, i++) { if (bcmp((char *)dev_addr_ptr->gfc_port_dev. pub_port.dev_nwwn.raw_wwn, byte_ptr, WWN_SIZE) == 0) break; } if (i >= map->count) return (L_INVALID_PATH); *pid = dev_addr_ptr->gfc_port_dev.pub_port.dev_did.port_id; return (0); } /* * Finds the disk's node wwn string, and * port A and B's WWNs and their port status. * * INPUT: * path - pointer to a ses path * wwn_list - pointer to the wwn_list * * OUTPUT: * state - node_wwn and wwn of ports A & B of disk, etc are inited * - by l_get_disk_status() * found_flag - incremented after each examined element in the map * * RETURNS: * 0 O.K. * non-zero otherwise. */ static int l_get_node_status(char *path, struct l_disk_state_struct *state, int *found_flag, WWN_list *wwn_list, int verbose) { int j, select_id, err; int path_pid; char temp_path[MAXPATHLEN]; char sbuf[MAXPATHLEN], *char_ptr; gfc_map_mp_t *map_mp, *map_ptr; struct stat stat_buf; WWN_list *wwnlp; char wwnp[WWN_S_LEN]; /* * Get a new map. */ map_mp = NULL; if (err = get_mp_dev_map(path, &map_mp, verbose)) return (err); for (map_ptr = map_mp; map_ptr != NULL; map_ptr = map_ptr->map_next) { switch (map_ptr->map.hba_addr.port_topology) { case FC_TOP_PRIVATE_LOOP: for (j = 0; j < map_ptr->map.count; j++) { /* * Get a generic path to a device * * This assumes the path looks something like this * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0/... * ...ses@x,0:0 * then creates a path that looks like * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0/ssd@ */ (void) strcpy(temp_path, path); if ((char_ptr = strrchr(temp_path, '/')) == NULL) { free_mp_dev_map(&map_mp); return (L_INVALID_PATH); } *char_ptr = '\0'; /* Terminate sting */ (void) strcat(temp_path, SLSH_DRV_NAME_SSD); /* * Create complete path. * * Build entry ssd@xx,0:c,raw * where xx is the WWN. */ select_id = g_sf_alpa_to_switch[map_ptr->map. dev_addr[j].gfc_port_dev.priv_port.sf_al_pa]; G_DPRINTF(" l_get_node_status: Searching loop map " "to find disk: ID:0x%x" " AL_PA:0x%x\n", select_id, state->ib_status.sel_id); if (strstr(path, SCSI_VHCI) == NULL) { (void) sprintf(sbuf, "w%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x,0:c,raw", map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. sf_port_wwn[0], map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. sf_port_wwn[1], map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. sf_port_wwn[2], map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. sf_port_wwn[3], map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. sf_port_wwn[4], map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. sf_port_wwn[5], map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. sf_port_wwn[6], map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. sf_port_wwn[7]); (void) strcat(temp_path, sbuf); } /* * If we find a device on this loop in this box * update its status. */ if (state->ib_status.sel_id == select_id) { /* * Found a device on this loop in this box. * * Update state. */ (void) sprintf(state->g_disk_state.node_wwn_s, "%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x", map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. sf_node_wwn[0], map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. sf_node_wwn[1], map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. sf_node_wwn[2], map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. sf_node_wwn[3], map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. sf_node_wwn[4], map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. sf_node_wwn[5], map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. sf_node_wwn[6], map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. sf_node_wwn[7]); if (strstr(path, SCSI_VHCI) != NULL) { (void) g_ll_to_str(map_ptr->map.dev_addr[j].gfc_port_dev. priv_port.sf_node_wwn, wwnp); for (wwnlp = wwn_list; wwnlp != NULL; wwnlp = wwnlp->wwn_next) { if (strcmp(wwnlp->node_wwn_s, wwnp) == 0) { (void) strcpy(temp_path, wwnlp->physical_path); break; } } if (wwnlp == NULL) { (void) sprintf(sbuf, "g%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x:c,raw", map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. sf_node_wwn[0], map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. sf_node_wwn[1], map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. sf_node_wwn[2], map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. sf_node_wwn[3], map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. sf_node_wwn[4], map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. sf_node_wwn[5], map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. sf_node_wwn[6], map_ptr->map.dev_addr[j].gfc_port_dev.priv_port. sf_node_wwn[7]); (void) strcat(temp_path, sbuf); /* * check to make sure this is a valid path. * Paths may not always be created on the * host. So, we make a quick check. */ if (stat(temp_path, &stat_buf) == -1) { free_mp_dev_map(&map_mp); return (errno); } } } (void) strcpy(state->g_disk_state.physical_path, temp_path); /* Bad if WWN is all zeros. */ if (is_null_wwn(map_ptr->map.dev_addr[j]. gfc_port_dev.priv_port. sf_node_wwn)) { state->l_state_flag = L_INVALID_WWN; G_DPRINTF(" l_get_node_status: " "Disk state was " " Invalid WWN.\n"); (*found_flag)++; free_mp_dev_map(&map_mp); return (0); } /* get device status */ if (err = l_get_disk_status(temp_path, state, wwn_list, verbose)) { free_mp_dev_map(&map_mp); return (err); } /* * found device in map. Don't need to look * any further */ (*found_flag)++; free_mp_dev_map(&map_mp); return (0); } } /* for loop */ break; case FC_TOP_PUBLIC_LOOP: case FC_TOP_FABRIC: /* * Get a generic path to a device * This assumes the path looks something like this * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0/ses@wWWN,0:0 * then creates a path that looks like * /devices/sbus@1f,0/SUNW,socal@1,0/SUNW,sf@0,0/ssd@ */ (void) strcpy(temp_path, path); if ((char_ptr = strrchr(temp_path, '/')) == NULL) { free_mp_dev_map(&map_mp); return (L_INVALID_PATH); } *char_ptr = '\0'; /* Terminate sting */ if (err = l_get_pid_from_path(path, &map_ptr->map, &path_pid)) { free_mp_dev_map(&map_mp); return (err); } /* Now append the ssd string */ (void) strcat(temp_path, SLSH_DRV_NAME_SSD); /* * Create complete path. * * Build entry ssd@WWN,0:c,raw * * First, search the map for a device with the area code and * domain as in 'path_pid'. */ for (j = 0; j < map_ptr->map.count; j++) { if (map_ptr->map.dev_addr[j].gfc_port_dev.pub_port. dev_dtype != DTYPE_ESI) { select_id = g_sf_alpa_to_switch[map_ptr->map. dev_addr[j].gfc_port_dev.pub_port.dev_did. port_id & 0xFF]; if (((map_ptr->map.dev_addr[j].gfc_port_dev. pub_port.dev_did.port_id & AREA_DOMAIN_ID) == (path_pid & AREA_DOMAIN_ID)) && (state->ib_status.sel_id == select_id)) { /* * Found the device. Update state. */ if (strstr(temp_path, SCSI_VHCI) == NULL) { (void) sprintf(sbuf, "w%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x,0:c,raw", map_ptr->map.dev_addr[j].gfc_port_dev. pub_port.dev_pwwn.raw_wwn[0], map_ptr->map.dev_addr[j].gfc_port_dev. pub_port.dev_pwwn.raw_wwn[1], map_ptr->map.dev_addr[j].gfc_port_dev. pub_port.dev_pwwn.raw_wwn[2], map_ptr->map.dev_addr[j].gfc_port_dev. pub_port.dev_pwwn.raw_wwn[3], map_ptr->map.dev_addr[j].gfc_port_dev. pub_port.dev_pwwn.raw_wwn[4], map_ptr->map.dev_addr[j].gfc_port_dev. pub_port.dev_pwwn.raw_wwn[5], map_ptr->map.dev_addr[j].gfc_port_dev. pub_port.dev_pwwn.raw_wwn[6], map_ptr->map.dev_addr[j].gfc_port_dev. pub_port.dev_pwwn.raw_wwn[7]); (void) strcat(temp_path, sbuf); /* * Paths for fabric cases may not always * be created on the host. So, we make a * quick check. */ if (stat(temp_path, &stat_buf) == -1) { free_mp_dev_map(&map_mp); return (errno); } (void) sprintf(state-> g_disk_state.node_wwn_s, "%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x", map_ptr->map.dev_addr[j].gfc_port_dev. pub_port.dev_nwwn.raw_wwn[0], map_ptr->map.dev_addr[j].gfc_port_dev. pub_port.dev_nwwn.raw_wwn[1], map_ptr->map.dev_addr[j].gfc_port_dev. pub_port.dev_nwwn.raw_wwn[2], map_ptr->map.dev_addr[j].gfc_port_dev. pub_port.dev_nwwn.raw_wwn[3], map_ptr->map.dev_addr[j].gfc_port_dev. pub_port.dev_nwwn.raw_wwn[4], map_ptr->map.dev_addr[j].gfc_port_dev. pub_port.dev_nwwn.raw_wwn[5], map_ptr->map.dev_addr[j].gfc_port_dev. pub_port.dev_nwwn.raw_wwn[6], map_ptr->map.dev_addr[j].gfc_port_dev. pub_port.dev_nwwn.raw_wwn[7]); } else { (void) g_ll_to_str(map_ptr->map.dev_addr[j].gfc_port_dev. priv_port.sf_node_wwn, wwnp); for (wwnlp = wwn_list; wwnlp != NULL; wwnlp = wwnlp->wwn_next) { if (strcmp(wwnlp->node_wwn_s, wwnp) == 0) { (void) strcpy(temp_path, wwnlp->physical_path); break; } } if (wwnlp == NULL) { (void) sprintf(sbuf, "w%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x%1.2x,0:c,raw", map_ptr->map.dev_addr[j].gfc_port_dev.pub_port. dev_nwwn.raw_wwn[0], map_ptr->map.dev_addr[j].gfc_port_dev.pub_port. dev_nwwn.raw_wwn[1], map_ptr->map.dev_addr[j].gfc_port_dev.pub_port. dev_nwwn.raw_wwn[2], map_ptr->map.dev_addr[j].gfc_port_dev.pub_port. dev_nwwn.raw_wwn[3], map_ptr->map.dev_addr[j].gfc_port_dev.pub_port. dev_nwwn.raw_wwn[4], map_ptr->map.dev_addr[j].gfc_port_dev.pub_port. dev_nwwn.raw_wwn[5], map_ptr->map.dev_addr[j].gfc_port_dev.pub_port. dev_nwwn.raw_wwn[6], map_ptr->map.dev_addr[j].gfc_port_dev.pub_port. dev_nwwn.raw_wwn[7]); (void) strcat(temp_path, sbuf); } } (void) strcpy(state->g_disk_state.physical_path, temp_path); /* Bad if WWN is all zeros. */ if (is_null_wwn(map_ptr->map. dev_addr[j].gfc_port_dev. pub_port.dev_nwwn. raw_wwn)) { state->l_state_flag = L_INVALID_WWN; G_DPRINTF( " l_get_node_status: " "Disk state was " " Invalid WWN.\n"); (*found_flag)++; free_mp_dev_map(&map_mp); return (0); } /* get device status */ if (err = l_get_disk_status(temp_path, state, wwn_list, verbose)) { free_mp_dev_map(&map_mp); return (err); } (*found_flag)++; free_mp_dev_map(&map_mp); return (0); } /* if select_id match */ } /* if !DTYPE_ESI */ } /* for loop */ break; case FC_TOP_PT_PT: free_mp_dev_map(&map_mp); return (L_PT_PT_FC_TOP_NOT_SUPPORTED); default: free_mp_dev_map(&map_mp); return (L_UNEXPECTED_FC_TOPOLOGY); } /* End of switch on port_topology */ } free_mp_dev_map(&map_mp); return (0); } /* * Get the individual drives status for the device specified by the index. * device at the path where the path is of the IB and updates the * g_disk_state_struct structure. * * If the disk's port is bypassed, it gets the * drive status such as node WWN from the second port. * * RETURNS: * 0 O.K. * non-zero otherwise */ int l_get_individual_state(char *path, struct l_disk_state_struct *state, Ib_state *ib_state, int front_flag, struct box_list_struct *box_list, struct wwn_list_struct *wwn_list, int verbose) { int found_flag = 0, elem_index = 0; int port_a_flag, err, j; struct dlist *seslist = NULL; Bp_elem_st bpf, bpr; hrtime_t start_time, end_time; if ((path == NULL) || (state == NULL) || (ib_state == NULL) || (box_list == NULL)) { return (L_INVALID_PATH_FORMAT); } start_time = gethrtime(); if ((state->ib_status.code != S_NOT_INSTALLED) && (state->ib_status.code != S_NOT_AVAILABLE)) { /* * Disk could have been bypassed on this loop. * Check the port state before l_state_flag * is set to L_INVALID_MAP. */ for (j = 0; j < (int)ib_state->config.enc_num_elem; j++) { elem_index++; if (ib_state->config.type_hdr[j].type == ELM_TYP_BP) break; elem_index += ib_state->config.type_hdr[j].num; } /* * check if port A & B of backplane are bypassed. * If so, do not bother. */ if (front_flag) { bcopy((const void *) &(ib_state->p2_s.element[elem_index]), (void *)&bpf, sizeof (bpf)); if ((bpf.byp_a_enabled || bpf.en_bypass_a) && (bpf.byp_b_enabled || bpf.en_bypass_b)) return (0); } else { /* if disk is in rear slot */ bcopy((const void *) &(ib_state->p2_s.element[elem_index+1]), (void *)&bpr, sizeof (bpr)); if ((bpr.byp_b_enabled || bpr.en_bypass_b) && (bpr.byp_a_enabled || bpr.en_bypass_a)) return (0); } if ((err = l_get_node_status(path, state, &found_flag, wwn_list, verbose)) != 0) return (err); if (!found_flag) { if ((err = l_get_allses(path, box_list, &seslist, 0)) != 0) { return (err); } if (err = l_get_port(path, &port_a_flag, verbose)) goto done; if (port_a_flag) { if ((state->ib_status.bypass_a_en && !(state->ib_status.bypass_b_en)) || !(state->ib_status.bypass_b_en)) { while (seslist != NULL && !found_flag) { if (err = l_get_port( seslist->dev_path, &port_a_flag, verbose)) { goto done; } if ((strcmp(seslist->dev_path, path) != 0) && !port_a_flag) { *path = NULL; (void) strcpy(path, seslist->dev_path); if (err = l_get_node_status(path, state, &found_flag, wwn_list, verbose)) { goto done; } } seslist = seslist->next; } } } else { if ((state->ib_status.bypass_b_en && !(state->ib_status.bypass_a_en)) || !(state->ib_status.bypass_a_en)) { while (seslist != NULL && !found_flag) { if (err = l_get_port( seslist->dev_path, &port_a_flag, verbose)) { goto done; } if ((strcmp(seslist->dev_path, path) != 0) && port_a_flag) { *path = NULL; (void) strcpy(path, seslist->dev_path); if (err = l_get_node_status(path, state, &found_flag, wwn_list, verbose)) { goto done; } } seslist = seslist->next; } } } if (!found_flag) { state->l_state_flag = L_INVALID_MAP; G_DPRINTF(" l_get_individual_state: " "Disk state was " "Not in map.\n"); } else { G_DPRINTF(" l_get_individual_state: " "Disk was found in the map.\n"); } if (seslist != NULL) (void) g_free_multipath(seslist); } } else { G_DPRINTF(" l_get_individual_state: Disk state was %s.\n", (state->ib_status.code == S_NOT_INSTALLED) ? "Not Installed" : "Not Available"); } if (getenv("_LUX_T_DEBUG") != NULL) { end_time = gethrtime(); (void) fprintf(stdout, " l_get_individual_state:" "\tTime = %lld millisec\n", (end_time - start_time)/1000000); } return (0); done: (void) g_free_multipath(seslist); return (err); } /* * Get the global state of the photon. * * INPUT: * path and verbose flag * * "path" must be of the ses driver. * e.g. * /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/ses@WWN,0:0 * * OUTPUT: * The struct l_state (which was passed in) has the status info * * RETURNS: * 0 O.K. * non-zero otherwise */ int l_get_status(char *path, struct l_state_struct *l_state, int verbose) { int err = 0, i, count; L_inquiry inq; uchar_t node_wwn[WWN_SIZE], port_wwn[WWN_SIZE]; int al_pa, found_front, found_rear, front_flag, enc_type; char ses_path_front[MAXPATHLEN]; char ses_path_rear[MAXPATHLEN]; Box_list *b_list = NULL; Box_list *o_list = NULL; char node_wwn_s[(WWN_SIZE*2)+1]; uint_t select_id; hrtime_t start_time, end_time; WWN_list *wwn_list = NULL; if ((path == NULL) || (l_state == NULL)) { return (L_INVALID_PATH_FORMAT); } start_time = gethrtime(); G_DPRINTF(" l_get_status: Get Status for enclosure at: " " %s\n", path); /* initialization */ (void) memset(l_state, 0, sizeof (struct l_state_struct)); if (err = g_get_inquiry(path, &inq)) { return (err); } if ((strstr((char *)inq.inq_pid, ENCLOSURE_PROD_ID) == 0) && (!(strncmp((char *)inq.inq_vid, "SUN ", sizeof (inq.inq_vid)) && ((inq.inq_dtype & DTYPE_MASK) == DTYPE_ESI)))) { return (L_ENCL_INVALID_PATH); } (void) strncpy((char *)l_state->ib_tbl.enclosure_name, (char *)inq.inq_box_name, sizeof (inq.inq_box_name)); /* * Get all of the IB Receive Diagnostic pages. */ if (err = l_get_ib_status(path, l_state, verbose)) { return (err); } /* * Now get the individual devices information from * the device itself. * * May need to use multiple paths to get to the * front and rear drives in the box. * If the loop is split some drives may not even be available * from this host. * * The way this works is in the select ID the front disks * are accessed via the IB with the bit 4 = 0 * and the rear disks by the IB with bit 4 = 1. * * First get device map from fc nexus driver for this loop. */ /* * Get the boxes node WWN & al_pa for this path. */ if (err = g_get_wwn(path, port_wwn, node_wwn, &al_pa, verbose)) { return (err); } if (err = l_get_box_list(&o_list, verbose)) { (void) l_free_box_list(&o_list); return (err); /* Failure */ } found_front = found_rear = 0; for (i = 0; i < WWN_SIZE; i++) { (void) sprintf(&node_wwn_s[i << 1], "%02x", node_wwn[i]); } /* * The al_pa (or pa) can be 24 bits in size for fabric loops. * But we will take only the low order byte to get the select_id. * Private loops have al_pa which is only a byte in size. */ select_id = g_sf_alpa_to_switch[al_pa & 0xFF]; l_state->ib_tbl.box_id = (select_id & BOX_ID_MASK) >> 5; G_DPRINTF(" l_get_status: Using this select_id 0x%x " "and node WWN %s\n", select_id, node_wwn_s); if (strstr(path, SCSI_VHCI) != NULL) { /* there is no way to obtain all the al_pa with */ /* current implementation. assume both front */ /* and rear. need changes later on. */ found_rear = 1; found_front = 1; (void) strcpy(ses_path_rear, path); (void) strcpy(ses_path_front, path); } else { if (select_id & ALT_BOX_ID) { found_rear = 1; (void) strcpy(ses_path_rear, path); b_list = o_list; while (b_list) { if (strcmp(b_list->b_node_wwn_s, node_wwn_s) == 0) { if (err = g_get_wwn(b_list->b_physical_path, port_wwn, node_wwn, &al_pa, verbose)) { (void) l_free_box_list(&o_list); return (err); } /* Take the low order byte of al_pa */ select_id = g_sf_alpa_to_switch[al_pa & 0xFF]; if (!(select_id & ALT_BOX_ID)) { (void) strcpy(ses_path_front, b_list->b_physical_path); found_front = 1; break; } } b_list = b_list->box_next; } } else { (void) strcpy(ses_path_front, path); found_front = 1; b_list = o_list; while (b_list) { if (strcmp(b_list->b_node_wwn_s, node_wwn_s) == 0) { if (err = g_get_wwn(b_list->b_physical_path, port_wwn, node_wwn, &al_pa, verbose)) { (void) l_free_box_list(&o_list); return (err); } select_id = g_sf_alpa_to_switch[al_pa & 0xFF]; if (select_id & ALT_BOX_ID) { (void) strcpy(ses_path_rear, b_list->b_physical_path); found_rear = 1; break; } } b_list = b_list->box_next; } } } if (getenv("_LUX_G_DEBUG") != NULL) { if (!found_front) { (void) printf("l_get_status: Loop to front disks not found.\n"); } if (!found_rear) { (void) printf("l_get_status: Loop to rear disks not found.\n"); } } /* * Get path to all the FC disk and tape devices. * * I get this now and pass down for performance * reasons. * If for some reason the list can become invalid, * i.e. device being offlined, then the list * must be re-gotten. */ if (err = g_get_wwn_list(&wwn_list, verbose)) { return (err); /* Failure */ } enc_type = l_get_enc_type(inq); if (found_front) { front_flag = 1; for (i = 0, count = 0; i < l_state->total_num_drv/2; count++, i++) { if (enc_type == DAK_ENC_TYPE) { G_DPRINTF(" l_get_status: Getting individual" " State for disk in slot %d\n", count); } else { G_DPRINTF(" l_get_status: Getting individual" " State for front disk in slot %d\n", i); } if (err = l_get_individual_state(ses_path_front, (struct l_disk_state_struct *)&l_state->drv_front[i], &l_state->ib_tbl, front_flag, o_list, wwn_list, verbose)) { (void) l_free_box_list(&o_list); (void) g_free_wwn_list(&wwn_list); return (err); } } } else { /* Set to loop not accessable. */ for (i = 0; i < l_state->total_num_drv/2; i++) { l_state->drv_front[i].l_state_flag = L_NO_LOOP; } } /* * For Daktari's, disk 0-5 information are located in the * l_state->drv_front array * For Daktari's, disk 6-11 information are located in the * l_state->drv_rear array * * For this reason, on daktari's, I ignore the found_front and * found_rear flags and check both the drv_front and drv_rear */ if (enc_type == DAK_ENC_TYPE && found_front) { front_flag = 1; for (i = 0; i < l_state->total_num_drv/2; i++, count++) { G_DPRINTF(" l_get_status: Getting individual" " State for disk in slot %d\n", count); if (err = l_get_individual_state(ses_path_front, (struct l_disk_state_struct *)&l_state->drv_rear[i], &l_state->ib_tbl, front_flag, o_list, wwn_list, verbose)) { (void) l_free_box_list(&o_list); (void) g_free_wwn_list(&wwn_list); return (err); } } } else if (enc_type != DAK_ENC_TYPE && found_rear) { for (i = 0; i < l_state->total_num_drv/2; i++, count++) { G_DPRINTF(" l_get_status: Getting individual" " State for rear disk in slot %d\n", i); if (err = l_get_individual_state(ses_path_rear, (struct l_disk_state_struct *)&l_state->drv_rear[i], &l_state->ib_tbl, front_flag, o_list, wwn_list, verbose)) { (void) l_free_box_list(&o_list); (void) g_free_wwn_list(&wwn_list); return (err); } } } else if (enc_type != DAK_ENC_TYPE) { /* Set to loop not accessable. */ for (i = 0; i < l_state->total_num_drv/2; i++) { l_state->drv_rear[i].l_state_flag = L_NO_LOOP; } } (void) l_free_box_list(&o_list); (void) g_free_wwn_list(&wwn_list); if (getenv("_LUX_T_DEBUG") != NULL) { end_time = gethrtime(); (void) fprintf(stdout, " l_get_status: " "Time = %lld millisec\n", (end_time - start_time)/1000000); } return (0); } /* * Check the SENA file for validity: * - verify the size is that of 3 proms worth of text. * - verify PROM_MAGIC. * - verify (and print) the date. * - verify the checksum. * - verify the WWN == 0. * Since this requires reading the entire file, do it now and pass a pointer * to the allocated buffer back to the calling routine (which is responsible * for freeing it). If the buffer is not allocated it will be NULL. * * RETURNS: * 0 O.K. * non-zero otherwise */ static int check_file(int fd, int verbose, uchar_t **buf_ptr, int dl_info_offset) { struct exec the_exec; int temp, i, j, *p, size, *start; uchar_t *buf; char *date_str; struct dl_info *dl_info; *buf_ptr = NULL; /* read exec header */ if (lseek(fd, 0, SEEK_SET) == -1) return (errno); if ((temp = read(fd, (char *)&the_exec, sizeof (the_exec))) == -1) { return (L_DWNLD_READ_HEADER_FAIL); } if (temp != sizeof (the_exec)) { return (L_DWNLD_READ_INCORRECT_BYTES); } if (the_exec.a_text != PROMSIZE) { return (L_DWNLD_INVALID_TEXT_SIZE); } if (!(buf = (uchar_t *)g_zalloc(PROMSIZE))) return (L_MALLOC_FAILED); if ((temp = read(fd, buf, PROMSIZE)) == -1) { return (L_DWNLD_READ_ERROR); } if (temp != PROMSIZE) { return (L_DWNLD_READ_INCORRECT_BYTES); } /* check the IB firmware MAGIC */ dl_info = (struct dl_info *)(unsigned long)(buf + dl_info_offset); if (dl_info->magic != PROM_MAGIC) { return (L_DWNLD_BAD_FRMWARE); } /* * Get the date */ date_str = ctime(&dl_info->datecode); if (verbose) { (void) fprintf(stdout, MSGSTR(9050, " IB Prom Date: %s"), date_str); } /* * verify checksum */ if (dl_info_offset == FPM_DL_INFO) { start = (int *)(long)(buf + FPM_OFFSET); size = FPM_SZ; } else { start = (int *)(long)buf; size = TEXT_SZ + IDATA_SZ; } for (j = 0, p = start, i = 0; i < (size/ 4); i++, j ^= *p++); if (j != 0) { return (L_DWNLD_CHKSUM_FAILED); } /* file verified */ *buf_ptr = buf; return (0); } /* * Check the DPM file for validity: * * RETURNS: * 0 O.K. * non-zero otherwise */ #define dakstring "64616B74617269" #define dakoffs "BFC00000" static int check_dpm_file(int fd) { struct s3hdr { char rtype[2]; char rlen[2]; char data[255]; } theRec; int nread; int reclen; if (fd < 0) { return (L_DWNLD_READ_ERROR); } lseek(fd, 0, SEEK_SET); /* First record */ memset((void*)&theRec, 0, sizeof (struct s3hdr)); nread = read(fd, (void *)&theRec, 4); if (nread != 4) { /* error reading first record/length */ return (L_DWNLD_READ_ERROR); } if (strncmp((char *)&theRec.rtype[0], "S0", 2) != 0) { /* error in first record type */ return (L_DWNLD_READ_HEADER_FAIL); } reclen = strtol(&theRec.rlen[0], (char **)NULL, 16); if (reclen == 0) { /* error in length == 0 */ return (L_DWNLD_READ_HEADER_FAIL); } nread = read(fd, (void *)&theRec.data[0], ((reclen*2) +1)); if (nread != ((reclen*2) +1)) { /* error in trying to read data */ return (L_DWNLD_READ_HEADER_FAIL); } if (strncmp(&theRec.data[4], dakstring, 14) != 0) { /* error in compiled file name */ return (L_DWNLD_READ_HEADER_FAIL); } /* Second record */ memset((void*)&theRec, 0, sizeof (struct s3hdr)); nread = read(fd, (void *)&theRec, 4); if (nread != 4) { /* error reading second record/length */ return (L_DWNLD_READ_ERROR); } if (strncmp((char *)&theRec.rtype[0], "S3", 2) != 0) { /* error in second record type */ return (L_DWNLD_READ_HEADER_FAIL); } reclen = strtol(&theRec.rlen[0], (char **)NULL, 16); if (reclen == 0) { /* error in length == 0 */ return (L_DWNLD_READ_HEADER_FAIL); } nread = read(fd, (void *)&theRec.data[0], ((reclen*2) +1)); if (nread != ((reclen*2) +1)) { /* error in trying to read data */ return (L_DWNLD_READ_HEADER_FAIL); } if (strncmp(&theRec.data[0], dakoffs, 8) != 0) { /* error in SSC100 offset pointer */ return (L_DWNLD_READ_HEADER_FAIL); } lseek(fd, 0, SEEK_SET); return (0); } int l_check_file(char *file, int verbose) { int file_fd; int err; uchar_t *buf; if ((file_fd = g_object_open(file, O_RDONLY)) == -1) { return (L_OPEN_PATH_FAIL); } err = check_file(file_fd, verbose, &buf, FW_DL_INFO); if (buf) (void) g_destroy_data((char *)buf); return (err); } /* * Write buffer command set up to download * firmware to the Photon IB. * * RETURNS: * status */ static int ib_download_code_cmd(int fd, int promid, int off, uchar_t *buf_ptr, int buf_len, int sp) { int status, sz; while (buf_len) { sz = MIN(256, buf_len); buf_len -= sz; status = g_scsi_writebuffer_cmd(fd, off, buf_ptr, sz, (sp) ? 3 : 2, promid); if (status) return (status); buf_ptr += sz; off += sz; } return (status); } /* * * Downloads the code to the DAKTARI/DPM with the hdr set correctly * * * Inputs: * fd - int for the file descriptor * buf_ptr - uchar_t pointer to the firmware itself * buf_len - int for the length of the data * * Returns: * status: 0 indicates success, != 0 failure, returned from writebuffer * */ static int dak_download_code_cmd(int fd, uchar_t *buf_ptr, int buf_len) { int status = 0; int sz = 0; int offs = 0; while (buf_len > 0) { sz = MIN(256, buf_len); buf_len -= sz; status = g_scsi_writebuffer_cmd(fd, offs, buf_ptr, sz, 0x07, 0); if (status != 0) { return (status); } buf_ptr += sz; offs += sz; } return (status); } /* * Downloads the new prom image to IB. * * INPUTS: * path - physical path of Photon SES card * file - input file for new code (may be NULL) * ps - whether the "save" bit should be set * verbose - to be verbose or not * * RETURNS: * 0 O.K. * non-zero otherwise */ int l_download(char *path_phys, char *file, int ps, int verbose) { int file_fd, controller_fd; int err, status; uchar_t *buf_ptr; char printbuf[MAXPATHLEN]; int retry; char file_path[MAXPATHLEN]; struct stat statbuf; int enc_type; L_inquiry inq; if (path_phys == NULL) { return (L_INVALID_PATH_FORMAT); } if (!file) { (void) strcpy(file_path, IBFIRMWARE_FILE); } else { (void) strncpy(file_path, file, sizeof (file_path)); } if (verbose) (void) fprintf(stdout, "%s\n", MSGSTR(9051, " Opening the IB for I/O.")); if ((controller_fd = g_object_open(path_phys, O_NDELAY | O_RDWR)) == -1) return (L_OPEN_PATH_FAIL); (void) sprintf(printbuf, MSGSTR(9052, " Doing download to:" "\n\t%s.\n From file: %s."), path_phys, file_path); if (verbose) (void) fprintf(stdout, "%s\n", printbuf); P_DPRINTF(" Doing download to:" "\n\t%s\n From file: %s\n", path_phys, file_path); if ((file_fd = g_object_open(file_path, O_NDELAY | O_RDONLY)) == -1) { /* * Return a different error code here to differentiate between * this failure in g_object_open() and the one above. */ return (L_INVALID_PATH); } if (g_scsi_inquiry_cmd(controller_fd, (uchar_t *)&inq, sizeof (inq))) { return (L_SCSI_ERROR); } enc_type = l_get_enc_type(inq); switch (enc_type) { case DAK_ENC_TYPE: /* * We don't have a default daktari file location, so * the user must specify the firmware file on the command line */ if (!file) { return (L_REQUIRE_FILE); } /* Validate the file */ if ((err = check_dpm_file(file_fd))) { return (err); } /* Now go ahead and load up the data */ if (fstat(file_fd, &statbuf) == -1) { err = errno; (void) fprintf(stdout, "%s %s\n", MSGSTR(9101, " Stat'ing the F/W file:"), strerror(err)); return (L_OPEN_PATH_FAIL); } buf_ptr = (uchar_t *)g_zalloc(statbuf.st_size); if (buf_ptr == NULL) { err = errno; (void) fprintf(stdout, "%s %s\n", MSGSTR(9102, " Cannot alloc mem to read F/W file:"), strerror(err)); return (L_MALLOC_FAILED); } if (read(file_fd, buf_ptr, statbuf.st_size) == -1) { err = errno; (void) fprintf(stdout, "%s %s\n", MSGSTR(9103, " Reading F/W file:"), strerror(err)); g_destroy_data((char *)buf_ptr); return (L_DWNLD_READ_ERROR); } break; default: if (err = check_file(file_fd, verbose, &buf_ptr, FW_DL_INFO)) { if (buf_ptr) { (void) g_destroy_data((char *)buf_ptr); return (err); } } break; } if (verbose) { (void) fprintf(stdout, " "); (void) fprintf(stdout, MSGSTR(127, "Checkfile O.K.")); (void) fprintf(stdout, "\n"); } P_DPRINTF(" Checkfile OK.\n"); (void) close(file_fd); if (verbose) { (void) fprintf(stdout, MSGSTR(9053, " Verifying the IB is available.\n")); } retry = DOWNLOAD_RETRIES; while (retry) { if ((status = g_scsi_tur(controller_fd)) == 0) { break; } else { if ((retry % 30) == 0) { ER_DPRINTF(" Waiting for the IB to be" " available.\n"); } (void) sleep(1); } } if (!retry) { if (buf_ptr) (void) g_destroy_data((char *)buf_ptr); (void) close(controller_fd); return (status); } if (verbose) (void) fprintf(stdout, "%s\n", MSGSTR(9054, " Writing new text image to IB.")); P_DPRINTF(" Writing new image to IB\n"); switch (enc_type) { case DAK_ENC_TYPE: status = dak_download_code_cmd(controller_fd, buf_ptr, statbuf.st_size); if (status != 0) { if (buf_ptr != NULL) { g_destroy_data((char *)buf_ptr); } (void) close(controller_fd); return (status); } break; default: status = ib_download_code_cmd(controller_fd, IBEEPROM, TEXT_OFFSET, (uchar_t *)(buf_ptr + TEXT_OFFSET), TEXT_SZ, ps); if (status) { (void) close(controller_fd); (void) g_destroy_data((char *)buf_ptr); return (status); } if (verbose) { (void) fprintf(stdout, "%s\n", MSGSTR(9055, " Writing new data image to IB.")); } status = ib_download_code_cmd(controller_fd, IBEEPROM, IDATA_OFFSET, (uchar_t *)(buf_ptr + IDATA_OFFSET), IDATA_SZ, ps); if (status) { (void) close(controller_fd); (void) g_destroy_data((char *)buf_ptr); return (status); } break; } if (verbose) { (void) fprintf(stdout, MSGSTR(9056, " Re-verifying the IB is available.\n")); } retry = DOWNLOAD_RETRIES; while (retry) { if ((status = g_scsi_tur(controller_fd)) == 0) { break; } else { if ((retry % 30) == 0) { ER_DPRINTF(" Waiting for the IB to be" " available.\n"); } (void) sleep(1); } retry--; } if (!retry) { (void) close(controller_fd); (void) g_destroy_data((char *)buf_ptr); return (L_DWNLD_TIMED_OUT); } switch (enc_type) { case DAK_ENC_TYPE: break; default: if (verbose) { (void) fprintf(stdout, "%s\n", MSGSTR(9057, " Writing new image to FPM.")); } status = ib_download_code_cmd(controller_fd, MBEEPROM, FPM_OFFSET, (uchar_t *)(buf_ptr + FPM_OFFSET), FPM_SZ, ps); break; } if ((!status) && ps) { /* * Reset the IB */ status = g_scsi_reset(controller_fd); } (void) close(controller_fd); return (status); } /* * Set the World Wide Name * in page 4 of the Send Diagnostic command. * * Is it allowed to change the wwn ??? * The path must point to an IB. * */ int l_set_wwn(char *path_phys, char *wwn) { Page4_name page4; L_inquiry inq; int fd, status; char wwnp[WWN_SIZE]; (void) memset(&inq, 0, sizeof (inq)); (void) memset(&page4, 0, sizeof (page4)); if ((fd = g_object_open(path_phys, O_NDELAY | O_RDONLY)) == -1) { return (L_OPEN_PATH_FAIL); } /* Verify it is a Photon */ if (status = g_scsi_inquiry_cmd(fd, (uchar_t *)&inq, sizeof (struct l_inquiry_struct))) { (void) close(fd); return (status); } if ((strstr((char *)inq.inq_pid, ENCLOSURE_PROD_ID) == 0) && (!(strncmp((char *)inq.inq_vid, "SUN ", sizeof (inq.inq_vid)) && ((inq.inq_dtype & DTYPE_MASK) == DTYPE_ESI)))) { (void) close(fd); return (L_ENCL_INVALID_PATH); } page4.page_code = L_PAGE_4; page4.page_len = (ushort_t)((sizeof (struct page4_name) - 4)); page4.string_code = L_WWN; page4.enable = 1; if (g_string_to_wwn((uchar_t *)wwn, (uchar_t *)&page4.name)) { close(fd); return (EINVAL); } bcopy((void *)wwnp, (void *)page4.name, (size_t)WWN_SIZE); if (status = g_scsi_send_diag_cmd(fd, (uchar_t *)&page4, sizeof (page4))) { (void) close(fd); return (status); } /* * Check the wwn really changed. */ bzero((char *)page4.name, 32); if (status = g_scsi_rec_diag_cmd(fd, (uchar_t *)&page4, sizeof (page4), L_PAGE_4)) { (void) close(fd); return (status); } if (bcmp((char *)page4.name, wwnp, WWN_SIZE)) { (void) close(fd); return (L_WARNING); } (void) close(fd); return (0); } /* * Use a physical path to a disk in a Photon box * as the base to genererate a path to a SES * card in this box. * * path_phys: Physical path to a Photon disk. * ses_path: This must be a pointer to an already allocated path string. * * RETURNS: * 0 O.K. * non-zero otherwise */ int l_get_ses_path(char *path_phys, char *ses_path, gfc_map_t *map, int verbose) { char *char_ptr, id_buf[MAXPATHLEN], wwn[20]; uchar_t t_wwn[20], *ses_wwn, *ses_wwn1, *ses_nwwn; int j, al_pa, al_pa1, box_id, fd, disk_flag = 0; int err, found = 0; gfc_port_dev_info_t *dev_addr_ptr; if ((path_phys == NULL) || (ses_path == NULL) || (map == NULL)) { return (L_NO_SES_PATH); } (void) strcpy(ses_path, path_phys); if ((char_ptr = strrchr(ses_path, '/')) == NULL) { return (L_INVLD_PATH_NO_SLASH_FND); } disk_flag++; *char_ptr = '\0'; /* Terminate sting */ (void) strcat(ses_path, SLSH_SES_NAME); /* * Figure out and create the boxes pathname. * * NOTE: This uses the fact that the disks's * AL_PA and the boxes AL_PA must match * the assigned hard address in the current * implementations. This may not be true in the * future. */ if ((char_ptr = strrchr(path_phys, '@')) == NULL) { return (L_INVLD_PATH_NO_ATSIGN_FND); } char_ptr++; /* point to the loop identifier */ if ((err = g_get_wwn(path_phys, t_wwn, t_wwn, &al_pa, verbose)) != 0) { return (err); } box_id = g_sf_alpa_to_switch[al_pa & 0xFF] & BOX_ID_MASK; switch (map->hba_addr.port_topology) { case FC_TOP_PRIVATE_LOOP: for (j = 0, dev_addr_ptr = map->dev_addr; j < map->count; j++, dev_addr_ptr++) { if (dev_addr_ptr->gfc_port_dev.priv_port. sf_inq_dtype == DTYPE_ESI) { al_pa1 = dev_addr_ptr->gfc_port_dev. priv_port.sf_al_pa; if (box_id == (g_sf_alpa_to_switch[al_pa1] & BOX_ID_MASK)) { if (!found) { ses_wwn = dev_addr_ptr-> gfc_port_dev.priv_port.sf_port_wwn; ses_nwwn = dev_addr_ptr-> gfc_port_dev.priv_port.sf_node_wwn; if (getenv("_LUX_P_DEBUG")) { (void) g_ll_to_str(ses_wwn, (char *)t_wwn); (void) printf( " l_get_ses_path: " "Found ses wwn = %s " "al_pa 0x%x\n", t_wwn, al_pa1); } } else { ses_wwn1 = dev_addr_ptr-> gfc_port_dev.priv_port.sf_port_wwn; if (getenv("_LUX_P_DEBUG")) { (void) g_ll_to_str(ses_wwn1, (char *)t_wwn); (void) printf( " l_get_ses_path: " "Found second ses " "wwn = %s " "al_pa 0x%x\n", t_wwn, al_pa1); } } found++; } } } break; case FC_TOP_FABRIC: case FC_TOP_PUBLIC_LOOP: for (j = 0, dev_addr_ptr = map->dev_addr; j < map->count; j++, dev_addr_ptr++) { if (dev_addr_ptr->gfc_port_dev.pub_port.dev_dtype == DTYPE_ESI) { /* * We found an enclosure, lets match the * area and domain codes for this enclosure with * that of the ses path since there may be * multiple enclosures with same box id on a * fabric */ al_pa1 = dev_addr_ptr->gfc_port_dev. pub_port.dev_did.port_id; if ((al_pa & AREA_DOMAIN_ID) == (al_pa1 & AREA_DOMAIN_ID)) { /* * The area and domain matched. Now, we * match the box id of the disk with * this enclosure */ if (box_id == (g_sf_alpa_to_switch[al_pa1 & 0xFF] & BOX_ID_MASK)) { if (!found) { ses_wwn = dev_addr_ptr-> gfc_port_dev.pub_port. dev_pwwn.raw_wwn; ses_nwwn = dev_addr_ptr-> gfc_port_dev.pub_port. dev_nwwn.raw_wwn; if (getenv("_LUX_P_DEBUG")) { (void) g_ll_to_str(ses_wwn, (char *)t_wwn); (void) printf( " l_get_ses_path: " "Found ses wwn = %s " "al_pa 0x%x\n", t_wwn, al_pa1); } } else { ses_wwn1 = dev_addr_ptr-> gfc_port_dev.pub_port. dev_pwwn.raw_wwn; if (getenv("_LUX_P_DEBUG")) { (void) g_ll_to_str(ses_wwn1, (char *)t_wwn); (void) printf( " l_get_ses_path: " "Found second ses " "wwn = %s " "al_pa 0x%x\n", t_wwn, al_pa1); } } found++; } } } } break; case FC_TOP_PT_PT: return (L_PT_PT_FC_TOP_NOT_SUPPORTED); default: return (L_UNEXPECTED_FC_TOPOLOGY); } /* End of switch on port_topology */ if (!found) { return (L_NO_SES_PATH); } if (strstr(path_phys, SCSI_VHCI) != NULL) { (void) g_ll_to_str(ses_nwwn, wwn); (void) sprintf(id_buf, "g%s:0", wwn); } else { (void) g_ll_to_str(ses_wwn, wwn); (void) sprintf(id_buf, "w%s,0:0", wwn); } (void) strcat(ses_path, id_buf); if (verbose) { (void) fprintf(stdout, MSGSTR(9058, " Creating enclosure path:\n %s\n"), ses_path); } /* * see if these paths exist. */ if ((fd = g_object_open(ses_path, O_NDELAY | O_RDONLY)) == -1) { if (strstr(path_phys, SCSI_VHCI) != NULL) { return (L_INVALID_PATH); } char_ptr = strrchr(ses_path, '/'); *char_ptr = '\0'; (void) strcat(ses_path, SLSH_SES_NAME); if (found > 1) { (void) g_ll_to_str(ses_wwn1, wwn); P_DPRINTF(" l_get_ses_path: " "Using second path, ses wwn1 = %s\n", wwn); (void) sprintf(id_buf, "w%s,0:0", wwn); strcat(ses_path, id_buf); return (0); } else { return (L_NO_SES_PATH); } } close(fd); return (0); } /* * Get a valid location, front/rear & slot. * * path_struct->p_physical_path must be of a disk. * * OUTPUT: path_struct->slot_valid * path_struct->slot * path_struct->f_flag * * RETURN: * 0 O.K. * non-zero otherwise */ int l_get_slot(struct path_struct *path_struct, L_state *l_state, int verbose) { int err, al_pa, slot, found = 0; uchar_t node_wwn[WWN_SIZE], port_wwn[WWN_SIZE]; uint_t select_id; if ((path_struct == NULL) || (l_state == NULL)) { return (L_INVALID_PATH_FORMAT); } /* Double check to see if we need to calculate. */ if (path_struct->slot_valid) return (0); /* Programming error if this occures */ assert(path_struct->ib_path_flag == 0); if (strstr(path_struct->p_physical_path, "ssd") == NULL) { return (L_INVLD_PHYS_PATH_TO_DISK); } if (err = g_get_wwn(path_struct->p_physical_path, port_wwn, node_wwn, &al_pa, verbose)) { return (err); } /* * Find the slot by searching for the matching hard address. * Take only the low order byte ignoring area and domain code in * fabric devices' 24 bit al_pa */ select_id = g_sf_alpa_to_switch[al_pa & 0xFF]; P_DPRINTF(" l_get_slot: Searching Receive Diagnostic page 2, " "to find the slot number with this ID:0x%x\n", select_id); for (slot = 0; slot < l_state->total_num_drv/2; slot++) { if (l_state->drv_front[slot].ib_status.sel_id == select_id) { path_struct->f_flag = 1; found = 1; break; } else if (l_state->drv_rear[slot].ib_status.sel_id == select_id) { path_struct->f_flag = 0; found = 1; break; } } if (!found) { return (L_INVALID_SLOT); /* Failure */ } if ((strncmp((char *)l_state->ib_tbl.config.prod_id, DAK_OFF_NAME, strlen(DAK_OFF_NAME)) == 0) || (strncmp((char *)l_state->ib_tbl.config.prod_id, DAK_PROD_STR, strlen(DAK_OFF_NAME)) == 0)) { P_DPRINTF(" l_get_slot: Found slot %d.\n", path_struct->f_flag ? slot : slot + (MAX_DRIVES_DAK/2)); } else { P_DPRINTF(" l_get_slot: Found slot %d %s.\n", slot, path_struct->f_flag ? "Front" : "Rear"); } path_struct->slot = slot; path_struct->slot_valid = 1; return (0); } void l_element_msg_string(uchar_t code, char *es) { if (code == S_OK) { (void) sprintf(es, MSGSTR(29, "O.K.")); } else if (code == S_NOT_AVAILABLE) { (void) sprintf(es, MSGSTR(34, "Disabled")); } else if (code == S_NOT_INSTALLED) { (void) sprintf(es, MSGSTR(30, "Not Installed")); } else if (code == S_NONCRITICAL) { (void) sprintf(es, MSGSTR(9059, "Noncritical failure")); } else if (code == S_CRITICAL) { (void) sprintf(es, MSGSTR(122, "Critical failure")); } else { (void) sprintf(es, MSGSTR(4, "Unknown status")); } } /* * Get all ses paths paths to a given box. * The arg should be the physical path to one of the box's IB. * NOTE: The caller must free the allocated lists. * * OUTPUT: * a pointer to a list of ses paths if found * NULL on error. * * RETURNS: * 0 if O.K. * non-zero otherwise */ int l_get_allses(char *path, struct box_list_struct *box_list, struct dlist **ses_list, int verbose) { struct box_list_struct *box_list_ptr; char node_wwn_s[WWN_S_LEN]; struct dlist *dlt, *dl; if ((path == NULL) || (box_list == NULL) || (ses_list == NULL)) { return (L_INVALID_PATH_FORMAT); } /* Initialize lists/arrays */ *ses_list = dlt = dl = (struct dlist *)NULL; node_wwn_s[0] = '\0'; H_DPRINTF(" l_get_allses: Looking for all ses paths for" " box at path: %s\n", path); for (box_list_ptr = box_list; box_list_ptr != NULL; box_list_ptr = box_list_ptr->box_next) { H_DPRINTF(" l_get_allses: physical_path= %s\n", box_list_ptr->b_physical_path); if (strcmp(path, box_list_ptr->b_physical_path) == 0) { (void) strcpy(node_wwn_s, box_list_ptr->b_node_wwn_s); break; } } if (node_wwn_s[0] == '\0') { H_DPRINTF("node_wwn_s is NULL!\n"); return (L_NO_NODE_WWN_IN_BOXLIST); } H_DPRINTF(" l_get_allses: node_wwn=%s\n", node_wwn_s); for (box_list_ptr = box_list; box_list_ptr != NULL; box_list_ptr = box_list_ptr->box_next) { if (strcmp(node_wwn_s, box_list_ptr->b_node_wwn_s) == 0) { if ((dl = (struct dlist *) g_zalloc(sizeof (struct dlist))) == NULL) { while (*ses_list != NULL) { dl = dlt->next; (void) g_destroy_data(dlt); dlt = dl; } return (L_MALLOC_FAILED); } H_DPRINTF(" l_get_allses: Found ses=%s\n", box_list_ptr->b_physical_path); dl->dev_path = strdup(box_list_ptr->b_physical_path); dl->logical_path = strdup(box_list_ptr->logical_path); if (*ses_list == NULL) { *ses_list = dlt = dl; } else { dlt->next = dl; dl->prev = dlt; dlt = dl; } } } return (0); } /* * Routine to return the enclosure type pointed to by the path. * Inputs: The inquiry data for the device in question * * Return: >= 0 is the type: * * Types are defined in storage/libg_fc/common/hdrs/g_state.h: * * 0 -> default (SENA) * 1 -> Daktari * 2 -> Other Enclosures * */ int l_get_enc_type(L_inquiry inq) { if (strncmp((char *)&inq.inq_pid[0], ENCLOSURE_PROD_ID, strlen(ENCLOSURE_PROD_ID)) == 0) { return (SENA_ENC_TYPE); } if (strncmp((char *)&inq.inq_pid[0], DAK_OFF_NAME, strlen(DAK_OFF_NAME)) == 0) { return (DAK_ENC_TYPE); } if (strncmp((char *)&inq.inq_pid[0], DAK_PROD_STR, strlen(DAK_PROD_STR)) == 0) { return (DAK_ENC_TYPE); } /* * ADD OTHERS here if ever needed/wanted, and add to def's * as noted above */ return (UNDEF_ENC_TYPE); } void free_mp_dev_map(gfc_map_mp_t **map_mp_ptr) { gfc_map_mp_t *next = NULL; for (; *map_mp_ptr != NULL; *map_mp_ptr = next) { next = (*map_mp_ptr)->map_next; (void) g_destroy_data((*map_mp_ptr)->map.dev_addr); (void) g_destroy_data(*map_mp_ptr); } *map_mp_ptr = NULL; } /* * This function will return a linked list of device maps * An example of when this will be used is when we want to return the device * map of a vhci path. */ int get_mp_dev_map(char *path, gfc_map_mp_t **map_mp_ptr, int verbose) { int pathcnt, i, err; mp_pathlist_t pathlist; gfc_map_mp_t *new_map_mp_ptr; char drvr_path[MAXPATHLEN]; if (strstr(path, SCSI_VHCI)) { if (g_get_pathlist(path, &pathlist)) { return (L_INVALID_PATH); } pathcnt = pathlist.path_count; for (i = 0; i < pathcnt; i++) { if (pathlist.path_info[i].path_state < MAXPATHSTATE) { /* * only pay attention to paths that are either * ONLINE or STANDBY */ if ((pathlist.path_info[i].path_state == MDI_PATHINFO_STATE_ONLINE) || (pathlist.path_info[i].path_state == MDI_PATHINFO_STATE_STANDBY)) { if ((new_map_mp_ptr = (gfc_map_mp_t *) g_zalloc(sizeof (gfc_map_mp_t))) == NULL) { free(pathlist.path_info); free_mp_dev_map(map_mp_ptr); return (L_MALLOC_FAILED); } (void) strcpy(drvr_path, pathlist.path_info[i].path_hba); (void) strcat(drvr_path, FC_CTLR); if (err = g_get_dev_map(drvr_path, &(new_map_mp_ptr->map), verbose)) { free(pathlist.path_info); free_mp_dev_map(map_mp_ptr); return (err); } /* add newly created map onto list */ if (*map_mp_ptr == NULL) { new_map_mp_ptr->map_next = NULL; *map_mp_ptr = new_map_mp_ptr; } else { new_map_mp_ptr->map_next = *map_mp_ptr; *map_mp_ptr = new_map_mp_ptr; } } } } free(pathlist.path_info); } else { if ((new_map_mp_ptr = (gfc_map_mp_t *)g_zalloc (sizeof (gfc_map_mp_t))) == NULL) { return (L_MALLOC_FAILED); } g_get_dev_map(path, &(new_map_mp_ptr->map), verbose); *map_mp_ptr = new_map_mp_ptr; } return (0); }