/* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2023 Nexenta by DDN, Inc. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define INVALID_OPT_VAL ((uintptr_t)(-1)) /* ---- Forward references ---- */ static int smartpqi(uintptr_t, uint_t, int, const mdb_arg_t *); static void smartpqi_help(void); static const mdb_dcmd_t dcmds[] = { { "smartpqi", "-c [-v]", "display smartpqi state", smartpqi, smartpqi_help }, { NULL } }; static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, NULL }; static void smartpqi_help(void) { mdb_printf("%s", "-c display the state for and the no." " of devices attached.\n" "-v provide detailed information about each device attached.\n"); } char * bool_to_str(int v) { return (v ? "TRUE" : "FALSE"); } const mdb_modinfo_t * _mdb_init(void) { return (&modinfo); } static void display_sense_data(struct scsi_extended_sense data) { mdb_printf(" SCSI sense data es_key 0x%x ", data.es_key); mdb_printf(" es_add_code 0x%x ", data.es_add_code); mdb_printf(" es_qual_code 0x%x\n", data.es_qual_code); } static void display_scsi_status(struct scsi_arq_status scsi_status) { mdb_printf(" req pkt status\t\t\t0x%x\n", *(int8_t *)&scsi_status.sts_rqpkt_status); mdb_printf(" req pkt resid\t\t\t0x%x\n", scsi_status.sts_rqpkt_resid); mdb_printf(" req pkt state\t\t\t%d\n", scsi_status.sts_rqpkt_state); mdb_printf(" req pkt state\t\t\t%d\n", scsi_status.sts_rqpkt_statistics); if (scsi_status.sts_status.sts_chk) display_sense_data(scsi_status.sts_sensedata); } static char * cmd_action_str(pqi_cmd_action_t action, char *tmpstr, int tmplen) { switch (action) { case PQI_CMD_UNINIT: return ("UNINIT"); case PQI_CMD_QUEUE: return ("QUEUE"); case PQI_CMD_START: return ("START"); case PQI_CMD_CMPLT: return ("COMPLETE"); case PQI_CMD_TIMEOUT: return ("TIMEOUT"); case PQI_CMD_FAIL: return ("FAIL"); default: (void) mdb_snprintf(tmpstr, tmplen, "BAD ACTION <0x%x>", action); return (tmpstr); } } struct scsi_key_strings pqi_cmds[] = { SCSI_CMDS_KEY_STRINGS, BMIC_READ, "BMIC Read", BMIC_WRITE, "BMIC Write", CISS_REPORT_LOG, "CISS Report Logical", CISS_REPORT_PHYS, "CISS Report Physical", -1, NULL }; static char * mdb_cdb_to_str(uint8_t scsi_cmd, char *tmpstr, int tmplen) { int i = 0; while (pqi_cmds[i].key != -1) { if (scsi_cmd == pqi_cmds[i].key) return ((char *)pqi_cmds[i].message); i++; } (void) mdb_snprintf(tmpstr, tmplen, "", scsi_cmd); return (tmpstr); } static void display_cdb(uint8_t *cdb) { int i, tmplen; char tmpstr[64]; tmplen = sizeof (tmpstr); mdb_printf("CDB %s", mdb_cdb_to_str(cdb[0], tmpstr, tmplen)); for (i = 1; i < SCSI_CDB_SIZE; i++) mdb_printf(":%02x", cdb[i]); mdb_printf("\n"); } static char * pqi_iu_type_to_str(int val) { switch (val) { case PQI_RESPONSE_IU_RAID_PATH_IO_SUCCESS: return ("Success"); case PQI_RESPONSE_IU_AIO_PATH_IO_SUCCESS: return ("AIO Success"); case PQI_RESPONSE_IU_GENERAL_MANAGEMENT: return ("General"); case PQI_RESPONSE_IU_TASK_MANAGEMENT: return ("Task"); case PQI_RESPONSE_IU_RAID_PATH_IO_ERROR: return ("IO Error"); case PQI_RESPONSE_IU_AIO_PATH_IO_ERROR: return ("AIO IO Error"); case PQI_RESPONSE_IU_AIO_PATH_DISABLED: return ("AIO Path Disabled"); default: return ("UNHANDLED"); } } static void display_raid_error_info(uintptr_t error_info) { struct pqi_raid_error_info info; int cnt; if (error_info == 0) return; if ((cnt = mdb_vread((void *)&info, sizeof (struct pqi_raid_error_info), (uintptr_t)error_info)) != sizeof (struct pqi_raid_error_info)) { mdb_warn(" Unable to read Raid error info(%d,%p)\n", cnt, error_info); return; } mdb_printf(" ---- Raid error info ----\n"); mdb_printf(" data_in_result %d\n", info.data_in_result); mdb_printf(" data_out_result %d\n", info.data_out_result); mdb_printf(" status %d\n", info.status); mdb_printf(" status_qualifier %d\n", info.status_qualifier); mdb_printf(" sense_data_length %d\n", info.sense_data_length); mdb_printf(" response_data_length %d\n", info.response_data_length); mdb_printf(" data_in_transferred %d\n", info.data_in_transferred); mdb_printf(" data_out_transferred %d\n", info.data_out_transferred); } static void display_io_request(pqi_io_request_t *io) { if (io == (pqi_io_request_t *)0) return; mdb_printf(" ---- Command IO request ----\n"); mdb_printf(" io_refcount\t\t\t\t%d\n", io->io_refcount); mdb_printf(" io_index\t\t\t\t%d\n", io->io_index); mdb_printf(" io_gen\t\t\t\t%d\n", io->io_gen); mdb_printf(" io_serviced\t\t\t\t%s\n", bool_to_str(io->io_serviced)); mdb_printf(" io_raid_bypass\t\t\t%d\n", io->io_raid_bypass); mdb_printf(" io_status\t\t\t\t%d\n", io->io_status); mdb_printf(" io_iu\t\t\t\t0x%p\n", io->io_iu); mdb_printf(" io_pi\t\t\t\t%d\n", io->io_pi); mdb_printf(" io_iu_type\t\t\t\t%s\n", pqi_iu_type_to_str(io->io_iu_type)); display_raid_error_info((uintptr_t)io->io_error_info); } static int display_cmd(pqi_cmd_t *cmdp) { int read_cnt, tmplen; char tmpstr[64]; pqi_io_request_t pqi_io; tmplen = sizeof (tmpstr); display_cdb(cmdp->pc_cdb); mdb_printf(" cur action\t\t\t%s\n", cmd_action_str(cmdp->pc_cur_action, tmpstr, tmplen)); mdb_printf(" last action\t\t\t%s\n", cmd_action_str(cmdp->pc_last_action, tmpstr, tmplen)); display_scsi_status(cmdp->pc_cmd_scb); mdb_printf(" pc_dma_count\t\t\t%d\n", cmdp->pc_dma_count); mdb_printf(" pc_flags\t\t\t\t0x%x\n", cmdp->pc_flags); mdb_printf(" pc_statuslen\t\t\t%d\n", cmdp->pc_statuslen); mdb_printf(" pc_cmdlen\t\t\t\t%d\n", cmdp->pc_cmdlen); if (cmdp->pc_io_rqst == (pqi_io_request_t *)0) return (DCMD_OK); read_cnt = mdb_vread(&pqi_io, sizeof (pqi_io_request_t), (uintptr_t)cmdp->pc_io_rqst); if (read_cnt == -1) { mdb_warn(" Error reading IO structure address 0x%p - " "skipping diplay of IO commands\n", cmdp->pc_io_rqst); return (DCMD_ERR); } else if (read_cnt != sizeof (pqi_io_request_t)) { mdb_warn(" cannot read IO structure count %d at0x%p - " "skipping diplay of IO commands\n", read_cnt, cmdp->pc_io_rqst); return (DCMD_ERR); } else { display_io_request(&pqi_io); } return (DCMD_OK); } /* * listp - the pointer to the head of the linked list * sz - size of the lest element to be read * current - pointer to current list_node structure in local storage */ static list_node_t * pqi_list_next(list_node_t *listp, size_t sz, void *structp, list_node_t *current) { int rval; if (current->list_next == (list_node_t *)listp) return ((list_node_t *)NULL); if (current->list_next == (list_node_t *)NULL) return ((list_node_t *)NULL); if (current->list_next == current->list_prev) return ((list_node_t *)NULL); rval = mdb_vread(structp, sz, (uintptr_t)current->list_next); if (rval == -1 || (size_t)rval != sz) { mdb_warn("Error reading a next list element so " "skipping display of remaining elements\n"); return ((list_node_t *)NULL); } return (current); } static void pqi_list_head(list_t list, uint8_t *drvp, size_t offset, list_node_t **list_anchor) { *list_anchor = (list_node_t *)(drvp + offset + offsetof(list_t, list_head)); if (*list_anchor == list.list_head.list_next) { *list_anchor = NULL; } } static int pqi_device_list_head(list_t s_devnodes, uint8_t *addr, list_node_t **dev_head, struct pqi_device *dev) { int rval; pqi_list_head(s_devnodes, addr, offsetof(struct pqi_state, s_devnodes), dev_head); if (*dev_head == NULL) return (DCMD_ERR); rval = mdb_vread((void *)dev, sizeof (struct pqi_device), (uintptr_t)s_devnodes.list_head.list_next); if (rval == -1) { mdb_warn(" cannot read device list head (0x%p)\n", *dev_head); return (DCMD_ERR); } return (DCMD_OK); } static int pqi_cmd_list_head(list_t cmds, uint8_t *addr, list_node_t **cmd_head, struct pqi_cmd *cmdp) { int rval; pqi_list_head(cmds, (uint8_t *)addr, offsetof(struct pqi_device, pd_cmd_list), cmd_head); /* Read in the first entry of the command list */ rval = mdb_vread(cmdp, sizeof (struct pqi_cmd), (uintptr_t)cmds.list_head.list_next); if (rval == -1) { mdb_warn(" cannot read initial entry in " "command list (0x%p)\n", cmds.list_head.list_next); } return (rval); } static char * pqi_get_guid(char *pd_guid) { static char myguid[41]; if (mdb_vread(myguid, sizeof (myguid) - 1, (uintptr_t)pd_guid) == -1) myguid[0] = '\0'; return (myguid); } static void display_device_info(pqi_device_t *dev) { char str[40]; mdb_printf("-- Device pd_target %d --\n", dev->pd_target); mdb_printf("pd_devtype\t\t\t\t%d\n", dev->pd_devtype); mdb_printf("device pd_flags\t\t\t\t0x%x\n", dev->pd_flags); mdb_printf("pd_active_cmds\t\t\t\t%d\n", dev->pd_active_cmds); mdb_printf("pd_dip\t\t\t\t\t0x%p\n", dev->pd_dip); mdb_printf("pd_pip\t\t\t\t\t0x%p\n", dev->pd_pip); mdb_printf("pd_pip_offlined\t\t\t\t0x%p\n", dev->pd_pip_offlined); mdb_printf("pd_online\t\t\t\t%s\n", bool_to_str(dev->pd_online)); mdb_printf("pd_scanned\t\t\t\t%s\n", bool_to_str(dev->pd_scanned)); mdb_printf("pd_phys_dev\t\t\t\t%s\n", bool_to_str(dev->pd_phys_dev)); mdb_printf("pd_external_raid\t\t\t%s\n", bool_to_str(dev->pd_external_raid)); mdb_printf("pd_pd_aio_enabled\t\t\t%s\n", bool_to_str(dev->pd_aio_enabled)); mdb_printf("GUID\t\t\t\t\t%s\n", pqi_get_guid(dev->pd_guid)); (void) strncpy(str, (char *)(dev->pd_vendor), sizeof (dev->pd_vendor)); str[sizeof (dev->pd_vendor)] = '\0'; mdb_printf("pd_vendor\t\t\t\t%s\n", str); (void) strncpy(str, (char *)(dev->pd_model), sizeof (dev->pd_model)); str[sizeof (dev->pd_model)] = '\0'; mdb_printf("pd_model\t\t\t\t%s\n", str); } /* * display device info: number of drives attached, number of commands running on * each device, drive data and command data. */ static void pqi_display_devices(list_t s_devnodes, pqi_state_t *drvp, uint_t dev_verbose) { int rval; int dev_count = 0; struct pqi_device d; pqi_device_t *next_dp; pqi_cmd_t *cmdp; struct list_node *list_head; struct list_node *d_list_head; struct list_node *dev_current; struct list_node *cmd_current; struct pqi_device *d_drvrp; /* driver addr of device list entry */ mdb_printf("---- Devices for controller (0x%p) ----\n", ((uint8_t *)drvp) + offsetof(struct pqi_state, s_devnodes)); rval = pqi_device_list_head(s_devnodes, (uint8_t *)drvp, &d_list_head, &d); if (d_list_head == NULL) { mdb_printf("Number of devices %d\n", dev_count); return; } cmdp = (pqi_cmd_t *)mdb_alloc(sizeof (struct pqi_cmd), UM_SLEEP|UM_GC); next_dp = &d; dev_current = (list_node_t *)((uint8_t *)(&d) + offsetof(struct pqi_device, pd_list)); d_drvrp = (pqi_device_t *)(s_devnodes.list_head.list_next); while (dev_current != NULL) { if (dev_verbose) { display_device_info((pqi_device_t *)&d); /* now display command information */ rval = pqi_cmd_list_head(d.pd_cmd_list, (uint8_t *)d_drvrp, &list_head, cmdp); if (rval == -1) { mdb_warn("unable to read the command list head" " for device %d\n", d.pd_target); list_head = NULL; } if (list_head != NULL) { mdb_printf(" ---- Commands for device %d" " (0x%p) ----\n", next_dp->pd_target, list_head); cmd_current = (list_node_t *)((uint8_t *)(cmdp) + offsetof(struct pqi_cmd, pc_list)); while (cmd_current != NULL) { rval = display_cmd(cmdp); if (rval != DCMD_OK) { mdb_warn("Display of commands" " aborted (%d)\n", rval); break; } cmd_current = pqi_list_next(list_head, sizeof (struct pqi_cmd), (void *)cmdp, cmd_current); } } } d_drvrp = (pqi_device_t *)(next_dp->pd_list.list_next); dev_current = pqi_list_next( d_list_head, sizeof (struct pqi_device), (void*)next_dp, dev_current); dev_count++; } if (!dev_verbose) mdb_printf("Number of devices\t\t\t%d\n", dev_count); } static void pqi_display_instance(pqi_state_t *pqi_statep) { mdb_printf("s_dip\t\t\t\t\t0x%p\n", pqi_statep->s_dip); mdb_printf("s_flags\t\t\t\t\t0x%x\n", pqi_statep->s_flags); mdb_printf("s_firmware_version\t\t\t%s\n", pqi_statep->s_firmware_version); mdb_printf("s_offline\t\t\t\t%s\ns_disable_mpxio\t\t\t\t%s\n", bool_to_str(pqi_statep->s_offline), bool_to_str(pqi_statep->s_disable_mpxio)); mdb_printf("s_debug level\t\t\t\t%d\n", pqi_statep->s_debug_level); mdb_printf("---- State for watchdog----\n"); mdb_printf("s_intr_count\t\t\t\t%d\n", pqi_statep->s_intr_count); mdb_printf("s_last_intr_count\t\t\t%d\n", pqi_statep->s_last_intr_count); mdb_printf("s_last_heartbeat_count\t\t\t%d\n", pqi_statep->s_last_heartbeat_count); mdb_printf("---- PQI cpabilities from controller ----\n"); mdb_printf("s_max_inbound_queues\t\t\t%d\n", pqi_statep->s_max_inbound_queues); mdb_printf("s_max_elements_per_iq\t\t\t%d\n", pqi_statep->s_max_elements_per_iq); mdb_printf("s_max_iq_element_length\t\t\t%d\n", pqi_statep->s_max_iq_element_length); mdb_printf("s_max_outbound_queues\t\t\t%d\n", pqi_statep->s_max_outbound_queues); mdb_printf("s_max_elements_per_oq\t\t\t%d\n", pqi_statep->s_max_elements_per_oq); mdb_printf("s_max_elements_per_oq\t\t\t%d\n", pqi_statep->s_max_elements_per_oq); mdb_printf("s_max_oq_element_length\t\t\t%d\n", pqi_statep->s_max_oq_element_length); mdb_printf("s_max_inbound_iu_length_per_firmware\t%d\n", pqi_statep->s_max_inbound_iu_length_per_firmware); mdb_printf("s_max_inbound_queues\t\t\t%d\n", pqi_statep->s_max_inbound_queues); mdb_printf("s_inbound_spanning_supported:\t\t%d\n", pqi_statep->s_inbound_spanning_supported); mdb_printf("s_outbound_spanning_supported:\t\t%dk\n", pqi_statep->s_outbound_spanning_supported); mdb_printf("s_outbound_spanning_supported:\t\t%d\n", pqi_statep->s_outbound_spanning_supported); mdb_printf("s_pqi_mode_enabled:\t\t\t%d\n", pqi_statep->s_pqi_mode_enabled); mdb_printf("s_cmd_queue_len\t\t\t\t%d\n", pqi_statep->s_cmd_queue_len); mdb_printf("---- SIS capabilities from controller ----\n"); mdb_printf("s_max_sg_entries\t\t\t%d\n", pqi_statep->s_max_sg_entries); mdb_printf("s_max_xfer_size\t\t\t\t%d\n", pqi_statep->s_max_xfer_size); mdb_printf("s_max_outstainding_requests\t\t%d\n", pqi_statep->s_max_sg_entries); mdb_printf("---- Computed values from config ----\n"); mdb_printf("s_max_sg_per_iu\t\t\t\t%d\n", pqi_statep->s_max_sg_per_iu); mdb_printf("s_num_elements_per_iq\t\t\t%d\n", pqi_statep->s_num_elements_per_iq); mdb_printf("s_num_elements_per_oq\t\t\t%d\n", pqi_statep->s_num_elements_per_oq); mdb_printf("s_max_inbound_iu_length\t\t\t%d\n", pqi_statep->s_max_inbound_iu_length); mdb_printf("s_num_queue_groups\t\t\t%d\n", pqi_statep->s_num_queue_groups); mdb_printf("s_max_io_slots\t\t\t\t%d\n", pqi_statep->s_max_io_slots); mdb_printf("s_sg_chain_buf_length\t\t\t%d\n", pqi_statep->s_sg_chain_buf_length); mdb_printf("s_max_sectors\t\t\t\t%d\n", pqi_statep->s_max_sectors); mdb_printf("---- IO slot information ----\n"); mdb_printf("s_io_rqst_pool\t\t\t\t0x%p\n", pqi_statep->s_io_rqst_pool); mdb_printf("s_io_wait_cnt\t\t\t\t%d\n", pqi_statep->s_io_wait_cnt); mdb_printf("s_next_io_slot\t\t\t\t%d\n", pqi_statep->s_next_io_slot); mdb_printf("s_io_need\t\t\t\t%d\n", pqi_statep->s_io_need); mdb_printf("s_io_had2wait\t\t\t\t%d\n", pqi_statep->s_io_had2wait); mdb_printf("s_io_sig\t\t\t\t%d\n", pqi_statep->s_io_sig); } static int pqi_getopts(uintptr_t addr, int argc, const mdb_arg_t *argv, uintptr_t *cntlr, uint_t *print_devices) { uintptr_t device = INVALID_OPT_VAL; *cntlr = INVALID_OPT_VAL; *print_devices = FALSE; mdb_getopts(argc, argv, 'v', MDB_OPT_SETBITS, TRUE, print_devices, 'c', MDB_OPT_UINTPTR, (uintptr_t)cntlr, 'd', MDB_OPT_UINTPTR, (uintptr_t)&device, NULL); if (*cntlr == INVALID_OPT_VAL) { mdb_warn("-c required\n"); return (DCMD_USAGE); } return (DCMD_OK); } static int smartpqi(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { int array_size; int rval; int pqi_statesz = sizeof (struct pqi_state); uintptr_t instance = INVALID_OPT_VAL; uintptr_t adr; void **array_vaddr; void **pqi_array; pqi_state_t *pqi_drvp; struct i_ddi_soft_state ss; pqi_state_t *pqi_statep; uint_t print_devices = 0; if ((flags & DCMD_ADDRSPEC) == 0) { /* * MDB has this peculiarity that addr can be non-null * from a previous invocation: * e.g. 0xfffffef49cd64000::smartpqi, * but flags shows that * the command line is ::spamrtpqi or ::smartpai * To make sure the desired command line options are * honored, we set addr to 0 and proceed with evaluating * these command as entered. */ addr = (uintptr_t)0; } rval = pqi_getopts(addr, argc, argv, &instance, &print_devices); if (rval != DCMD_OK) { return (rval); } /* read the address of the pqi_state variable in the smartpqi driver */ if (mdb_readvar((void *)&adr, "pqi_state") == -1) { mdb_warn("Cannot read pqi driver variable pqi_softstate.\n"); return (DCMD_ERR); } /* now read the i_ddi_soft_state structure pointer */ if (mdb_vread((void *)&ss, sizeof (ss), adr) != sizeof (ss)) { mdb_warn("Cannot read smartpqi softstate struct" " pqi_state (Invalid pointer?(0x%p)).\n", (uintptr_t)adr); return (DCMD_ERR); } /* * now allocate space for the array containing the pqi_state * pointers and read in this array */ array_size = ss.n_items * (sizeof (void*)); array_vaddr = ss.array; pqi_array = (void **)mdb_alloc(array_size, UM_SLEEP|UM_GC); if (mdb_vread(pqi_array, array_size, (uintptr_t)array_vaddr) != array_size) { mdb_warn("Corrupted softstate struct\n"); return (DCMD_ERR); } if (instance >= ss.n_items || pqi_array[instance] == NULL) { mdb_warn("smartpqi - no information available for %d\n", instance); return (DCMD_USAGE); } pqi_statep = mdb_alloc(sizeof (struct pqi_state), UM_SLEEP|UM_GC); adr = (uintptr_t)pqi_array[instance]; pqi_drvp = (pqi_state_t *)adr; if (mdb_vread(pqi_statep, pqi_statesz, adr) != pqi_statesz) { mdb_warn("Cannot read pqi_state. adr 0x%p, size %d\n", adr, pqi_statesz); return (DCMD_ERR); } mdb_printf("-------- Controller %d pqi_state (0x%p) --------\n", instance, adr); pqi_display_instance(pqi_statep); pqi_display_devices(pqi_statep->s_devnodes, pqi_drvp, print_devices); return (DCMD_OK); }