devinfo.c revision 095be8246d11658e6dfb4091993683a23b722024
/*
* 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
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/sysmacros.h>
#include <sys/dditypes.h>
#include <sys/ddi_impldefs.h>
#include <sys/ddipropdefs.h>
#include <errno.h>
#include <sys/mdi_impldefs.h>
#include <ctype.h>
#include <mdb/mdb_modapi.h>
#include "nvpair.h"
/*
*/
#define DEVINFO_VERBOSE 0x1
#define DEVINFO_PARENT 0x2
#define DEVINFO_CHILD 0x4
#define DEVINFO_ALLBOLD 0x8
#define DEVINFO_SUMMARY 0x10
/*
* devinfo node state map. Used by devinfo() and devinfo_audit().
* Long words are deliberately truncated so that output
* fits in 80 column with 64-bit addresses.
*/
static const char *const di_state[] = {
"DS_INVAL",
"DS_PROTO",
"DS_LINKED",
"DS_BOUND",
"DS_INITIA",
"DS_PROBED",
"DS_ATTACH",
"DS_READY",
"?"
};
void
prtconf_help(void)
{
mdb_printf("Prints the devinfo tree from a given node.\n"
"Without the address of a \"struct devinfo\" given, "
"prints from the root;\n"
"with an address, prints the parents of, "
"and all children of, that address.\n\n"
"Switches:\n"
" -v be verbose - print device property lists\n"
" -p only print the ancestors of the given node\n"
" -c only print the children of the given node\n");
}
void
devinfo_help(void)
{
mdb_printf("Switches:\n"
" -q be quiet - don't print device property lists\n"
" -s print summary of dev_info structures\n");
}
/*
* Devinfo walker.
*/
typedef struct {
/*
* The "struct dev_info" must be the first thing in this structure.
*/
/*
* This is for the benefit of prtconf().
*/
int din_depth;
typedef struct devinfo_parents_walk_data {
/*
* The following three elements are for walking the parents of a node:
* "dip_base_depth" is the depth of the given node from the root.
* This starts at 1 (if we're walking devinfo_root), because
* it's the size of the dip_parent_{nodes,addresses} arrays,
* and has to include the given node.
* "dip_parent_nodes" is a collection of the parent node structures,
* already read in via mdb_vread(). dip_parent_nodes[0] is the
* root, dip_parent_nodes[1] is a child of the root, etc.
* "dip_parent_addresses" holds the vaddrs of all the parent nodes.
*/
int dip_base_depth;
int
{
int i;
mdb_warn("failed to read 'top_devinfo'");
return (NULL);
}
do {
addr) == -1) {
return (WALK_ERR);
}
if (addr != 0)
dip->dip_base_depth++;
} while (addr != 0);
return (WALK_ERR);
}
}
return (WALK_NEXT);
}
int
{
int status;
return (WALK_DONE);
wsp->walk_cbdata);
return (status);
}
void
{
}
typedef struct devinfo_children_walk_data {
int dic_print_first_node;
int
{
mdb_warn("failed to read 'top_devinfo'");
return (NULL);
}
/*
* This could be set by devinfo_walk_init().
*/
dic->dic_print_first_node = 0;
} else {
}
return (WALK_NEXT);
}
int
{
struct dev_info *v;
return (WALK_DONE);
return (WALK_DONE);
}
if (dic->dic_print_first_node == 0)
else
/*
* "v" is always a virtual address pointer,
* i.e. can't be deref'ed.
*/
} else {
if (v == NULL)
break;
}
v = NULL; /* Done */
}
return (status);
}
void
{
}
typedef struct devinfo_walk_data {
int
{
return (WALK_ERR);
}
/*
* This is why the "devinfo" walker needs to be marginally
* complicated - the child walker needs this initialization
* data, and the best way to get it is out of the parent walker.
*/
return (WALK_ERR);
}
return (WALK_NEXT);
}
int
{
/*
* Keep on going even if the parents walk hit an error.
*/
}
}
} else
return (status);
}
void
{
}
/*
* Given a devinfo pointer, figure out which driver is associated
* with the node (by driver name, from the devnames array).
*/
/*ARGSUSED*/
int
{
if (!(flags & DCMD_ADDRSPEC))
return (DCMD_USAGE);
return (DCMD_ERR);
}
/* No driver attached to this devinfo - nothing to do. */
return (DCMD_ERR);
}
mdb_warn("failed to determine driver name");
return (DCMD_ERR);
}
return (DCMD_OK);
}
typedef struct devnames_walk {
int dnw_ndx;
int dnw_devcnt;
int
{
int devcnt;
mdb_warn("devnames walker only supports global walks\n");
return (WALK_ERR);
}
mdb_warn("failed to read 'devcnt'");
return (WALK_ERR);
}
mdb_warn("failed to read 'devnamesp'");
return (WALK_ERR);
}
return (WALK_ERR);
}
return (WALK_NEXT);
}
int
{
int status;
return (WALK_DONE);
return (status);
}
void
{
}
int
{
mdb_warn("a dev_info struct address must be provided\n");
return (WALK_ERR);
}
return (WALK_ERR);
}
return (WALK_DONE);
}
mdb_warn("failed to read parent dev_info struct at %p",
return (WALK_ERR);
}
return (WALK_NEXT);
}
int
{
return (WALK_DONE);
return (WALK_DONE);
}
}
int
{
int status;
return (WALK_DONE);
return (WALK_DONE);
return (status);
}
/*
* Helper functions.
*/
static int
is_printable_string(unsigned char *prop_value)
{
while (*prop_value != 0)
if (!isprint(*prop_value++))
return (0);
return (1);
}
static void
devinfo_print_props_type(int type) {
switch (type) {
case DDI_PROP_TYPE_ANY:
type_str = "any";
break;
case DDI_PROP_TYPE_COMPOSITE:
type_str = "composite";
break;
case DDI_PROP_TYPE_INT64:
type_str = "int64";
break;
case DDI_PROP_TYPE_INT:
type_str = "int";
break;
case DDI_PROP_TYPE_BYTE:
type_str = "byte";
break;
case DDI_PROP_TYPE_STRING:
type_str = "string";
break;
}
else
}
static void
unsigned char *prop_value, int prop_value_len)
{
int i;
mdb_printf("value=");
if (elem_size == 0) {
/* if elem_size == 0, then we are printing out string(s) */
char *p = (char *)prop_value;
for (i = 0; i < nelem - 1; i++) {
mdb_printf("'%s' + ", p);
p += strlen(p) + 1;
}
mdb_printf("'%s'", p);
} else {
/*
* if elem_size != 0 then we are printing out an array
* where each element is of elem_size
*/
for (i = 1; i < prop_value_len; i++) {
if ((i % elem_size) == 0) {
mdb_nhconvert(&prop_value[i],
&prop_value[i], elem_size);
mdb_printf(".");
}
}
}
}
/*
* devinfo_print_props_guess()
* Guesses how to interpret the value of the property
*
* Params:
* type - Should be the type value of the property
* prop_val - Pointer to the property value data buffer
* prop_len - Length of the property value data buffer
*
* Return values:
* nelem - The number of elements stored in the property value
* data buffer pointed to by prop_val.
* elem_size - The size (in bytes) of the elements stored in the property
* value data buffer pointed to by prop_val.
* Upon return if elem_size == 0 and nelem != 0 then
* the property value data buffer contains strings
* len_err - There was an error with the length of the data buffer.
* Its size is not a multiple of the array value type.
* It will be interpreted as an array of bytes.
*/
static void
{
*len_err = 0;
*elem_size = 0;
*nelem = 0;
return;
}
/* by default, assume an array of bytes */
*elem_size = 1;
switch (type) {
case DDI_PROP_TYPE_BYTE:
/* default case, that was easy */
break;
case DDI_PROP_TYPE_INT64:
} else {
/* array is not a multiple of type size, error */
*len_err = 1;
}
break;
case DDI_PROP_TYPE_INT:
if ((prop_len % sizeof (int)) == 0) {
*elem_size = sizeof (int);
} else {
/* array is not a multiple of type size, error */
*len_err = 1;
}
break;
case DDI_PROP_TYPE_STRING:
case DDI_PROP_TYPE_COMPOSITE:
case DDI_PROP_TYPE_ANY:
default:
/*
* if we made it here the type is either unknown
* or a string. Try to interpret is as a string
* and if that fails assume an array of bytes.
*/
unsigned char *s = prop_val;
int i;
/* assume an array of strings */
*elem_size = 0;
*nelem = 0;
for (i = 0; i < prop_len; i++) {
if (prop_val[i] != '\0')
continue;
/*
* If the property is typed as a string
* property, then interpret empty strings
* as strings. Otherwise default to an
* array of bytes. If there are unprintable
* characters, always default to an array of
* bytes.
*/
if ((*s == '\0' && type !=
!is_printable_string(s)) {
*elem_size = 1;
break;
}
(*nelem)++;
s = &prop_val[i + 1];
}
}
break;
}
}
static void
{
if (p == NULL)
return;
mdb_printf("properties at %p:\n", p);
while (p != NULL) {
char prop_name[128];
unsigned char *prop_value;
/* read in the property struct */
mdb_warn("could not read property at 0x%p", p);
break;
}
/* print the property name */
mdb_warn("could not read property name at 0x%p",
goto next;
}
/* get the property type and print it out */
/* get the property value */
mdb_warn("could not read property value at "
goto next;
}
} else {
prop_value = NULL;
}
/* take a guess at interpreting the property value */
/* print out the number ot items */
/* print out any associated device information */
mdb_printf(" dev=");
mdb_printf("any");
mdb_printf("unknown");
else
mdb_printf("(%u,%u)",
}
/* print out the property value */
if (prop_value != NULL) {
mdb_printf("\n");
if (prop_len_error)
mdb_printf("NOTE: prop length is not a "
"multiple of element size\n");
}
next:
mdb_printf("\n");
}
}
static void
switch (state) {
case MDI_PATHINFO_STATE_INIT:
type_str = "init";
break;
type_str = "online";
break;
type_str = "standby";
break;
case MDI_PATHINFO_STATE_FAULT:
type_str = "fault";
break;
type_str = "offline";
break;
}
else
}
static void
struct mdi_pathinfo *pip;
/* we only print out multipathing info for client nodes */
if ((mdi_component & MDI_COMPONENT_CLIENT) == 0)
return;
/* read in the client multipathing info */
mdb_warn("failed to read mdi_client at %p",
goto exit;
}
/*
* walk through the clients list of pathinfo structures and print
* out the properties for each path
*/
char binding_name[128];
struct mdi_pathinfo pi;
/* read in the pathinfo structure */
mdb_warn("failed to read mdi_pathinfo at %p",
goto exit;
}
/* read in the pchi (path host adapter) info */
mdb_warn("failed to read mdi_pchi at %p",
goto exit;
}
/* read in the dip of the phci so we can get it's name */
mdb_warn("failed to read mdi_pchi at %p",
goto exit;
}
mdb_warn("failed to read binding_name at %p",
goto exit;
}
/* print out the pathing info */
goto exit;
}
}
exit:
}
typedef struct devinfo_cb_data {
static int
{
/*
* We know the walker passes us extra data after the dev_info.
*/
char binding_name[128];
mdb_warn("failed to read binding_name at %p",
return (WALK_ERR);
}
/* if there are any global properties, get a pointer to them */
mdb_warn("failed to read global prop_list at %p",
return (WALK_ERR);
}
}
mdb_printf("%<b>");
mdb_printf("%</b>");
if (dev->devi_instance >= 0)
mdb_printf(" (driver not attached)");
mdb_printf(" (could not determine driver name)");
else
mdb_printf("\n");
}
return (WALK_NEXT);
}
/*ARGSUSED*/
int
{
int status;
return (DCMD_USAGE);
mdb_warn("failed to read 'top_devinfo'");
return (NULL);
}
if ((flags & DCMD_ADDRSPEC) == 0) {
addr = devinfo_root;
}
(DEVINFO_PARENT | DEVINFO_CHILD)) {
} else {
mdb_warn("failed to read device");
return (DCMD_ERR);
}
}
if (status == -1) {
mdb_warn("couldn't walk devinfo tree");
return (DCMD_ERR);
}
return (DCMD_OK);
}
/*ARGSUSED*/
int
{
char tmpstr[MODMAXNAMELEN];
char nodename[MODMAXNAMELEN];
char bindname[MAXPATHLEN];
static const mdb_bitmask_t devi_state_masks[] = {
{ NULL, 0, 0 }
};
static const mdb_bitmask_t devi_flags_masks[] = {
{ "ATTACHED_CHILDREN",
{ "DEVI_REGISTERED_DEVID",
{ "PHCI_SIGNALS_VHCI",
{ NULL, 0, 0 }
};
!= argc)
return (DCMD_USAGE);
if ((flags & DCMD_ADDRSPEC) == 0) {
"devinfo doesn't give global information (try prtconf)\n");
return (DCMD_ERR);
}
"%-?s %5s %?s %-20s %-s\n"
"%-?s %5s %?s %-20s %-s\n"
"%<u>%-?s %5s %?s %-20s %-15s%</u>\n",
"DEVINFO", "MAJ", "REFCNT", "NODENAME", "NODESTATE",
"", "INST", "CIRCULAR", "BINDNAME", "STATE",
"", "", "THREAD", "", "FLAGS");
mdb_warn("failed to read device");
return (DCMD_ERR);
}
*nodename = '\0';
}
}
*bindname = '\0';
mdb_printf("%0?p %5d %?d %-20s %s\n",
mdb_printf("%?s %5d %?d %-20s <%b>\n",
mdb_printf("%?s %5s %?p %-20s <%b>\n\n",
return (DCMD_OK);
} else {
}
}
/*ARGSUSED*/
int
{
char name[MODMAXNAMELEN];
mdb_warn("couldn't read devi_binding_name at %p",
return (WALK_ERR);
}
return (WALK_NEXT);
}
/*ARGSUSED*/
int
{
char name[MODMAXNAMELEN];
if (!(flags & DCMD_ADDRSPEC))
return (DCMD_USAGE);
return (DCMD_ERR);
}
return (DCMD_ERR);
}
mdb_warn("couldn't walk devinfo");
return (DCMD_ERR);
}
return (DCMD_OK);
}
static int
{
mdb_warn("failed to read 'devcnt'");
return (-1);
}
mdb_warn("failed to read 'devnamesp'");
return (-1);
}
return (-1);
}
return (0);
}
/*ARGSUSED*/
int
{
static const mdb_bitmask_t dn_flag_bits[] = {
{ "DN_OPEN_RETURNS_EINTR", \
{ "DN_NETWORK_PHYSDRIVER", \
{ NULL, 0, 0 }
};
size_t i;
if (argc - i > 1)
return (DCMD_USAGE);
}
if (opt_m) {
if (!(flags & DCMD_ADDRSPEC))
return (DCMD_USAGE);
return (DCMD_ERR);
} else if (!(flags & DCMD_ADDRSPEC)) {
mdb_warn("failed to walk devnames");
return (DCMD_ERR);
}
return (DCMD_OK);
}
else
return (DCMD_ERR);
}
return (DCMD_ERR);
}
if (DCMD_HDRSPEC(flags)) {
if (opt_v)
else
}
return (DCMD_OK); /* Skip empty slots if we're printing table */
if (opt_v) {
mdb_inc_indent(2);
}
mdb_dec_indent(2);
} else
return (DCMD_OK);
}
/*ARGSUSED*/
int
{
if (flags & DCMD_ADDRSPEC)
return (DCMD_USAGE);
return (DCMD_USAGE);
mdb_warn("failed to convert name to major number\n");
return (DCMD_ERR);
}
return (DCMD_OK);
}
/*
* Get a numerical argument of a dcmd from addr if an address is specified
* or from argv if no address is specified. Return the argument in ret.
*/
static int
{
} else {
return (-1);
}
return (0);
}
/*ARGSUSED*/
int
{
const char *name;
return (DCMD_USAGE);
mdb_warn("failed to convert major number to name\n");
return (DCMD_ERR);
}
return (DCMD_OK);
}
/*ARGSUSED*/
int
{
return (DCMD_USAGE);
if (flags & DCMD_PIPE_OUT)
else
return (DCMD_OK);
}
/*ARGSUSED*/
int
{
return (DCMD_USAGE);
if (flags & DCMD_PIPE_OUT)
else
return (DCMD_OK);
}
/*ARGSUSED*/
int
{
return (DCMD_USAGE);
if (DCMD_HDRSPEC(flags)) {
"MINOR");
}
return (DCMD_OK);
}
/*ARGSUSED*/
int
{
int instance;
if (argc != 1) {
return (DCMD_USAGE);
}
else
} else {
mdb_warn("couldn't determine softstate for "
"instance %d", instance);
}
return (DCMD_ERR);
}
return (DCMD_OK);
}
/*
* Walker for all possible pointers to a driver state struct in an
* i_ddi_soft_state instance chain. Returns all non-NULL pointers.
*/
typedef struct soft_state_walk {
void **ssw_pointers; /* to driver state structs */
int
{
return (WALK_DONE);
mdb_warn("failed to read i_ddi_soft_state at %p",
return (WALK_ERR);
}
/* Read array of pointers to state structs into local storage. */
mdb_warn("failed to read i_ddi_soft_state at %p",
return (WALK_ERR);
}
return (WALK_NEXT);
}
int
{
/*
* If the entry indexed has a valid pointer to a soft state struct,
* invoke caller's callback func.
*/
wsp->walk_cbdata);
}
return (WALK_DONE);
return (status);
}
int
{
wsp->walk_cbdata);
return (WALK_DONE);
return (status);
}
/*ARGSUSED*/
int
{
return (DCMD_USAGE);
if (flags & DCMD_ADDRSPEC) {
/*
* If there's an address, then it's a major number
*/
} else {
/*
* We interpret the last argument. Any other arguments are
* forwarded to "devinfo"
*/
argc--;
/* the argument shouldn't be an option */
return (DCMD_USAGE);
} else {
mdb_warn("failed to get major number for %s\n",
return (DCMD_ERR);
}
}
}
return (DCMD_ERR);
return (DCMD_ERR);
}
return (DCMD_ERR);
}
return (DCMD_OK);
}
/*
* walk binding hashtable (as of of driver names (e.g., mb_hashtab))
*/
int
{
return (WALK_ERR);
mdb_warn("failed to read mb_hashtab");
return (WALK_ERR);
}
return (WALK_NEXT);
}
int
{
int status;
/*
* Walk the singly-linked list of struct bind
*/
mdb_warn("failed to read bind struct at %p",
return (WALK_ERR);
}
return (status);
}
}
return (WALK_DONE);
return (WALK_NEXT);
}
/*ARGSUSED*/
int
{
/* Arbitrary lengths based on output format below */
return (DCMD_USAGE);
/* Allow null addresses to be passed (as from a walker) */
return (DCMD_OK);
return (DCMD_ERR);
}
if (DCMD_HDRSPEC(flags)) {
mdb_printf("%<u>%?s% %-5s %s%</u>\n",
"NEXT", "MAJOR", "NAME(S)");
}
mdb_warn("failed to read 'name'");
/* There may be bind_name, so this may fail */
mdb_printf("%?p %5d %s\n",
} else {
mdb_printf("%?p %5d %s %s\n",
}
return (DCMD_OK);
}
typedef struct devinfo_audit_log_walk_data {
int dil_max; /* maximum index */
int dil_start; /* starting index */
int dil_index; /* current walking index */
int
{
/* read in devinfo_log_header structure */
mdb_warn("failed to read 'devinfo_audit_log'");
return (WALK_ERR);
}
devinfo_audit_log) == -1) {
mdb_warn("couldn't read devinfo_log_header at %p",
return (WALK_ERR);
}
return (WALK_DONE);
return (WALK_NEXT);
}
int
{
/* read in current entry and invoke callback */
return (WALK_DONE);
}
/* step to the previous log entry in time */
return (WALK_DONE);
}
return (status);
}
void
{
}
/*
* display devinfo_audit_t stack trace
*/
/*ARGSUSED*/
int
{
int i, depth;
if ((flags & DCMD_ADDRSPEC) == 0)
return (DCMD_USAGE);
return (DCMD_USAGE);
if (DCMD_HDRSPEC(flags)) {
mdb_printf(" %-?s %16s %-?s %-?s %5s\n",
"AUDIT", "TIMESTAMP", "THREAD", "DEVINFO", "STATE");
}
return (DCMD_ERR);
}
mdb_printf(" %0?p %16llx %0?p %0?p %s\n",
if (!verbose)
return (DCMD_OK);
mdb_inc_indent(4);
/*
* Guard against bogus da_depth in case the devinfo_audit_t
* is corrupt or the address does not really refer to a
* devinfo_audit_t.
*/
for (i = 0; i < depth; i++)
mdb_printf("\n");
mdb_dec_indent(4);
return (DCMD_OK);
}
int
{
if (flags & DCMD_ADDRSPEC)
return (DCMD_OK);
}
typedef struct devinfo_audit_node_walk_data {
int dih_on_devinfo; /* devi_audit on dev_info struct */
int
{
/* read in devinfo structure */
return (WALK_ERR);
}
/* read in devi_audit structure */
== -1) {
return (WALK_ERR);
}
return (WALK_NEXT);
}
int
{
return (WALK_DONE);
skip:
/* read in previous entry */
return (WALK_DONE);
return (WALK_DONE);
}
/* check if last log was over-written */
return (WALK_DONE);
/*
* skip the first common log entry, which is a duplicate of
* the devi_audit buffer on the dev_info structure
*/
if (dih->dih_on_devinfo) {
dih->dih_on_devinfo = 0;
goto skip;
}
return (WALK_NEXT);
}
void
{
}
int
{
if (!(flags & DCMD_ADDRSPEC))
return (DCMD_USAGE);
return (DCMD_OK);
}
/*
* mdb support for per-devinfo fault management data
*/
/*ARGSUSED*/
int
{
struct i_ddi_fmhdl fhdl;
if ((flags & DCMD_ADDRSPEC) == 0)
return (DCMD_USAGE);
if (DCMD_HDRSPEC(flags)) {
mdb_printf("%<u>%?s IPL CAPS DROP FMCFULL FMCMISS ACCERR "
"DMAERR %?s %?s%</u>\n", "ADDR", "DMACACHE", "ACCCACHE");
}
return (DCMD_ERR);
}
mdb_warn("failed to read devinfo fm struct at %p",
return (DCMD_ERR);
}
mdb_printf("%?p %3u %c%c%c%c %4llu %7llu %7llu %6llu %6llu %?p %?p\n",
return (DCMD_OK);
}
/*ARGSUSED*/
int
{
struct i_ddi_fmc_entry fce;
if ((flags & DCMD_ADDRSPEC) == 0)
return (DCMD_USAGE);
if (DCMD_HDRSPEC(flags)) {
"RESOURCE", "BUS_SPECIFIC");
}
return (DCMD_ERR);
}
mdb_printf("%?p %?p %?p\n",
return (DCMD_OK);
}
int
{
return (WALK_ERR);
return (WALK_ERR);
}
return (WALK_DONE);
return (WALK_NEXT);
}
int
{
int status;
struct i_ddi_fmc_entry fe;
mdb_warn("failed to read active fm cache entry at %p",
return (WALK_DONE);
}
return (WALK_DONE);
return (status);
}
int
{
mdb_warn("a dev_info struct address must be provided\n");
return (WALK_ERR);
}
return (WALK_ERR);
}
return (WALK_NEXT);
}
int
{
struct ddi_minor_data md;
return (WALK_DONE);
return (WALK_DONE);
}
}
static const char *const md_type[] = {
"DDI_MINOR",
"DDI_ALIAS",
"DDI_DEFAULT",
"DDI_I_PATH",
"?"
};
/*ARGSUSED*/
static int
{
char name[128];
char nodetype[128];
char *spectype;
*name = '\0';
*nodetype = '\0';
switch (mdp->ddm_spec_type) {
default: spectype = "?"; break;
}
mdb_printf("%?p %16lx %-4s %-11s %-10s %s\n",
return (WALK_NEXT);
}
/*ARGSUSED*/
int
{
return (DCMD_USAGE);
if (DCMD_HDRSPEC(flags))
mdb_printf("%<u>%?s %16s %-4s %-11s %-10s %-16s%</u>\n",
"ADDR", "DEV", "SPEC", "TYPE", "NAME", "NODETYPE");
mdb_warn("can't walk minornode");
return (DCMD_ERR);
}
return (DCMD_OK);
}