xref: /illumos-gate/usr/src/cmd/mdb/intel/modules/generic_cpu/gcpu.c (revision bfed486ad8de8b8ebc6345a8e10accae08bf2f45)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <mdb/mdb_modapi.h>
28 #include <generic_cpu/gcpu.h>
29 #include <sys/cpu_module_impl.h>
30 #include <sys/cpu_module_ms_impl.h>
31 
32 typedef struct cmi_hdl_impl {
33 	enum cmi_hdl_class cmih_class;		/* Handle nature */
34 	struct cmi_hdl_ops *cmih_ops;		/* Operations vector */
35 	uint_t cmih_chipid;			/* Chipid of cpu resource */
36 	uint_t cmih_coreid;			/* Core within die */
37 	uint_t cmih_strandid;			/* Thread within core */
38 	boolean_t cmih_mstrand;			/* cores are multithreaded */
39 	volatile uint32_t *cmih_refcntp;	/* Reference count pointer */
40 	uint64_t cmih_msrsrc;			/* MSR data source flags */
41 	void *cmih_hdlpriv;			/* cmi_hw.c private data */
42 	void *cmih_spec;			/* cmi_hdl_{set,get}_specific */
43 	void *cmih_cmi;				/* cpu mod control structure */
44 	void *cmih_cmidata;			/* cpu mod private data */
45 	const struct cmi_mc_ops *cmih_mcops;	/* Memory-controller ops */
46 	void *cmih_mcdata;			/* Memory-controller data */
47 	uint64_t cmih_flags;
48 } cmi_hdl_impl_t;
49 
50 struct cmi_hdl_arr_ent {
51 	volatile uint32_t cmae_refcnt;
52 	cmi_hdl_impl_t *cmae_hdlp;
53 };
54 
55 typedef struct cmi {
56 	struct cmi *cmi_next;
57 	struct cmi *cmi_prev;
58 	const cmi_ops_t *cmi_ops;
59 	struct modctl *cmi_modp;
60 	uint_t cmi_refcnt;
61 } cmi_t;
62 
63 typedef struct cms {
64 	struct cms *cms_next;
65 	struct cms *cms_prev;
66 	const cms_ops_t *cms_ops;
67 	struct modctl *cms_modp;
68 	uint_t cms_refcnt;
69 } cms_t;
70 
71 struct cms_ctl {
72 	cms_t *cs_cms;
73 	void *cs_cmsdata;
74 };
75 
76 #define	CMI_MAX_CHIPS			16
77 #define	CMI_MAX_CORES_PER_CHIP		8
78 #define	CMI_MAX_STRANDS_PER_CORE	2
79 #define	CMI_HDL_HASHSZ (CMI_MAX_CHIPS * CMI_MAX_CORES_PER_CHIP * \
80     CMI_MAX_STRANDS_PER_CORE)
81 
82 struct cmih_walk_state {
83 	int idx;
84 	struct cmi_hdl_arr_ent *arrent;
85 };
86 
87 static int
88 cmih_walk_init(mdb_walk_state_t *wsp)
89 {
90 	size_t sz = CMI_HDL_HASHSZ * sizeof (struct cmi_hdl_arr_ent);
91 	struct cmih_walk_state *awsp;
92 	uintptr_t addr;
93 
94 	if (wsp->walk_addr != NULL) {
95 		mdb_warn("cmihdl is a global walker\n");
96 		return (WALK_ERR);
97 	}
98 
99 	if (mdb_readvar(&addr, "cmi_hdl_arr") == -1) {
100 		mdb_warn("read of cmi_hdl_arr failed");
101 		return (WALK_ERR);
102 	} else if (addr == NULL) {
103 		return (WALK_DONE);
104 	}
105 
106 	wsp->walk_data = awsp =
107 	    mdb_zalloc(sizeof (struct cmih_walk_state), UM_SLEEP);
108 	awsp->arrent = mdb_alloc(sz, UM_SLEEP);
109 
110 	if (mdb_vread(awsp->arrent, sz, addr) != sz) {
111 		mdb_warn("read of cmi_hdl_arr array at 0x%p failed", addr);
112 		return (WALK_ERR);
113 	}
114 
115 	wsp->walk_addr =
116 	    (uintptr_t)((struct cmi_hdl_arr_ent *)wsp->walk_data)->cmae_hdlp;
117 
118 	return (WALK_NEXT);
119 }
120 
121 static int
122 cmih_walk_step(mdb_walk_state_t *wsp)
123 {
124 	struct cmih_walk_state *awsp = wsp->walk_data;
125 	uintptr_t addr = (uintptr_t)(awsp->arrent)[awsp->idx].cmae_hdlp;
126 	cmi_hdl_impl_t hdl;
127 	int rv;
128 
129 	if (wsp->walk_addr == NULL || addr == NULL)
130 		return (++awsp->idx < CMI_HDL_HASHSZ ? WALK_NEXT : WALK_DONE);
131 
132 	if (mdb_vread(&hdl, sizeof (hdl), addr) != sizeof (hdl)) {
133 		mdb_warn("read of handle at 0x%p failed", addr);
134 		return (WALK_DONE);
135 	}
136 
137 	if ((rv = wsp->walk_callback(addr, (void *)&hdl,
138 	    wsp->walk_cbdata)) != WALK_NEXT)
139 		return (rv);
140 
141 	return (++awsp->idx < CMI_HDL_HASHSZ ? WALK_NEXT : WALK_DONE);
142 }
143 
144 static void
145 cmih_walk_fini(mdb_walk_state_t *wsp)
146 {
147 	struct cmih_walk_state *awsp = wsp->walk_data;
148 
149 	if (awsp != NULL) {
150 		if (awsp->arrent != NULL)
151 			mdb_free(awsp->arrent, CMI_HDL_HASHSZ *
152 			    sizeof (struct cmih_walk_state));
153 		mdb_free(wsp->walk_data, sizeof (struct cmih_walk_state));
154 	}
155 }
156 
157 struct cmihdl_cb {
158 	int mod_cpuid;
159 	int mod_chipid;
160 	int mod_coreid;
161 	int mod_strandid;
162 	uintptr_t mod_hdladdr;
163 };
164 
165 static int
166 cmihdl_cb(uintptr_t addr, const void *arg, void *data)
167 {
168 	cmi_hdl_impl_t *hdl = (cmi_hdl_impl_t *)arg;
169 	struct cmihdl_cb *cbp = data;
170 	cpu_t *cp;
171 	int rv;
172 
173 	if (cbp->mod_cpuid != -1) {
174 		cp = mdb_alloc(sizeof (cpu_t), UM_SLEEP);
175 		if (mdb_vread(cp, sizeof (cpu_t),
176 		    (uintptr_t)hdl->cmih_hdlpriv) != sizeof (cpu_t)) {
177 			mdb_warn("Read of cpu_t at 0x%p failed",
178 			    hdl->cmih_hdlpriv);
179 			mdb_free(cp, sizeof (cpu_t));
180 			return (WALK_ERR);
181 		}
182 
183 		if (cp->cpu_id == cbp->mod_cpuid) {
184 			cbp->mod_hdladdr = addr;
185 			rv = WALK_DONE;
186 		} else {
187 			rv = WALK_NEXT;
188 		}
189 
190 		mdb_free(cp, sizeof (cpu_t));
191 		return (rv);
192 	} else {
193 		if (hdl->cmih_chipid == cbp->mod_chipid &&
194 		    hdl->cmih_coreid == cbp->mod_coreid &&
195 		    hdl->cmih_strandid == cbp->mod_strandid) {
196 			cbp->mod_hdladdr = addr;
197 			return (WALK_DONE);
198 		} else {
199 			return (WALK_NEXT);
200 		}
201 	}
202 }
203 
204 static int
205 cmihdl_disp(uintptr_t addr, cmi_hdl_impl_t *hdl)
206 {
207 	struct cms_ctl cmsctl;			/* 16 bytes max */
208 	struct modctl cmimodc, cmsmodc;		/* 288 bytes max */
209 	cmi_t cmi;				/* 40 bytes max */
210 	cms_t cms;				/* 40 bytes max */
211 	cpu_t *cp;
212 	char cmimodnm[25], cmsmodnm[25];	/* 50 bytes */
213 	char cpuidstr[4], hwidstr[16];
214 	int native = hdl->cmih_class == CMI_HDL_NATIVE;
215 	uint32_t refcnt;
216 
217 	cmimodnm[0] = cmsmodnm[0] = '-';
218 	cmimodnm[1] = cmsmodnm[1] = '\0';
219 
220 	if (hdl->cmih_cmi != NULL) {
221 		if (mdb_vread(&cmi, sizeof (cmi_t),
222 		    (uintptr_t)hdl->cmih_cmi) != sizeof (cmi)) {
223 			mdb_warn("Read of cmi_t at 0x%p failed",
224 			    hdl->cmih_cmi);
225 			return (0);
226 		}
227 
228 		if (cmi.cmi_modp != NULL) {
229 			if (mdb_vread(&cmimodc, sizeof (struct modctl),
230 			    (uintptr_t)cmi.cmi_modp) != sizeof (cmimodc)) {
231 				mdb_warn("Read of modctl at 0x%p failed",
232 				    cmi.cmi_modp);
233 				return (0);
234 			}
235 
236 			if (mdb_readstr(cmimodnm, sizeof (cmimodnm),
237 			    (uintptr_t)cmimodc.mod_modname) == -1) {
238 				mdb_warn("Read of cmi module name at 0x%p "
239 				    "failed", cmimodc.mod_modname);
240 				return (0);
241 			}
242 		}
243 	}
244 
245 	if (hdl->cmih_spec != NULL) {
246 		if (mdb_vread(&cmsctl, sizeof (struct cms_ctl),
247 		    (uintptr_t)hdl->cmih_spec) != sizeof (cmsctl)) {
248 			mdb_warn("Read of struct cms_ctl at 0x%p failed",
249 			    hdl->cmih_spec);
250 			return (0);
251 		}
252 
253 		if (mdb_vread(&cms, sizeof (cms_t),
254 		    (uintptr_t)cmsctl.cs_cms) != sizeof (cms)) {
255 			mdb_warn("Read of cms_t at 0x%p failed", cmsctl.cs_cms);
256 			return (0);
257 		}
258 
259 		if (cms.cms_modp != NULL) {
260 			if (mdb_vread(&cmsmodc, sizeof (struct modctl),
261 			    (uintptr_t)cms.cms_modp) != sizeof (cmsmodc)) {
262 				mdb_warn("Read of modctl at 0x%p failed",
263 				    cms.cms_modp);
264 				return (0);
265 			}
266 
267 			if (mdb_readstr(cmsmodnm, sizeof (cmsmodnm),
268 			    (uintptr_t)cmsmodc.mod_modname) == -1) {
269 				mdb_warn("Read of cms module name at 0x%p "
270 				    "failed", cmsmodc.mod_modname);
271 				return (0);
272 			}
273 		}
274 	}
275 
276 	if (mdb_vread(&refcnt, sizeof (uint32_t),
277 	    (uintptr_t)hdl->cmih_refcntp) != sizeof (uint32_t)) {
278 		mdb_warn("Read of reference count for hdl 0x%p failed", hdl);
279 		return (0);
280 	}
281 
282 	if (native) {
283 		cp = mdb_alloc(sizeof (cpu_t), UM_SLEEP);
284 
285 		if (mdb_vread(cp, sizeof (cpu_t),
286 		    (uintptr_t)hdl->cmih_hdlpriv) != sizeof (cpu_t)) {
287 			mdb_free(cp, sizeof (cpu_t));
288 			mdb_warn("Read of cpu_t at 0x%p failed",
289 			    hdl->cmih_hdlpriv);
290 			return (0);
291 		}
292 	}
293 
294 	if (native) {
295 		(void) mdb_snprintf(cpuidstr, sizeof (cpuidstr), "%d",
296 		    cp->cpu_id);
297 	} else {
298 		(void) mdb_snprintf(cpuidstr, sizeof (cpuidstr), "-");
299 	}
300 
301 	(void) mdb_snprintf(hwidstr, sizeof (hwidstr), "%d/%d/%d",
302 	    hdl->cmih_chipid, hdl->cmih_coreid, hdl->cmih_strandid);
303 
304 	mdb_printf("%16lx %3d %3s %8s %3s %2s %-13s %-24s\n", addr,
305 	    refcnt, cpuidstr, hwidstr, hdl->cmih_mstrand ? "M" : "S",
306 	    hdl->cmih_mcops ? "Y" : "N", cmimodnm, cmsmodnm);
307 
308 	if (native)
309 		mdb_free(cp, sizeof (cpu_t));
310 
311 	return (1);
312 }
313 
314 #define	HDRFMT "%-16s %3s %3s %8s %3s %2s %-13s %-24s\n"
315 
316 static int
317 cmihdl(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
318 {
319 	struct cmihdl_cb cb;
320 	cmi_hdl_impl_t *hdl;
321 
322 	/*
323 	 * If an address is given it must be that of a cmi handle.
324 	 * Otherwise if the user has specified -c <cpuid> or
325 	 * -c <chipid/coreid/strandid> we will lookup a matching handle.
326 	 * Otherwise we'll walk and callback to this dcmd.
327 	 */
328 	if (!(flags & DCMD_ADDRSPEC)) {
329 		char *p, *buf;
330 		int len;
331 
332 		if (argc == 0)
333 			return (mdb_walk_dcmd("cmihdl", "cmihdl", argc,
334 			    argv) == 0 ? DCMD_OK : DCMD_ERR);
335 
336 
337 		if (mdb_getopts(argc, argv,
338 		    'c', MDB_OPT_STR, &p,
339 		    NULL) != argc)
340 			return (DCMD_USAGE);
341 
342 		if ((len = strlen(p)) == 0) {
343 			return (DCMD_USAGE);
344 		} else {
345 			buf = mdb_alloc(len + 1, UM_SLEEP);
346 			strcpy(buf, p);
347 		}
348 
349 		cb.mod_cpuid = cb.mod_chipid = cb.mod_coreid =
350 		    cb.mod_strandid = -1;
351 
352 		if ((p = strchr(buf, '/')) == NULL) {
353 			/* Native cpuid */
354 			cb.mod_cpuid = (int)mdb_strtoull(buf);
355 		} else {
356 			/* Comma-separated triplet chip,core,strand. */
357 			char *q = buf;
358 
359 			*p = '\0';
360 			cb.mod_chipid = (int)mdb_strtoull(q);
361 
362 			if ((q = p + 1) >= buf + len ||
363 			    (p = strchr(q, '/')) == NULL) {
364 				mdb_free(buf, len);
365 				return (DCMD_USAGE);
366 			}
367 
368 			*p = '\0';
369 			cb.mod_coreid = (int)mdb_strtoull(q);
370 
371 			if ((q = p + 1) >= buf + len) {
372 				mdb_free(buf, len);
373 				return (DCMD_USAGE);
374 			}
375 
376 			cb.mod_strandid = (int)mdb_strtoull(q);
377 		}
378 
379 		mdb_free(buf, len);
380 
381 		cb.mod_hdladdr = NULL;
382 		if (mdb_walk("cmihdl", cmihdl_cb, &cb) == -1) {
383 			mdb_warn("cmi_hdl walk failed\n");
384 			return (DCMD_ERR);
385 		}
386 
387 		if (cb.mod_hdladdr == NULL) {
388 			if (cb.mod_cpuid != -1) {
389 				mdb_warn("No handle found for cpuid %d\n",
390 				    cb.mod_cpuid);
391 			} else {
392 
393 				mdb_warn("No handle found for chip %d "
394 				    "core %d strand %d\n", cb.mod_chipid,
395 				    cb.mod_coreid, cb.mod_strandid);
396 			}
397 			return (DCMD_ERR);
398 		}
399 
400 		addr = cb.mod_hdladdr;
401 	}
402 
403 	if (DCMD_HDRSPEC(flags)) {
404 		char ul[] = "----------------------------";
405 		char *p = ul + sizeof (ul) - 1;
406 
407 		mdb_printf(HDRFMT HDRFMT,
408 		    "HANDLE", "REF", "CPU", "CH/CR/ST", "CMT", "MC",
409 		    "MODULE", "MODEL-SPECIFIC",
410 		    p - 16,  p - 3, p - 3, p - 8, p - 3, p - 2, p - 13, p - 24);
411 	}
412 
413 	hdl = mdb_alloc(sizeof (cmi_hdl_impl_t), UM_SLEEP);
414 
415 	if (mdb_vread(hdl, sizeof (cmi_hdl_impl_t), addr) !=
416 	    sizeof (cmi_hdl_impl_t)) {
417 		mdb_free(hdl, sizeof (cmi_hdl_impl_t));
418 		mdb_warn("Read of cmi handle at 0x%p failed", addr);
419 		return (DCMD_ERR);
420 	}
421 
422 	if (!cmihdl_disp(addr, hdl)) {
423 		mdb_free(hdl, sizeof (cmi_hdl_impl_t));
424 		return (DCMD_ERR);
425 	}
426 
427 	mdb_free(hdl, sizeof (cmi_hdl_impl_t));
428 
429 	return (DCMD_OK);
430 }
431 
432 /*ARGSUSED*/
433 static int
434 gcpu_mpt_dump(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
435 {
436 	static const char *const whatstrs[] = {
437 		"ntv-cyc-poll",		/* GCPU_MPT_WHAT_CYC_ERR */
438 		"poll-poked",		/* GCPU_MPT_WHAT_POKE_ERR */
439 		"unfaulting",		/* GCPU_MPT_WHAT_UNFAULTING */
440 		"#MC",			/* GCPU_MPT_WHAT_MC_ERR */
441 		"CMCI-int",		/* GCPU_MPT_WHAT_CMCI_ERR */
442 		"xpv-virq-nrec",	/* GCPU_MPT_WHAT_XPV_VIRQ */
443 		"xpv-virq-lgout",	/* GCPU_MPT_WHAT_XPV_VIRQ_LOGOUT */
444 	};
445 
446 	gcpu_poll_trace_t mpt;
447 	const char *what;
448 
449 	if (argc != 0 || !(flags & DCMD_ADDRSPEC))
450 		return (DCMD_USAGE);
451 
452 	if (mdb_vread(&mpt, sizeof (mpt), addr) != sizeof (mpt)) {
453 		mdb_warn("failed to read gcpu_poll_trace_t at 0x%p", addr);
454 		return (DCMD_ERR);
455 	}
456 
457 	if (DCMD_HDRSPEC(flags)) {
458 		mdb_printf("%<u>%?s%</u> %<u>%?s%</u> %<u>%15s%</u> "
459 		    "%<u>%4s%</u>\n", "ADDR", "WHEN", "WHAT", "NERR");
460 	}
461 
462 	if (mpt.mpt_what < sizeof (whatstrs) / sizeof (char *))
463 		what = whatstrs[mpt.mpt_what];
464 	else
465 		what = "???";
466 
467 	mdb_printf("%?p %?p %15s %4u\n", addr, mpt.mpt_when, what,
468 	    mpt.mpt_nerr);
469 
470 	return (DCMD_OK);
471 }
472 
473 typedef struct mptwalk_data {
474 	uintptr_t mw_traceaddr;
475 	gcpu_poll_trace_t *mw_trace;
476 	size_t mw_tracesz;
477 	uint_t mw_tracenent;
478 	uint_t mw_curtrace;
479 } mptwalk_data_t;
480 
481 static int
482 gcpu_mptwalk_init(mdb_walk_state_t *wsp)
483 {
484 	gcpu_poll_trace_t *mpt;
485 	mptwalk_data_t *mw;
486 	GElf_Sym sym;
487 	uint_t nent, i;
488 	hrtime_t latest;
489 
490 	if (wsp->walk_addr == NULL) {
491 		mdb_warn("the address of a poll trace array must be "
492 		    "specified\n");
493 		return (WALK_ERR);
494 	}
495 
496 	if (mdb_lookup_by_name("gcpu_poll_trace_nent", &sym) < 0 ||
497 	    sym.st_size != sizeof (uint_t) || mdb_vread(&nent, sizeof (uint_t),
498 	    sym.st_value) != sizeof (uint_t)) {
499 		mdb_warn("failed to read gcpu_poll_trace_nent from kernel");
500 		return (WALK_ERR);
501 	}
502 
503 	mw = mdb_alloc(sizeof (mptwalk_data_t), UM_SLEEP);
504 	mw->mw_traceaddr = wsp->walk_addr;
505 	mw->mw_tracenent = nent;
506 	mw->mw_tracesz = nent * sizeof (gcpu_poll_trace_t);
507 	mw->mw_trace = mdb_alloc(mw->mw_tracesz, UM_SLEEP);
508 
509 	if (mdb_vread(mw->mw_trace, mw->mw_tracesz, wsp->walk_addr) !=
510 	    mw->mw_tracesz) {
511 		mdb_free(mw->mw_trace, mw->mw_tracesz);
512 		mdb_free(mw, sizeof (mptwalk_data_t));
513 		mdb_warn("failed to read poll trace array from kernel");
514 		return (WALK_ERR);
515 	}
516 
517 	latest = 0;
518 	mw->mw_curtrace = 0;
519 	for (mpt = mw->mw_trace, i = 0; i < mw->mw_tracenent; i++, mpt++) {
520 		if (mpt->mpt_when > latest) {
521 			latest = mpt->mpt_when;
522 			mw->mw_curtrace = i;
523 		}
524 	}
525 
526 	if (latest == 0) {
527 		mdb_free(mw->mw_trace, mw->mw_tracesz);
528 		mdb_free(mw, sizeof (mptwalk_data_t));
529 		return (WALK_DONE); /* trace array is empty */
530 	}
531 
532 	wsp->walk_data = mw;
533 
534 	return (WALK_NEXT);
535 }
536 
537 static int
538 gcpu_mptwalk_step(mdb_walk_state_t *wsp)
539 {
540 	mptwalk_data_t *mw = wsp->walk_data;
541 	gcpu_poll_trace_t *thismpt, *prevmpt;
542 	int prev, rv;
543 
544 	thismpt = &mw->mw_trace[mw->mw_curtrace];
545 
546 	rv = wsp->walk_callback(mw->mw_traceaddr + (mw->mw_curtrace *
547 	    sizeof (gcpu_poll_trace_t)), thismpt, wsp->walk_cbdata);
548 
549 	if (rv != WALK_NEXT)
550 		return (rv);
551 
552 	prev = (mw->mw_curtrace - 1) % mw->mw_tracenent;
553 	prevmpt = &mw->mw_trace[prev];
554 
555 	if (prevmpt->mpt_when == 0 || prevmpt->mpt_when > thismpt->mpt_when)
556 		return (WALK_DONE);
557 
558 	mw->mw_curtrace = prev;
559 
560 	return (WALK_NEXT);
561 }
562 
563 static void
564 gcpu_mptwalk_fini(mdb_walk_state_t *wsp)
565 {
566 	mptwalk_data_t *mw = wsp->walk_data;
567 
568 	mdb_free(mw->mw_trace, mw->mw_tracesz);
569 	mdb_free(mw, sizeof (mptwalk_data_t));
570 }
571 
572 static const mdb_dcmd_t dcmds[] = {
573 	{ "cmihdl", ": -c <cpuid>|<chip,core,strand> ",
574 	    "dump a cmi_handle_t", cmihdl },
575 	{ "gcpu_poll_trace", ":", "dump a poll trace buffer", gcpu_mpt_dump },
576 	{ NULL }
577 };
578 
579 static const mdb_walker_t walkers[] = {
580 	{ "cmihdl", "walks cpu module interface handle list",
581 	    cmih_walk_init, cmih_walk_step, cmih_walk_fini, NULL },
582 	{ "gcpu_poll_trace", "walks poll trace buffers in reverse "
583 	    "chronological order", gcpu_mptwalk_init, gcpu_mptwalk_step,
584 	    gcpu_mptwalk_fini, NULL },
585 	{ NULL }
586 };
587 
588 static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, walkers };
589 
590 const mdb_modinfo_t *
591 _mdb_init(void)
592 {
593 	return (&modinfo);
594 }
595