#include <sys/mdb_modapi.h>
#include <sys/scsi/scsi.h>
#include <sys/dkio.h>
#include <sys/taskq.h>
#include <sys/scsi/targets/sddef.h>
#define SD_DATA(param) ((sd_str_p)wsp->walk_data)->param
#define SD_DATA_IN_CBACK(param) ((sd_str_p)walk_data)->param
#define SUCCESS WALK_NEXT
#define FAIL WALK_ERR
struct __ddi_xbuf_attr {
kmutex_t xa_mutex;
size_t xa_allocsize;
uint32_t xa_pending;
uint32_t xa_active_limit;
uint32_t xa_active_count;
uint32_t xa_active_lowater;
struct buf *xa_headp;
struct buf *xa_tailp;
kmutex_t xa_reserve_mutex;
uint32_t xa_reserve_limit;
uint32_t xa_reserve_count;
void *xa_reserve_headp;
void (*xa_strategy)(struct buf *, void *, void *);
void *xa_attr_arg;
timeout_id_t xa_timeid;
taskq_t *xa_tq;
};
typedef struct i_ddi_soft_state sd_state_str_t, *sd_state_str_ptr;
typedef struct sd_str {
void *sd_state;
uintptr_t current_root;
int current_list_count;
int valid_root_count;
int silent;
sd_state_str_t sd_state_data;
} sd_str_t, *sd_str_p;
static int
buf_avforw_walk_init(mdb_walk_state_t *wsp)
{
if (wsp->walk_addr == 0) {
mdb_warn("buffer address required with the command\n");
return (WALK_ERR);
}
wsp->walk_data = mdb_alloc(sizeof (buf_t), UM_SLEEP);
return (WALK_NEXT);
}
static int
buf_avforw_walk_step(mdb_walk_state_t *wsp)
{
int status;
if (wsp->walk_addr == 0) {
return (WALK_DONE);
}
if (mdb_vread(wsp->walk_data, sizeof (buf_t), wsp->walk_addr) == -1) {
mdb_warn("failed to read buf at %p", wsp->walk_addr);
return (WALK_DONE);
}
status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
wsp->walk_cbdata);
wsp->walk_addr = (uintptr_t)(((buf_t *)wsp->walk_data)->av_forw);
return (status);
}
static int
buf_callback(uintptr_t addr, const void *walk_data, void *buf_entries)
{
int *count = (int *)buf_entries;
if (*count == 0) {
mdb_printf("============================\n");
mdb_printf("Walking buf list via av_forw\n");
mdb_printf("============================\n");
}
mdb_set_dot(addr);
mdb_eval("$<buf");
mdb_printf("---\n");
(*count)++;
if (((buf_t *)walk_data)->av_forw == NULL) {
mdb_printf("---------------------------\n");
mdb_printf("Processed %d Buf entries\n", *count);
mdb_printf("---------------------------\n");
return (WALK_DONE);
}
return (WALK_NEXT);
}
static void
buf_avforw_walk_fini(mdb_walk_state_t *wsp)
{
mdb_free(wsp->walk_data, sizeof (buf_t));
}
static void
dump_xbuf_attr(struct __ddi_xbuf_attr *xba_ptr, uintptr_t mem_addr)
{
mdb_printf("0x%8lx:\tmutex\t\tallocsize\tpending\n",
mem_addr + offsetof(struct __ddi_xbuf_attr, xa_mutex));
mdb_printf(" \t%lx\t\t%d\t\t%d\n",
xba_ptr->xa_mutex._opaque[0], xba_ptr->xa_allocsize,
xba_ptr->xa_pending);
mdb_printf("0x%8lx:\tactive_limit\tactive_count\tactive_lowater\n",
mem_addr + offsetof(struct __ddi_xbuf_attr, xa_active_limit));
mdb_printf(" \t%lx\t\t%lx\t\t%lx\n",
xba_ptr->xa_active_limit, xba_ptr->xa_active_count,
xba_ptr->xa_active_lowater);
mdb_printf("0x%8lx:\theadp\t\ttailp\n",
mem_addr + offsetof(struct __ddi_xbuf_attr, xa_headp));
mdb_printf(" \t%lx%c\t%lx\n",
xba_ptr->xa_headp, (xba_ptr->xa_headp == 0?'\t':' '),
xba_ptr->xa_tailp);
mdb_printf(
"0x%8lx:\treserve_mutex\treserve_limit\treserve_count\treserve_headp\n",
mem_addr + offsetof(struct __ddi_xbuf_attr, xa_reserve_mutex));
mdb_printf(" \t%lx\t\t%lx\t\t%lx\t\t%lx\n",
xba_ptr->xa_reserve_mutex._opaque[0], xba_ptr->xa_reserve_limit,
xba_ptr->xa_reserve_count, xba_ptr->xa_reserve_headp);
mdb_printf("0x%8lx:\ttimeid\t\ttq\n",
mem_addr + offsetof(struct __ddi_xbuf_attr, xa_timeid));
mdb_printf(" \t%lx%c\t%lx\n",
xba_ptr->xa_timeid, (xba_ptr->xa_timeid == 0?'\t':' '),
xba_ptr->xa_tq);
}
static int
init_softstate_members(mdb_walk_state_t *wsp)
{
wsp->walk_data = mdb_alloc(sizeof (sd_str_t), UM_SLEEP);
SD_DATA(sd_state) = (sd_state_str_ptr)wsp->walk_addr;
SD_DATA(current_list_count) = 0;
SD_DATA(valid_root_count) = 0;
if (mdb_vread((void *)&SD_DATA(sd_state_data),
sizeof (sd_state_str_t), wsp->walk_addr) == -1) {
mdb_warn("failed to sd_state at %p", wsp->walk_addr);
return (WALK_ERR);
}
wsp->walk_addr = (uintptr_t)(SD_DATA(sd_state_data.array));
SD_DATA(current_root) = wsp->walk_addr;
return (WALK_NEXT);
}
#if (!defined(__fibre))
static int
sd_state_walk_init(mdb_walk_state_t *wsp)
{
if (wsp->walk_addr == 0 &&
mdb_readvar(&wsp->walk_addr, "sd_state") == -1) {
mdb_warn("failed to read 'sd_state'");
return (WALK_ERR);
}
return (init_softstate_members(wsp));
}
#else
static int
ssd_state_walk_init(mdb_walk_state_t *wsp)
{
if (wsp->walk_addr == (uintptr_t)NULL &&
mdb_readvar(&wsp->walk_addr, "ssd_state") == -1) {
mdb_warn("failed to read 'ssd_state'");
return (WALK_ERR);
}
return (init_softstate_members(wsp));
}
#endif
static int
sd_state_walk_step(mdb_walk_state_t *wsp)
{
int status;
void *tp;
if (SD_DATA(current_list_count) >= SD_DATA(sd_state_data.n_items)) {
return (WALK_DONE);
}
if (mdb_vread(&tp, sizeof (void *), wsp->walk_addr) == -1) {
mdb_warn("failed to read at %p", wsp->walk_addr);
return (WALK_ERR);
}
status = wsp->walk_callback((uintptr_t)tp, wsp->walk_data,
wsp->walk_cbdata);
if (tp != 0) {
SD_DATA(valid_root_count++);
}
wsp->walk_addr += sizeof (void *);
SD_DATA(current_list_count++);
return (status);
}
static void
sd_state_walk_fini(mdb_walk_state_t *wsp)
{
mdb_free(wsp->walk_data, sizeof (sd_str_t));
}
static int
process_sdlun_waitq(uintptr_t walk_addr, int silent)
{
uintptr_t rootBuf;
buf_t currentBuf;
int sdLunQ_count = 0;
rootBuf = walk_addr;
if (!silent) {
mdb_printf("\nUN WAIT Q:\n");
mdb_printf("----------\n");
}
mdb_printf("UN wait Q head: %lx\n", rootBuf);
while (rootBuf) {
if (!silent) {
mdb_printf("UN WAIT Q list entry:\n");
mdb_printf("------------------\n");
}
if (mdb_vread(¤tBuf, sizeof (buf_t),
(uintptr_t)rootBuf) == -1) {
mdb_warn("failed to read buf at %p",
(uintptr_t)rootBuf);
return (FAIL);
}
if (!silent) {
mdb_set_dot(rootBuf);
mdb_eval("$<buf");
mdb_printf("---\n");
}
rootBuf = (uintptr_t)currentBuf.av_forw;
++sdLunQ_count;
}
if (rootBuf == 0) {
mdb_printf("------------------------------\n");
mdb_printf("Processed %d UN WAIT Q entries\n", sdLunQ_count);
mdb_printf("------------------------------\n");
}
return (SUCCESS);
}
static int
process_xbuf(uintptr_t xbuf_attr, int silent)
{
struct __ddi_xbuf_attr xba;
buf_t xba_current;
void *xba_root;
int xbuf_q_count = 0;
if (xbuf_attr == 0) {
mdb_printf("---------------------------\n");
mdb_printf("No XBUF ATTR entry\n");
mdb_printf("---------------------------\n");
return (SUCCESS);
}
if (mdb_vread((void *)&xba, sizeof (struct __ddi_xbuf_attr),
xbuf_attr) == -1) {
mdb_warn("failed to read xbuf_attr at %p", xbuf_attr);
return (FAIL);
}
if (!silent) {
mdb_printf("\nXBUF ATTR:\n");
mdb_printf("----------\n");
dump_xbuf_attr(&xba, xbuf_attr);
mdb_printf("---\n");
mdb_printf("\nXBUF Q:\n");
mdb_printf("-------\n");
}
mdb_printf("xbuf Q head: %lx\n", xba.xa_headp);
xba_root = (void *) xba.xa_headp;
while ((uintptr_t)xba_root) {
if (!silent) {
mdb_printf("XBUF_Q list entry:\n");
mdb_printf("------------------\n");
}
if (mdb_vread((void *)&xba_current, sizeof (buf_t),
(uintptr_t)xba_root) == -1) {
mdb_warn("failed to read buf at %p",
(uintptr_t)xba_root);
return (FAIL);
}
if (!silent) {
mdb_set_dot((uintptr_t)xba_root);
mdb_eval("$<buf");
mdb_printf("---\n");
}
++xbuf_q_count;
xba_root = (void *)xba_current.av_forw;
}
if (xba_root == NULL) {
mdb_printf("---------------------------\n");
mdb_printf("Processed %d XBUF Q entries\n", xbuf_q_count);
mdb_printf("---------------------------\n");
}
return (SUCCESS);
}
static void
print_footer(const void *walk_data)
{
if (SD_DATA_IN_CBACK(current_list_count) >=
(SD_DATA_IN_CBACK(sd_state_data.n_items) - 1)) {
mdb_printf("---------------------------\n");
mdb_printf("Processed %d UN softstate entries\n",
SD_DATA_IN_CBACK(valid_root_count));
mdb_printf("---------------------------\n");
}
}
static int
sd_callback(uintptr_t addr, const void *walk_data, void *flg_silent)
{
struct sd_lun sdLun;
int silent = *(int *)flg_silent;
if (SD_DATA_IN_CBACK(current_list_count) == 0) {
mdb_printf("walk_addr = %lx\n", SD_DATA_IN_CBACK(sd_state));
mdb_printf("walking sd_state units via ptr: %lx\n",
SD_DATA_IN_CBACK(current_root));
mdb_printf("%d entries in sd_state table\n",
SD_DATA_IN_CBACK(sd_state_data.n_items));
}
mdb_printf("\nun %d: %lx\n", SD_DATA_IN_CBACK(current_list_count),
addr);
mdb_printf("--------------\n");
if (addr == 0) {
print_footer(walk_data);
return (SUCCESS);
}
else if (mdb_vread(&sdLun, sizeof (struct sd_lun), (uintptr_t)addr) ==
sizeof (sdLun)) {
if (!silent) {
mdb_set_dot(addr);
mdb_eval("$<sd_lun");
mdb_printf("---\n");
}
} else {
mdb_warn("failed to read softstate at %p", addr);
return (FAIL);
}
process_xbuf((uintptr_t)sdLun.un_xbuf_attr, silent);
process_sdlun_waitq((uintptr_t)sdLun.un_waitq_headp, silent);
print_footer(walk_data);
return (SUCCESS);
}
#if (!defined(__fibre))
static int
dcmd_sd_state(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
struct sd_lun sdLun;
uint_t silent = 0;
if (mdb_getopts(argc, argv, 's', MDB_OPT_SETBITS, TRUE, &silent, NULL)
!= argc) {
return (DCMD_USAGE);
}
if (!(flags & DCMD_ADDRSPEC)) {
mdb_walk("sd_state", sd_callback, (void *)&silent);
return (DCMD_OK);
} else {
mdb_printf("\nun: %lx\n", addr);
mdb_printf("--------------\n");
if (mdb_vread(&sdLun, sizeof (struct sd_lun),
(uintptr_t)addr) == sizeof (sdLun)) {
if (!silent) {
mdb_set_dot(addr);
mdb_eval("$<sd_lun");
mdb_printf("---\n");
}
} else {
mdb_warn("failed to read softstate at %p", addr);
return (DCMD_OK);
}
process_xbuf((uintptr_t)sdLun.un_xbuf_attr, silent);
process_sdlun_waitq((uintptr_t)sdLun.un_waitq_headp, silent);
}
return (DCMD_OK);
}
#else
static int
dcmd_ssd_state(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
struct sd_lun sdLun;
uint_t silent = 0;
if (mdb_getopts(argc, argv, 's', MDB_OPT_SETBITS, TRUE, &silent, NULL)
!= argc) {
return (DCMD_USAGE);
}
if (!(flags & DCMD_ADDRSPEC)) {
mdb_walk("ssd_state", sd_callback, (void *)&silent);
return (DCMD_OK);
} else {
mdb_printf("\nun: %lx\n", addr);
mdb_printf("--------------\n");
if (mdb_vread(&sdLun, sizeof (struct sd_lun),
(uintptr_t)addr) == sizeof (sdLun)) {
if (!silent) {
mdb_set_dot(addr);
mdb_eval("$<sd_lun");
mdb_printf("---\n");
}
} else {
mdb_warn("failed to read softstate at %p", addr);
return (DCMD_OK);
}
process_xbuf((uintptr_t)sdLun.un_xbuf_attr, silent);
process_sdlun_waitq((uintptr_t)sdLun.un_waitq_headp, silent);
}
return (DCMD_OK);
}
#endif
static int
dcmd_buf_avforw(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
int buf_entries = 0;
if (argc != 0)
return (DCMD_USAGE);
if ((flags & DCMD_ADDRSPEC)) {
mdb_pwalk("buf_avforw", buf_callback, (void *)&buf_entries,
addr);
return (DCMD_OK);
} else {
mdb_printf("buffer address required with the command\n");
}
return (DCMD_USAGE);
}
static const mdb_dcmd_t dcmds[] = {
{ "buf_avforw", ":", "buf_t list via av_forw", dcmd_buf_avforw},
#if (!defined(__fibre))
{ "sd_state", "[-s]", "sd soft state list", dcmd_sd_state},
#else
{ "ssd_state", "[-s]", "ssd soft state list", dcmd_ssd_state},
#endif
{ NULL }
};
static const mdb_walker_t walkers[] = {
{ "buf_avforw", "walk list of buf_t structures via av_forw",
buf_avforw_walk_init, buf_avforw_walk_step, buf_avforw_walk_fini },
#if (!defined(__fibre))
{ "sd_state", "walk all sd soft state queues",
sd_state_walk_init, sd_state_walk_step, sd_state_walk_fini },
#else
{ "ssd_state", "walk all ssd soft state queues",
ssd_state_walk_init, sd_state_walk_step, sd_state_walk_fini },
#endif
{ NULL }
};
static const mdb_modinfo_t modinfo = {
MDB_API_VERSION, dcmds, walkers
};
const mdb_modinfo_t *
_mdb_init(void)
{
return (&modinfo);
}