/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2003 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright 2022 RackTop Systems, Inc. */ #include #include #include #include #include /* Represents global soft state data in walk_step, walk_init */ #define SD_DATA(param) ((sd_str_p)wsp->walk_data)->param /* Represents global soft state data in callback and related routines */ #define SD_DATA_IN_CBACK(param) ((sd_str_p)walk_data)->param #define SUCCESS WALK_NEXT #define FAIL WALK_ERR /* * Primary attribute struct for buf extensions. */ struct __ddi_xbuf_attr { kmutex_t xa_mutex; size_t xa_allocsize; uint32_t xa_pending; /* call to xbuf_iostart() is iminent */ uint32_t xa_active_limit; uint32_t xa_active_count; uint32_t xa_active_lowater; struct buf *xa_headp; /* FIFO buf queue head ptr */ struct buf *xa_tailp; /* FIFO buf queue tail ptr */ 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; }; /* * Provides soft state information like the number of elements, pointer * to soft state elements etc */ typedef struct i_ddi_soft_state sd_state_str_t, *sd_state_str_ptr; /* structure to store soft state statistics */ 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; /* * Function: buf_avforw_walk_init * * Description: MDB calls the init function to initiate the walk, * in response to mdb_walk() function called by the * dcmd 'buf_avforw' or when the user executes the * walk dcmd 'address::walk buf_avforw'. * * Arguments: new mdb_walk_state_t structure. A new structure is * created for each walk, so that multiple instances of * the walker can be active simultaneously. */ 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); } /* * Function: buf_avforw_walk_step * * Description: The step function is invoked by the walker during each * iteration. Its primary job is to determine the address * of the next 'buf_avforw' object, read in the local copy * of this object, call the callback 'buf_callback' function, * and return its status. The iteration is terminated when * the walker encounters a null queue pointer which signifies * end of queue. * * Arguments: mdb_walk_state_t structure */ static int buf_avforw_walk_step(mdb_walk_state_t *wsp) { int status; /* * if walk_addr is null then it effectively means an end of all * buf structures, hence end the iterations. */ if (wsp->walk_addr == 0) { return (WALK_DONE); } /* * Read the contents of the current object, invoke the callback * and assign the next objects address to mdb_walk_state_t structure. */ 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); } /* * Function: buf_callback * * Description: This is the callback function called by the 'buf_avforw' * walker when 'buf_avforw' dcmd is invoked. * It is called during each walk step. It displays the contents * of the current object (addr) passed to it by the step * function. It also prints the header and footer during the * first and the last iteration of the walker. * * Arguments: addr -> current buf_avforw objects address. * walk_data -> private storage for the walker. * buf_entries -> private data for the callback. It represents * the count of objects processed so far. */ static int buf_callback(uintptr_t addr, const void *walk_data, void *buf_entries) { int *count = (int *)buf_entries; /* * If this is the first invocation of the command, print a * header line for the output that will follow. */ if (*count == 0) { mdb_printf("============================\n"); mdb_printf("Walking buf list via av_forw\n"); mdb_printf("============================\n"); } /* * read the object and print the contents. */ mdb_set_dot(addr); mdb_eval("$av_forw == NULL) { mdb_printf("---------------------------\n"); mdb_printf("Processed %d Buf entries\n", *count); mdb_printf("---------------------------\n"); return (WALK_DONE); } return (WALK_NEXT); } /* * Function: buf_avforw_walk_fini * * Description: The buf_avforw_walk_fini is called when the walk is terminated * in response to WALK_DONE in buf_avforw_walk_step. It frees * the walk_data structure. * * Arguments: mdb_walk_state_t structure */ static void buf_avforw_walk_fini(mdb_walk_state_t *wsp) { mdb_free(wsp->walk_data, sizeof (buf_t)); } /* * Function: dump_xbuf_attr * * Description: Prints the contents of Xbuf queue. * * Arguments: object contents pointer and address. */ 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); } /* * Function: init_softstate_members * * Description: Initialize mdb_walk_state_t structure with either 'sd' or * 'ssd' related information. * * Arguments: new mdb_walk_state_t structure */ static int init_softstate_members(mdb_walk_state_t *wsp) { wsp->walk_data = mdb_alloc(sizeof (sd_str_t), UM_SLEEP); /* * store the soft state statistics variables like non-zero * soft state entries, base address, actual count of soft state * processed etc. */ 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)) /* * Function: sd_state_walk_init * * Description: MDB calls the init function to initiate the walk, * in response to mdb_walk() function called by the * dcmd 'sd_state' or when the user executes the * walk dcmd '::walk sd_state'. * The init function initializes the walker to either * the user specified address or the default kernel * 'sd_state' pointer. * * Arguments: new mdb_walk_state_t structure */ 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 /* * Function: ssd_state_walk_init * * Description: MDB calls the init function to initiate the walk, * in response to mdb_walk() function called by the * dcmd 'ssd_state' or when the user executes the * walk dcmd '::walk ssd_state'. * The init function initializes the walker to either * the user specified address or the default kernel * 'ssd_state' pointer. * * Arguments: new mdb_walk_state_t structure */ 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 /* * Function: sd_state_walk_step * * Description: The step function is invoked by the walker during each * iteration. Its primary job is to determine the address * of the next 'soft state' object, read in the local copy * of this object, call the callback 'sd_callback' function, * and return its status. The iteration is terminated when * the soft state counter equals the total soft state count * obtained initially. * * Arguments: mdb_walk_state_t structure */ static int sd_state_walk_step(mdb_walk_state_t *wsp) { int status; void *tp; /* * If all the soft state entries have been processed then stop * future iterations. */ if (SD_DATA(current_list_count) >= SD_DATA(sd_state_data.n_items)) { return (WALK_DONE); } /* * read the object contents, invoke the callback and set the * mdb_walk_state_t structure to the next object. */ 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) { /* Count the number of non-zero un entries. */ SD_DATA(valid_root_count++); } wsp->walk_addr += sizeof (void *); SD_DATA(current_list_count++); return (status); } /* * Function: sd_state_walk_fini * * Description: The sd_state_walk_fini is called when the walk is terminated * in response to WALK_DONE in sd_state_walk_step. It frees * the walk_data structure. * * Arguments: mdb_walk_state_t structure */ static void sd_state_walk_fini(mdb_walk_state_t *wsp) { mdb_free(wsp->walk_data, sizeof (sd_str_t)); } /* * Function: process_sdlun_waitq * * Description: Iterate over the wait Q members of the soft state. * Print the contents of each member. In case of silent mode * the contents are avoided and only the address is printed. * * Arguments: starting queue address, print mode. */ 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) { /* Process the device's cmd. wait Q */ 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("$= (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"); } } /* * Function: sd_callback * * Description: This is the callback function called by the * 'sd_state/ssd_state' walker when 'sd_state/ssd_state' dcmd * invokes the walker. * It is called during each walk step. It displays the contents * of the current soft state object (addr) passed to it by the * step function. It also prints the header and footer during the * first and the last step of the walker. * The contents of the soft state also includes various queues * it includes like Xbuf, semo_close, sdlun_waitq. * * Arguments: addr -> current soft state objects address. * walk_data -> private storage for the walker. * flg_silent -> private data for the callback. It represents * the silent mode of operation. */ static int sd_callback(uintptr_t addr, const void *walk_data, void *flg_silent) { struct sd_lun sdLun; int silent = *(int *)flg_silent; /* * If this is the first invocation of the command, print a * header line for the output that will follow. */ 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 null soft state iterate over to next one */ if (addr == 0) { print_footer(walk_data); return (SUCCESS); } /* * For each buf, we need to read the sd_lun struct, * and then print out its contents, and get the next. */ else if (mdb_vread(&sdLun, sizeof (struct sd_lun), (uintptr_t)addr) == sizeof (sdLun)) { if (!silent) { mdb_set_dot(addr); mdb_eval("$ user specified address or NULL if no address is * specified. * flags -> integer reflecting whether an address was specified, * or if it was invoked by the walker in a loop etc. * argc -> the number of arguments supplied to the dcmd. * argv -> the actual arguments supplied by the user. */ /*ARGSUSED*/ 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; /* Enable the silent mode if '-s' option specified the user */ if (mdb_getopts(argc, argv, 's', MDB_OPT_SETBITS, TRUE, &silent, NULL) != argc) { return (DCMD_USAGE); } /* * If no address is specified on the command line, perform * a global walk invoking 'sd_state' walker. If a particular address * is specified then print the soft state and its queues. */ 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"); /* read the sd_lun struct and print the contents */ if (mdb_vread(&sdLun, sizeof (struct sd_lun), (uintptr_t)addr) == sizeof (sdLun)) { if (!silent) { mdb_set_dot(addr); mdb_eval("$ user specified address or NULL if no address is * specified. * flags -> integer reflecting whether an address was specified, * or if it was invoked by the walker in a loop etc. * argc -> the number of arguments supplied to the dcmd. * argv -> the actual arguments supplied by the user. */ /*ARGSUSED*/ 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; /* Enable the silent mode if '-s' option specified the user */ if (mdb_getopts(argc, argv, 's', MDB_OPT_SETBITS, TRUE, &silent, NULL) != argc) { return (DCMD_USAGE); } /* * If no address is specified on the command line, perform * a global walk invoking 'sd_state' walker. If a particular address * is specified then print the soft state and its queues. */ 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"); /* read the sd_lun struct and print the contents */ if (mdb_vread(&sdLun, sizeof (struct sd_lun), (uintptr_t)addr) == sizeof (sdLun)) { if (!silent) { mdb_set_dot(addr); mdb_eval("$ user specified address. * flags -> integer reflecting whether an address was specified, * or if it was invoked by the walker in a loop etc. * argc -> the number of arguments supplied to the dcmd. * argv -> the actual arguments supplied by the user. */ /*ARGSUSED*/ static int dcmd_buf_avforw(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { int buf_entries = 0; /* it does not take any arguments */ if (argc != 0) return (DCMD_USAGE); /* * If no address was specified on the command line, print the * error msg, else scan and * print out all the buffers available by invoking buf_avforw walker. */ 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); } /* * MDB module linkage information: * * List of structures describing our dcmds, a list of structures * describing our walkers, and a function named _mdb_init to return a pointer * to our module information. */ 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 }; /* * Function: _mdb_init * * Description: Returns mdb_modinfo_t structure which provides linkage and * module identification information to the debugger. * * Arguments: void */ const mdb_modinfo_t * _mdb_init(void) { return (&modinfo); }