#include <sys/kmem.h>
#include <sys/proc.h>
#include <sys/time.h>
#include <sys/conf.h>
#include <sys/file.h>
#include <sys/ddi.h>
#include <sys/ddi_impldefs.h>
#include <sys/modctl.h>
#include <sys/sunddi.h>
#include <sys/scsi/scsi.h>
#include <sys/scsi/impl/scsi_reset_notify.h>
#include <sys/sunmdi.h>
#include <sys/mdi_impldefs.h>
#include <smartpqi.h>
#include <smartpqi_hw.h>
#include <sys/scsi/scsi_types.h>
#include <sys/disp.h>
#include <sys/types.h>
#include <sys/mdb_modapi.h>
#define INVALID_OPT_VAL ((uintptr_t)(-1))
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 <controller number> [-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 <cntlr> display the state for <cntlr> 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, "<undecoded cmd 0x%x>", 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);
}
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);
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);
}
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;
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);
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 <controller> 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) {
addr = (uintptr_t)0;
}
rval = pqi_getopts(addr, argc, argv, &instance, &print_devices);
if (rval != DCMD_OK) {
return (rval);
}
if (mdb_readvar((void *)&adr, "pqi_state") == -1) {
mdb_warn("Cannot read pqi driver variable pqi_softstate.\n");
return (DCMD_ERR);
}
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);
}
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);
}