17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5789d94c2Sjwadams  * Common Development and Distribution License (the "License").
6789d94c2Sjwadams  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22346799e8SJonathan W Adams  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
26d7dba7e5SBryan Cantrill /*
27dfec2ecfSJohn Levon  * Copyright 2018 Joyent, Inc.  All rights reserved.
2828e4da25SMatthew Ahrens  * Copyright (c) 2012 by Delphix. All rights reserved.
29d7dba7e5SBryan Cantrill  */
30d7dba7e5SBryan Cantrill 
317c478bd9Sstevel@tonic-gate #include <mdb/mdb_param.h>
327c478bd9Sstevel@tonic-gate #include <mdb/mdb_modapi.h>
337c478bd9Sstevel@tonic-gate #include <mdb/mdb_ctf.h>
344a1c2431SJonathan Adams #include <mdb/mdb_whatis.h>
357c478bd9Sstevel@tonic-gate #include <sys/cpuvar.h>
367c478bd9Sstevel@tonic-gate #include <sys/kmem_impl.h>
377c478bd9Sstevel@tonic-gate #include <sys/vmem_impl.h>
387c478bd9Sstevel@tonic-gate #include <sys/machelf.h>
397c478bd9Sstevel@tonic-gate #include <sys/modctl.h>
407c478bd9Sstevel@tonic-gate #include <sys/kobj.h>
417c478bd9Sstevel@tonic-gate #include <sys/panic.h>
427c478bd9Sstevel@tonic-gate #include <sys/stack.h>
437c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
447c478bd9Sstevel@tonic-gate #include <vm/page.h>
457c478bd9Sstevel@tonic-gate 
46b5fca8f8Stomee #include "avl.h"
47b5fca8f8Stomee #include "combined.h"
48087e1372Stomee #include "dist.h"
497c478bd9Sstevel@tonic-gate #include "kmem.h"
50b5fca8f8Stomee #include "list.h"
517c478bd9Sstevel@tonic-gate 
527c478bd9Sstevel@tonic-gate #define	dprintf(x) if (mdb_debug_level) { \
537c478bd9Sstevel@tonic-gate 	mdb_printf("kmem debug: ");  \
547c478bd9Sstevel@tonic-gate 	/*CSTYLED*/\
557c478bd9Sstevel@tonic-gate 	mdb_printf x ;\
567c478bd9Sstevel@tonic-gate }
577c478bd9Sstevel@tonic-gate 
587c478bd9Sstevel@tonic-gate #define	KM_ALLOCATED		0x01
597c478bd9Sstevel@tonic-gate #define	KM_FREE			0x02
607c478bd9Sstevel@tonic-gate #define	KM_BUFCTL		0x04
617c478bd9Sstevel@tonic-gate #define	KM_CONSTRUCTED		0x08	/* only constructed free buffers */
627c478bd9Sstevel@tonic-gate #define	KM_HASH			0x10
637c478bd9Sstevel@tonic-gate 
647c478bd9Sstevel@tonic-gate static int mdb_debug_level = 0;
657c478bd9Sstevel@tonic-gate 
667c478bd9Sstevel@tonic-gate /*ARGSUSED*/
677c478bd9Sstevel@tonic-gate static int
kmem_init_walkers(uintptr_t addr,const kmem_cache_t * c,void * ignored)687c478bd9Sstevel@tonic-gate kmem_init_walkers(uintptr_t addr, const kmem_cache_t *c, void *ignored)
697c478bd9Sstevel@tonic-gate {
707c478bd9Sstevel@tonic-gate 	mdb_walker_t w;
717c478bd9Sstevel@tonic-gate 	char descr[64];
727c478bd9Sstevel@tonic-gate 
737c478bd9Sstevel@tonic-gate 	(void) mdb_snprintf(descr, sizeof (descr),
747c478bd9Sstevel@tonic-gate 	    "walk the %s cache", c->cache_name);
757c478bd9Sstevel@tonic-gate 
767c478bd9Sstevel@tonic-gate 	w.walk_name = c->cache_name;
777c478bd9Sstevel@tonic-gate 	w.walk_descr = descr;
787c478bd9Sstevel@tonic-gate 	w.walk_init = kmem_walk_init;
797c478bd9Sstevel@tonic-gate 	w.walk_step = kmem_walk_step;
807c478bd9Sstevel@tonic-gate 	w.walk_fini = kmem_walk_fini;
817c478bd9Sstevel@tonic-gate 	w.walk_init_arg = (void *)addr;
827c478bd9Sstevel@tonic-gate 
837c478bd9Sstevel@tonic-gate 	if (mdb_add_walker(&w) == -1)
847c478bd9Sstevel@tonic-gate 		mdb_warn("failed to add %s walker", c->cache_name);
857c478bd9Sstevel@tonic-gate 
867c478bd9Sstevel@tonic-gate 	return (WALK_NEXT);
877c478bd9Sstevel@tonic-gate }
887c478bd9Sstevel@tonic-gate 
897c478bd9Sstevel@tonic-gate /*ARGSUSED*/
907c478bd9Sstevel@tonic-gate int
kmem_debug(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)917c478bd9Sstevel@tonic-gate kmem_debug(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
927c478bd9Sstevel@tonic-gate {
937c478bd9Sstevel@tonic-gate 	mdb_debug_level ^= 1;
947c478bd9Sstevel@tonic-gate 
957c478bd9Sstevel@tonic-gate 	mdb_printf("kmem: debugging is now %s\n",
967c478bd9Sstevel@tonic-gate 	    mdb_debug_level ? "on" : "off");
977c478bd9Sstevel@tonic-gate 
987c478bd9Sstevel@tonic-gate 	return (DCMD_OK);
997c478bd9Sstevel@tonic-gate }
1007c478bd9Sstevel@tonic-gate 
1017c478bd9Sstevel@tonic-gate int
kmem_cache_walk_init(mdb_walk_state_t * wsp)1027c478bd9Sstevel@tonic-gate kmem_cache_walk_init(mdb_walk_state_t *wsp)
1037c478bd9Sstevel@tonic-gate {
1047c478bd9Sstevel@tonic-gate 	GElf_Sym sym;
1057c478bd9Sstevel@tonic-gate 
106b5fca8f8Stomee 	if (mdb_lookup_by_name("kmem_caches", &sym) == -1) {
107b5fca8f8Stomee 		mdb_warn("couldn't find kmem_caches");
1087c478bd9Sstevel@tonic-gate 		return (WALK_ERR);
1097c478bd9Sstevel@tonic-gate 	}
1107c478bd9Sstevel@tonic-gate 
111b5fca8f8Stomee 	wsp->walk_addr = (uintptr_t)sym.st_value;
1127c478bd9Sstevel@tonic-gate 
113b5fca8f8Stomee 	return (list_walk_init_named(wsp, "cache list", "cache"));
1147c478bd9Sstevel@tonic-gate }
1157c478bd9Sstevel@tonic-gate 
1167c478bd9Sstevel@tonic-gate int
kmem_cpu_cache_walk_init(mdb_walk_state_t * wsp)1177c478bd9Sstevel@tonic-gate kmem_cpu_cache_walk_init(mdb_walk_state_t *wsp)
1187c478bd9Sstevel@tonic-gate {
119892ad162SToomas Soome 	if (wsp->walk_addr == 0) {
1207c478bd9Sstevel@tonic-gate 		mdb_warn("kmem_cpu_cache doesn't support global walks");
1217c478bd9Sstevel@tonic-gate 		return (WALK_ERR);
1227c478bd9Sstevel@tonic-gate 	}
1237c478bd9Sstevel@tonic-gate 
1247c478bd9Sstevel@tonic-gate 	if (mdb_layered_walk("cpu", wsp) == -1) {
1257c478bd9Sstevel@tonic-gate 		mdb_warn("couldn't walk 'cpu'");
1267c478bd9Sstevel@tonic-gate 		return (WALK_ERR);
1277c478bd9Sstevel@tonic-gate 	}
1287c478bd9Sstevel@tonic-gate 
1297c478bd9Sstevel@tonic-gate 	wsp->walk_data = (void *)wsp->walk_addr;
1307c478bd9Sstevel@tonic-gate 
1317c478bd9Sstevel@tonic-gate 	return (WALK_NEXT);
1327c478bd9Sstevel@tonic-gate }
1337c478bd9Sstevel@tonic-gate 
1347c478bd9Sstevel@tonic-gate int
kmem_cpu_cache_walk_step(mdb_walk_state_t * wsp)1357c478bd9Sstevel@tonic-gate kmem_cpu_cache_walk_step(mdb_walk_state_t *wsp)
1367c478bd9Sstevel@tonic-gate {
1377c478bd9Sstevel@tonic-gate 	uintptr_t caddr = (uintptr_t)wsp->walk_data;
1387c478bd9Sstevel@tonic-gate 	const cpu_t *cpu = wsp->walk_layer;
1397c478bd9Sstevel@tonic-gate 	kmem_cpu_cache_t cc;
1407c478bd9Sstevel@tonic-gate 
1411db3a682SMichael Corcoran 	caddr += OFFSETOF(kmem_cache_t, cache_cpu[cpu->cpu_seqid]);
1427c478bd9Sstevel@tonic-gate 
1437c478bd9Sstevel@tonic-gate 	if (mdb_vread(&cc, sizeof (kmem_cpu_cache_t), caddr) == -1) {
1447c478bd9Sstevel@tonic-gate 		mdb_warn("couldn't read kmem_cpu_cache at %p", caddr);
1457c478bd9Sstevel@tonic-gate 		return (WALK_ERR);
1467c478bd9Sstevel@tonic-gate 	}
1477c478bd9Sstevel@tonic-gate 
1487c478bd9Sstevel@tonic-gate 	return (wsp->walk_callback(caddr, &cc, wsp->walk_cbdata));
1497c478bd9Sstevel@tonic-gate }
1507c478bd9Sstevel@tonic-gate 
151b5fca8f8Stomee static int
kmem_slab_check(void * p,uintptr_t saddr,void * arg)152b5fca8f8Stomee kmem_slab_check(void *p, uintptr_t saddr, void *arg)
153b5fca8f8Stomee {
154b5fca8f8Stomee 	kmem_slab_t *sp = p;
155b5fca8f8Stomee 	uintptr_t caddr = (uintptr_t)arg;
156b5fca8f8Stomee 	if ((uintptr_t)sp->slab_cache != caddr) {
157b5fca8f8Stomee 		mdb_warn("slab %p isn't in cache %p (in cache %p)\n",
158b5fca8f8Stomee 		    saddr, caddr, sp->slab_cache);
159b5fca8f8Stomee 		return (-1);
160b5fca8f8Stomee 	}
161b5fca8f8Stomee 
162b5fca8f8Stomee 	return (0);
163b5fca8f8Stomee }
164b5fca8f8Stomee 
165b5fca8f8Stomee static int
kmem_partial_slab_check(void * p,uintptr_t saddr,void * arg)166b5fca8f8Stomee kmem_partial_slab_check(void *p, uintptr_t saddr, void *arg)
167b5fca8f8Stomee {
168b5fca8f8Stomee 	kmem_slab_t *sp = p;
169b5fca8f8Stomee 
170b5fca8f8Stomee 	int rc = kmem_slab_check(p, saddr, arg);
171b5fca8f8Stomee 	if (rc != 0) {
172b5fca8f8Stomee 		return (rc);
173b5fca8f8Stomee 	}
174b5fca8f8Stomee 
175b5fca8f8Stomee 	if (!KMEM_SLAB_IS_PARTIAL(sp)) {
176b5fca8f8Stomee 		mdb_warn("slab %p is not a partial slab\n", saddr);
177b5fca8f8Stomee 		return (-1);
178b5fca8f8Stomee 	}
179b5fca8f8Stomee 
180b5fca8f8Stomee 	return (0);
181b5fca8f8Stomee }
182b5fca8f8Stomee 
183b5fca8f8Stomee static int
kmem_complete_slab_check(void * p,uintptr_t saddr,void * arg)184b5fca8f8Stomee kmem_complete_slab_check(void *p, uintptr_t saddr, void *arg)
185b5fca8f8Stomee {
186b5fca8f8Stomee 	kmem_slab_t *sp = p;
187b5fca8f8Stomee 
188b5fca8f8Stomee 	int rc = kmem_slab_check(p, saddr, arg);
189b5fca8f8Stomee 	if (rc != 0) {
190b5fca8f8Stomee 		return (rc);
191b5fca8f8Stomee 	}
192b5fca8f8Stomee 
193b5fca8f8Stomee 	if (!KMEM_SLAB_IS_ALL_USED(sp)) {
194b5fca8f8Stomee 		mdb_warn("slab %p is not completely allocated\n", saddr);
195b5fca8f8Stomee 		return (-1);
196b5fca8f8Stomee 	}
197b5fca8f8Stomee 
198b5fca8f8Stomee 	return (0);
199b5fca8f8Stomee }
200b5fca8f8Stomee 
201b5fca8f8Stomee typedef struct {
202b5fca8f8Stomee 	uintptr_t kns_cache_addr;
203b5fca8f8Stomee 	int kns_nslabs;
204b5fca8f8Stomee } kmem_nth_slab_t;
205b5fca8f8Stomee 
206b5fca8f8Stomee static int
kmem_nth_slab_check(void * p,uintptr_t saddr,void * arg)207b5fca8f8Stomee kmem_nth_slab_check(void *p, uintptr_t saddr, void *arg)
208b5fca8f8Stomee {
209b5fca8f8Stomee 	kmem_nth_slab_t *chkp = arg;
210b5fca8f8Stomee 
211b5fca8f8Stomee 	int rc = kmem_slab_check(p, saddr, (void *)chkp->kns_cache_addr);
212b5fca8f8Stomee 	if (rc != 0) {
213b5fca8f8Stomee 		return (rc);
214b5fca8f8Stomee 	}
215b5fca8f8Stomee 
216b5fca8f8Stomee 	return (chkp->kns_nslabs-- == 0 ? 1 : 0);
217b5fca8f8Stomee }
218b5fca8f8Stomee 
219b5fca8f8Stomee static int
kmem_complete_slab_walk_init(mdb_walk_state_t * wsp)220b5fca8f8Stomee kmem_complete_slab_walk_init(mdb_walk_state_t *wsp)
221b5fca8f8Stomee {
222b5fca8f8Stomee 	uintptr_t caddr = wsp->walk_addr;
223b5fca8f8Stomee 
224b5fca8f8Stomee 	wsp->walk_addr = (uintptr_t)(caddr +
225b5fca8f8Stomee 	    offsetof(kmem_cache_t, cache_complete_slabs));
226b5fca8f8Stomee 
227b5fca8f8Stomee 	return (list_walk_init_checked(wsp, "slab list", "slab",
228b5fca8f8Stomee 	    kmem_complete_slab_check, (void *)caddr));
229b5fca8f8Stomee }
230b5fca8f8Stomee 
231b5fca8f8Stomee static int
kmem_partial_slab_walk_init(mdb_walk_state_t * wsp)232b5fca8f8Stomee kmem_partial_slab_walk_init(mdb_walk_state_t *wsp)
233b5fca8f8Stomee {
234b5fca8f8Stomee 	uintptr_t caddr = wsp->walk_addr;
235b5fca8f8Stomee 
236b5fca8f8Stomee 	wsp->walk_addr = (uintptr_t)(caddr +
237b5fca8f8Stomee 	    offsetof(kmem_cache_t, cache_partial_slabs));
238b5fca8f8Stomee 
239b5fca8f8Stomee 	return (avl_walk_init_checked(wsp, "slab list", "slab",
240b5fca8f8Stomee 	    kmem_partial_slab_check, (void *)caddr));
241b5fca8f8Stomee }
242b5fca8f8Stomee 
2437c478bd9Sstevel@tonic-gate int
kmem_slab_walk_init(mdb_walk_state_t * wsp)2447c478bd9Sstevel@tonic-gate kmem_slab_walk_init(mdb_walk_state_t *wsp)
2457c478bd9Sstevel@tonic-gate {
2467c478bd9Sstevel@tonic-gate 	uintptr_t caddr = wsp->walk_addr;
2477c478bd9Sstevel@tonic-gate 
248892ad162SToomas Soome 	if (caddr == 0) {
2497c478bd9Sstevel@tonic-gate 		mdb_warn("kmem_slab doesn't support global walks\n");
2507c478bd9Sstevel@tonic-gate 		return (WALK_ERR);
2517c478bd9Sstevel@tonic-gate 	}
2527c478bd9Sstevel@tonic-gate 
253b5fca8f8Stomee 	combined_walk_init(wsp);
254b5fca8f8Stomee 	combined_walk_add(wsp,
255b5fca8f8Stomee 	    kmem_complete_slab_walk_init, list_walk_step, list_walk_fini);
256b5fca8f8Stomee 	combined_walk_add(wsp,
257b5fca8f8Stomee 	    kmem_partial_slab_walk_init, avl_walk_step, avl_walk_fini);
2587c478bd9Sstevel@tonic-gate 
2597c478bd9Sstevel@tonic-gate 	return (WALK_NEXT);
2607c478bd9Sstevel@tonic-gate }
2617c478bd9Sstevel@tonic-gate 
262b5fca8f8Stomee static int
kmem_first_complete_slab_walk_init(mdb_walk_state_t * wsp)263b5fca8f8Stomee kmem_first_complete_slab_walk_init(mdb_walk_state_t *wsp)
264b5fca8f8Stomee {
265b5fca8f8Stomee 	uintptr_t caddr = wsp->walk_addr;
266b5fca8f8Stomee 	kmem_nth_slab_t *chk;
267b5fca8f8Stomee 
268b5fca8f8Stomee 	chk = mdb_alloc(sizeof (kmem_nth_slab_t),
269b5fca8f8Stomee 	    UM_SLEEP | UM_GC);
270b5fca8f8Stomee 	chk->kns_cache_addr = caddr;
271b5fca8f8Stomee 	chk->kns_nslabs = 1;
272b5fca8f8Stomee 	wsp->walk_addr = (uintptr_t)(caddr +
273b5fca8f8Stomee 	    offsetof(kmem_cache_t, cache_complete_slabs));
274b5fca8f8Stomee 
275b5fca8f8Stomee 	return (list_walk_init_checked(wsp, "slab list", "slab",
276b5fca8f8Stomee 	    kmem_nth_slab_check, chk));
277b5fca8f8Stomee }
278b5fca8f8Stomee 
2797c478bd9Sstevel@tonic-gate int
kmem_slab_walk_partial_init(mdb_walk_state_t * wsp)2807c478bd9Sstevel@tonic-gate kmem_slab_walk_partial_init(mdb_walk_state_t *wsp)
2817c478bd9Sstevel@tonic-gate {
2827c478bd9Sstevel@tonic-gate 	uintptr_t caddr = wsp->walk_addr;
2837c478bd9Sstevel@tonic-gate 	kmem_cache_t c;
2847c478bd9Sstevel@tonic-gate 
285892ad162SToomas Soome 	if (caddr == 0) {
2867c478bd9Sstevel@tonic-gate 		mdb_warn("kmem_slab_partial doesn't support global walks\n");
2877c478bd9Sstevel@tonic-gate 		return (WALK_ERR);
2887c478bd9Sstevel@tonic-gate 	}
2897c478bd9Sstevel@tonic-gate 
2907c478bd9Sstevel@tonic-gate 	if (mdb_vread(&c, sizeof (c), caddr) == -1) {
2917c478bd9Sstevel@tonic-gate 		mdb_warn("couldn't read kmem_cache at %p", caddr);
2927c478bd9Sstevel@tonic-gate 		return (WALK_ERR);
2937c478bd9Sstevel@tonic-gate 	}
2947c478bd9Sstevel@tonic-gate 
295b5fca8f8Stomee 	combined_walk_init(wsp);
2967c478bd9Sstevel@tonic-gate 
2977c478bd9Sstevel@tonic-gate 	/*
2987c478bd9Sstevel@tonic-gate 	 * Some consumers (umem_walk_step(), in particular) require at
2997c478bd9Sstevel@tonic-gate 	 * least one callback if there are any buffers in the cache.  So
300b5fca8f8Stomee 	 * if there are *no* partial slabs, report the first full slab, if
3017c478bd9Sstevel@tonic-gate 	 * any.
3027c478bd9Sstevel@tonic-gate 	 *
3037c478bd9Sstevel@tonic-gate 	 * Yes, this is ugly, but it's cleaner than the other possibilities.
3047c478bd9Sstevel@tonic-gate 	 */
305b5fca8f8Stomee 	if (c.cache_partial_slabs.avl_numnodes == 0) {
306b5fca8f8Stomee 		combined_walk_add(wsp, kmem_first_complete_slab_walk_init,
307b5fca8f8Stomee 		    list_walk_step, list_walk_fini);
308b5fca8f8Stomee 	} else {
309b5fca8f8Stomee 		combined_walk_add(wsp, kmem_partial_slab_walk_init,
310b5fca8f8Stomee 		    avl_walk_step, avl_walk_fini);
3117c478bd9Sstevel@tonic-gate 	}
3127c478bd9Sstevel@tonic-gate 
313b5fca8f8Stomee 	return (WALK_NEXT);
3147c478bd9Sstevel@tonic-gate }
3157c478bd9Sstevel@tonic-gate 
3167c478bd9Sstevel@tonic-gate int
kmem_cache(uintptr_t addr,uint_t flags,int ac,const mdb_arg_t * argv)3177c478bd9Sstevel@tonic-gate kmem_cache(uintptr_t addr, uint_t flags, int ac, const mdb_arg_t *argv)
3187c478bd9Sstevel@tonic-gate {
3197c478bd9Sstevel@tonic-gate 	kmem_cache_t c;
320b5fca8f8Stomee 	const char *filter = NULL;
321b5fca8f8Stomee 
322b5fca8f8Stomee 	if (mdb_getopts(ac, argv,
323b5fca8f8Stomee 	    'n', MDB_OPT_STR, &filter,
324b5fca8f8Stomee 	    NULL) != ac) {
325b5fca8f8Stomee 		return (DCMD_USAGE);
326b5fca8f8Stomee 	}
3277c478bd9Sstevel@tonic-gate 
3287c478bd9Sstevel@tonic-gate 	if (!(flags & DCMD_ADDRSPEC)) {
3297c478bd9Sstevel@tonic-gate 		if (mdb_walk_dcmd("kmem_cache", "kmem_cache", ac, argv) == -1) {
3307c478bd9Sstevel@tonic-gate 			mdb_warn("can't walk kmem_cache");
3317c478bd9Sstevel@tonic-gate 			return (DCMD_ERR);
3327c478bd9Sstevel@tonic-gate 		}
3337c478bd9Sstevel@tonic-gate 		return (DCMD_OK);
3347c478bd9Sstevel@tonic-gate 	}
3357c478bd9Sstevel@tonic-gate 
3367c478bd9Sstevel@tonic-gate 	if (DCMD_HDRSPEC(flags))
3377c478bd9Sstevel@tonic-gate 		mdb_printf("%-?s %-25s %4s %6s %8s %8s\n", "ADDR", "NAME",
3387c478bd9Sstevel@tonic-gate 		    "FLAG", "CFLAG", "BUFSIZE", "BUFTOTL");
3397c478bd9Sstevel@tonic-gate 
3407c478bd9Sstevel@tonic-gate 	if (mdb_vread(&c, sizeof (c), addr) == -1) {
3417c478bd9Sstevel@tonic-gate 		mdb_warn("couldn't read kmem_cache at %p", addr);
3427c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
3437c478bd9Sstevel@tonic-gate 	}
3447c478bd9Sstevel@tonic-gate 
345b5fca8f8Stomee 	if ((filter != NULL) && (strstr(c.cache_name, filter) == NULL))
346b5fca8f8Stomee 		return (DCMD_OK);
347b5fca8f8Stomee 
3487c478bd9Sstevel@tonic-gate 	mdb_printf("%0?p %-25s %04x %06x %8ld %8lld\n", addr, c.cache_name,
3497c478bd9Sstevel@tonic-gate 	    c.cache_flags, c.cache_cflags, c.cache_bufsize, c.cache_buftotal);
3507c478bd9Sstevel@tonic-gate 
3517c478bd9Sstevel@tonic-gate 	return (DCMD_OK);
3527c478bd9Sstevel@tonic-gate }
3537c478bd9Sstevel@tonic-gate 
354b5fca8f8Stomee void
kmem_cache_help(void)355b5fca8f8Stomee kmem_cache_help(void)
356b5fca8f8Stomee {
357b5fca8f8Stomee 	mdb_printf("%s", "Print kernel memory caches.\n\n");
358b5fca8f8Stomee 	mdb_dec_indent(2);
359b5fca8f8Stomee 	mdb_printf("%<b>OPTIONS%</b>\n");
360b5fca8f8Stomee 	mdb_inc_indent(2);
361b5fca8f8Stomee 	mdb_printf("%s",
362b5fca8f8Stomee "  -n name\n"
363b5fca8f8Stomee "        name of kmem cache (or matching partial name)\n"
364b5fca8f8Stomee "\n"
365b5fca8f8Stomee "Column\tDescription\n"
366b5fca8f8Stomee "\n"
367b5fca8f8Stomee "ADDR\t\taddress of kmem cache\n"
368b5fca8f8Stomee "NAME\t\tname of kmem cache\n"
369b5fca8f8Stomee "FLAG\t\tvarious cache state flags\n"
370b5fca8f8Stomee "CFLAG\t\tcache creation flags\n"
371b5fca8f8Stomee "BUFSIZE\tobject size in bytes\n"
372b5fca8f8Stomee "BUFTOTL\tcurrent total buffers in cache (allocated and free)\n");
373b5fca8f8Stomee }
3743893cb7fStomee 
3753893cb7fStomee #define	LABEL_WIDTH	11
3763893cb7fStomee static void
kmem_slabs_print_dist(uint_t * ks_bucket,size_t buffers_per_slab,size_t maxbuckets,size_t minbucketsize)3773893cb7fStomee kmem_slabs_print_dist(uint_t *ks_bucket, size_t buffers_per_slab,
3783893cb7fStomee     size_t maxbuckets, size_t minbucketsize)
3793893cb7fStomee {
3803893cb7fStomee 	uint64_t total;
3813893cb7fStomee 	int buckets;
3823893cb7fStomee 	int i;
3833893cb7fStomee 	const int *distarray;
3843893cb7fStomee 	int complete[2];
3853893cb7fStomee 
3863893cb7fStomee 	buckets = buffers_per_slab;
3873893cb7fStomee 
3883893cb7fStomee 	total = 0;
3893893cb7fStomee 	for (i = 0; i <= buffers_per_slab; i++)
3903893cb7fStomee 		total += ks_bucket[i];
3913893cb7fStomee 
3923893cb7fStomee 	if (maxbuckets > 1)
3933893cb7fStomee 		buckets = MIN(buckets, maxbuckets);
3943893cb7fStomee 
3953893cb7fStomee 	if (minbucketsize > 1) {
3963893cb7fStomee 		/*
3973893cb7fStomee 		 * minbucketsize does not apply to the first bucket reserved
3983893cb7fStomee 		 * for completely allocated slabs
3993893cb7fStomee 		 */
4003893cb7fStomee 		buckets = MIN(buckets, 1 + ((buffers_per_slab - 1) /
4013893cb7fStomee 		    minbucketsize));
4023893cb7fStomee 		if ((buckets < 2) && (buffers_per_slab > 1)) {
4033893cb7fStomee 			buckets = 2;
4043893cb7fStomee 			minbucketsize = (buffers_per_slab - 1);
4053893cb7fStomee 		}
4063893cb7fStomee 	}
4073893cb7fStomee 
4083893cb7fStomee 	/*
4093893cb7fStomee 	 * The first printed bucket is reserved for completely allocated slabs.
4103893cb7fStomee 	 * Passing (buckets - 1) excludes that bucket from the generated
4113893cb7fStomee 	 * distribution, since we're handling it as a special case.
4123893cb7fStomee 	 */
4133893cb7fStomee 	complete[0] = buffers_per_slab;
4143893cb7fStomee 	complete[1] = buffers_per_slab + 1;
415087e1372Stomee 	distarray = dist_linear(buckets - 1, 1, buffers_per_slab - 1);
4163893cb7fStomee 
4173893cb7fStomee 	mdb_printf("%*s\n", LABEL_WIDTH, "Allocated");
418087e1372Stomee 	dist_print_header("Buffers", LABEL_WIDTH, "Slabs");
4193893cb7fStomee 
420087e1372Stomee 	dist_print_bucket(complete, 0, ks_bucket, total, LABEL_WIDTH);
4213893cb7fStomee 	/*
4223893cb7fStomee 	 * Print bucket ranges in descending order after the first bucket for
4233893cb7fStomee 	 * completely allocated slabs, so a person can see immediately whether
4243893cb7fStomee 	 * or not there is fragmentation without having to scan possibly
4253893cb7fStomee 	 * multiple screens of output. Starting at (buckets - 2) excludes the
4263893cb7fStomee 	 * extra terminating bucket.
4273893cb7fStomee 	 */
4283893cb7fStomee 	for (i = buckets - 2; i >= 0; i--) {
429087e1372Stomee 		dist_print_bucket(distarray, i, ks_bucket, total, LABEL_WIDTH);
4303893cb7fStomee 	}
4313893cb7fStomee 	mdb_printf("\n");
4323893cb7fStomee }
4333893cb7fStomee #undef LABEL_WIDTH
4343893cb7fStomee 
4353893cb7fStomee /*ARGSUSED*/
4363893cb7fStomee static int
kmem_first_slab(uintptr_t addr,const kmem_slab_t * sp,boolean_t * is_slab)4373893cb7fStomee kmem_first_slab(uintptr_t addr, const kmem_slab_t *sp, boolean_t *is_slab)
4383893cb7fStomee {
4393893cb7fStomee 	*is_slab = B_TRUE;
4403893cb7fStomee 	return (WALK_DONE);
4413893cb7fStomee }
4423893cb7fStomee 
4433893cb7fStomee /*ARGSUSED*/
4443893cb7fStomee static int
kmem_first_partial_slab(uintptr_t addr,const kmem_slab_t * sp,boolean_t * is_slab)4453893cb7fStomee kmem_first_partial_slab(uintptr_t addr, const kmem_slab_t *sp,
4463893cb7fStomee     boolean_t *is_slab)
4473893cb7fStomee {
4483893cb7fStomee 	/*
449b5fca8f8Stomee 	 * The "kmem_partial_slab" walker reports the first full slab if there
4503893cb7fStomee 	 * are no partial slabs (for the sake of consumers that require at least
4513893cb7fStomee 	 * one callback if there are any buffers in the cache).
4523893cb7fStomee 	 */
453b5fca8f8Stomee 	*is_slab = KMEM_SLAB_IS_PARTIAL(sp);
4543893cb7fStomee 	return (WALK_DONE);
4553893cb7fStomee }
4563893cb7fStomee 
457b5fca8f8Stomee typedef struct kmem_slab_usage {
458b5fca8f8Stomee 	int ksu_refcnt;			/* count of allocated buffers on slab */
459b5fca8f8Stomee 	boolean_t ksu_nomove;		/* slab marked non-reclaimable */
460b5fca8f8Stomee } kmem_slab_usage_t;
461b5fca8f8Stomee 
462b5fca8f8Stomee typedef struct kmem_slab_stats {
463b5fca8f8Stomee 	const kmem_cache_t *ks_cp;
464b5fca8f8Stomee 	int ks_slabs;			/* slabs in cache */
465b5fca8f8Stomee 	int ks_partial_slabs;		/* partially allocated slabs in cache */
466b5fca8f8Stomee 	uint64_t ks_unused_buffers;	/* total unused buffers in cache */
467b5fca8f8Stomee 	int ks_max_buffers_per_slab;	/* max buffers per slab */
468b5fca8f8Stomee 	int ks_usage_len;		/* ks_usage array length */
469b5fca8f8Stomee 	kmem_slab_usage_t *ks_usage;	/* partial slab usage */
470b5fca8f8Stomee 	uint_t *ks_bucket;		/* slab usage distribution */
471b5fca8f8Stomee } kmem_slab_stats_t;
472b5fca8f8Stomee 
4733893cb7fStomee /*ARGSUSED*/
4743893cb7fStomee static int
kmem_slablist_stat(uintptr_t addr,const kmem_slab_t * sp,kmem_slab_stats_t * ks)4753893cb7fStomee kmem_slablist_stat(uintptr_t addr, const kmem_slab_t *sp,
4763893cb7fStomee     kmem_slab_stats_t *ks)
4773893cb7fStomee {
4783893cb7fStomee 	kmem_slab_usage_t *ksu;
4793893cb7fStomee 	long unused;
4803893cb7fStomee 
4813893cb7fStomee 	ks->ks_slabs++;
4823893cb7fStomee 	ks->ks_bucket[sp->slab_refcnt]++;
4833893cb7fStomee 
4843893cb7fStomee 	unused = (sp->slab_chunks - sp->slab_refcnt);
4853893cb7fStomee 	if (unused == 0) {
4863893cb7fStomee 		return (WALK_NEXT);
4873893cb7fStomee 	}
4883893cb7fStomee 
4893893cb7fStomee 	ks->ks_partial_slabs++;
4903893cb7fStomee 	ks->ks_unused_buffers += unused;
4913893cb7fStomee 
4923893cb7fStomee 	if (ks->ks_partial_slabs > ks->ks_usage_len) {
4933893cb7fStomee 		kmem_slab_usage_t *usage;
4943893cb7fStomee 		int len = ks->ks_usage_len;
4953893cb7fStomee 
4963893cb7fStomee 		len = (len == 0 ? 16 : len * 2);
4973893cb7fStomee 		usage = mdb_zalloc(len * sizeof (kmem_slab_usage_t), UM_SLEEP);
4983893cb7fStomee 		if (ks->ks_usage != NULL) {
4993893cb7fStomee 			bcopy(ks->ks_usage, usage,
5003893cb7fStomee 			    ks->ks_usage_len * sizeof (kmem_slab_usage_t));
5013893cb7fStomee 			mdb_free(ks->ks_usage,
5023893cb7fStomee 			    ks->ks_usage_len * sizeof (kmem_slab_usage_t));
5033893cb7fStomee 		}
5043893cb7fStomee 		ks->ks_usage = usage;
5053893cb7fStomee 		ks->ks_usage_len = len;
5063893cb7fStomee 	}
5073893cb7fStomee 
5083893cb7fStomee 	ksu = &ks->ks_usage[ks->ks_partial_slabs - 1];
5093893cb7fStomee 	ksu->ksu_refcnt = sp->slab_refcnt;
510b5fca8f8Stomee 	ksu->ksu_nomove = (sp->slab_flags & KMEM_SLAB_NOMOVE);
5113893cb7fStomee 	return (WALK_NEXT);
5123893cb7fStomee }
5133893cb7fStomee 
5143893cb7fStomee static void
kmem_slabs_header()5153893cb7fStomee kmem_slabs_header()
5163893cb7fStomee {
5173893cb7fStomee 	mdb_printf("%-25s %8s %8s %9s %9s %6s\n",
5183893cb7fStomee 	    "", "", "Partial", "", "Unused", "");
5193893cb7fStomee 	mdb_printf("%-25s %8s %8s %9s %9s %6s\n",
5203893cb7fStomee 	    "Cache Name", "Slabs", "Slabs", "Buffers", "Buffers", "Waste");
5213893cb7fStomee 	mdb_printf("%-25s %8s %8s %9s %9s %6s\n",
5223893cb7fStomee 	    "-------------------------", "--------", "--------", "---------",
5233893cb7fStomee 	    "---------", "------");
5243893cb7fStomee }
5253893cb7fStomee 
5263893cb7fStomee int
kmem_slabs(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)5273893cb7fStomee kmem_slabs(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
5283893cb7fStomee {
5293893cb7fStomee 	kmem_cache_t c;
5303893cb7fStomee 	kmem_slab_stats_t stats;
5313893cb7fStomee 	mdb_walk_cb_t cb;
5323893cb7fStomee 	int pct;
5333893cb7fStomee 	int tenths_pct;
5343893cb7fStomee 	size_t maxbuckets = 1;
5353893cb7fStomee 	size_t minbucketsize = 0;
5363893cb7fStomee 	const char *filter = NULL;
537b5fca8f8Stomee 	const char *name = NULL;
5383893cb7fStomee 	uint_t opt_v = FALSE;
539b5fca8f8Stomee 	boolean_t buckets = B_FALSE;
5403893cb7fStomee 	boolean_t skip = B_FALSE;
5413893cb7fStomee 
5423893cb7fStomee 	if (mdb_getopts(argc, argv,
5433893cb7fStomee 	    'B', MDB_OPT_UINTPTR, &minbucketsize,
5443893cb7fStomee 	    'b', MDB_OPT_UINTPTR, &maxbuckets,
5453893cb7fStomee 	    'n', MDB_OPT_STR, &filter,
546b5fca8f8Stomee 	    'N', MDB_OPT_STR, &name,
5473893cb7fStomee 	    'v', MDB_OPT_SETBITS, TRUE, &opt_v,
5483893cb7fStomee 	    NULL) != argc) {
5493893cb7fStomee 		return (DCMD_USAGE);
5503893cb7fStomee 	}
5513893cb7fStomee 
552b5fca8f8Stomee 	if ((maxbuckets != 1) || (minbucketsize != 0)) {
553b5fca8f8Stomee 		buckets = B_TRUE;
5543893cb7fStomee 	}
5553893cb7fStomee 
5563893cb7fStomee 	if (!(flags & DCMD_ADDRSPEC)) {
5573893cb7fStomee 		if (mdb_walk_dcmd("kmem_cache", "kmem_slabs", argc,
5583893cb7fStomee 		    argv) == -1) {
5593893cb7fStomee 			mdb_warn("can't walk kmem_cache");
5603893cb7fStomee 			return (DCMD_ERR);
5613893cb7fStomee 		}
5623893cb7fStomee 		return (DCMD_OK);
5633893cb7fStomee 	}
5643893cb7fStomee 
5653893cb7fStomee 	if (mdb_vread(&c, sizeof (c), addr) == -1) {
5663893cb7fStomee 		mdb_warn("couldn't read kmem_cache at %p", addr);
5673893cb7fStomee 		return (DCMD_ERR);
5683893cb7fStomee 	}
5693893cb7fStomee 
570b5fca8f8Stomee 	if (name == NULL) {
571b5fca8f8Stomee 		skip = ((filter != NULL) &&
572b5fca8f8Stomee 		    (strstr(c.cache_name, filter) == NULL));
573b5fca8f8Stomee 	} else if (filter == NULL) {
574b5fca8f8Stomee 		skip = (strcmp(c.cache_name, name) != 0);
575b5fca8f8Stomee 	} else {
576b5fca8f8Stomee 		/* match either -n or -N */
577b5fca8f8Stomee 		skip = ((strcmp(c.cache_name, name) != 0) &&
578b5fca8f8Stomee 		    (strstr(c.cache_name, filter) == NULL));
5793893cb7fStomee 	}
5803893cb7fStomee 
581b5fca8f8Stomee 	if (!(opt_v || buckets) && DCMD_HDRSPEC(flags)) {
5823893cb7fStomee 		kmem_slabs_header();
583b5fca8f8Stomee 	} else if ((opt_v || buckets) && !skip) {
5843893cb7fStomee 		if (DCMD_HDRSPEC(flags)) {
5853893cb7fStomee 			kmem_slabs_header();
5863893cb7fStomee 		} else {
5873893cb7fStomee 			boolean_t is_slab = B_FALSE;
5883893cb7fStomee 			const char *walker_name;
5893893cb7fStomee 			if (opt_v) {
5903893cb7fStomee 				cb = (mdb_walk_cb_t)kmem_first_partial_slab;
5913893cb7fStomee 				walker_name = "kmem_slab_partial";
5923893cb7fStomee 			} else {
5933893cb7fStomee 				cb = (mdb_walk_cb_t)kmem_first_slab;
5943893cb7fStomee 				walker_name = "kmem_slab";
5953893cb7fStomee 			}
5963893cb7fStomee 			(void) mdb_pwalk(walker_name, cb, &is_slab, addr);
5973893cb7fStomee 			if (is_slab) {
5983893cb7fStomee 				kmem_slabs_header();
5993893cb7fStomee 			}
6003893cb7fStomee 		}
6013893cb7fStomee 	}
6023893cb7fStomee 
6033893cb7fStomee 	if (skip) {
6043893cb7fStomee 		return (DCMD_OK);
6053893cb7fStomee 	}
6063893cb7fStomee 
6073893cb7fStomee 	bzero(&stats, sizeof (kmem_slab_stats_t));
608b5fca8f8Stomee 	stats.ks_cp = &c;
609b5fca8f8Stomee 	stats.ks_max_buffers_per_slab = c.cache_maxchunks;
610b5fca8f8Stomee 	/* +1 to include a zero bucket */
611b5fca8f8Stomee 	stats.ks_bucket = mdb_zalloc((stats.ks_max_buffers_per_slab + 1) *
612b5fca8f8Stomee 	    sizeof (*stats.ks_bucket), UM_SLEEP);
6133893cb7fStomee 	cb = (mdb_walk_cb_t)kmem_slablist_stat;
6143893cb7fStomee 	(void) mdb_pwalk("kmem_slab", cb, &stats, addr);
6153893cb7fStomee 
6163893cb7fStomee 	if (c.cache_buftotal == 0) {
6173893cb7fStomee 		pct = 0;
6183893cb7fStomee 		tenths_pct = 0;
6193893cb7fStomee 	} else {
6203893cb7fStomee 		uint64_t n = stats.ks_unused_buffers * 10000;
6213893cb7fStomee 		pct = (int)(n / c.cache_buftotal);
6223893cb7fStomee 		tenths_pct = pct - ((pct / 100) * 100);
6233893cb7fStomee 		tenths_pct = (tenths_pct + 5) / 10; /* round nearest tenth */
6243893cb7fStomee 		if (tenths_pct == 10) {
6253893cb7fStomee 			pct += 100;
6263893cb7fStomee 			tenths_pct = 0;
6273893cb7fStomee 		}
6283893cb7fStomee 	}
6293893cb7fStomee 
6303893cb7fStomee 	pct /= 100;
6313893cb7fStomee 	mdb_printf("%-25s %8d %8d %9lld %9lld %3d.%1d%%\n", c.cache_name,
6323893cb7fStomee 	    stats.ks_slabs, stats.ks_partial_slabs, c.cache_buftotal,
6333893cb7fStomee 	    stats.ks_unused_buffers, pct, tenths_pct);
6343893cb7fStomee 
6353893cb7fStomee 	if (maxbuckets == 0) {
636b5fca8f8Stomee 		maxbuckets = stats.ks_max_buffers_per_slab;
6373893cb7fStomee 	}
6383893cb7fStomee 
6393893cb7fStomee 	if (((maxbuckets > 1) || (minbucketsize > 0)) &&
6403893cb7fStomee 	    (stats.ks_slabs > 0)) {
6413893cb7fStomee 		mdb_printf("\n");
6423893cb7fStomee 		kmem_slabs_print_dist(stats.ks_bucket,
643b5fca8f8Stomee 		    stats.ks_max_buffers_per_slab, maxbuckets, minbucketsize);
644b5fca8f8Stomee 	}
645b5fca8f8Stomee 
646b5fca8f8Stomee 	mdb_free(stats.ks_bucket, (stats.ks_max_buffers_per_slab + 1) *
647b5fca8f8Stomee 	    sizeof (*stats.ks_bucket));
648b5fca8f8Stomee 
649b5fca8f8Stomee 	if (!opt_v) {
650b5fca8f8Stomee 		return (DCMD_OK);
6513893cb7fStomee 	}
6523893cb7fStomee 
6533893cb7fStomee 	if (opt_v && (stats.ks_partial_slabs > 0)) {
6543893cb7fStomee 		int i;
6553893cb7fStomee 		kmem_slab_usage_t *ksu;
6563893cb7fStomee 
657686031edSTom Erickson 		mdb_printf("  %d complete (%d), %d partial:",
6583893cb7fStomee 		    (stats.ks_slabs - stats.ks_partial_slabs),
659686031edSTom Erickson 		    stats.ks_max_buffers_per_slab,
6603893cb7fStomee 		    stats.ks_partial_slabs);
661686031edSTom Erickson 
6623893cb7fStomee 		for (i = 0; i < stats.ks_partial_slabs; i++) {
6633893cb7fStomee 			ksu = &stats.ks_usage[i];
664686031edSTom Erickson 			mdb_printf(" %d%s", ksu->ksu_refcnt,
665686031edSTom Erickson 			    (ksu->ksu_nomove ? "*" : ""));
6663893cb7fStomee 		}
6673893cb7fStomee 		mdb_printf("\n\n");
6683893cb7fStomee 	}
6693893cb7fStomee 
6703893cb7fStomee 	if (stats.ks_usage_len > 0) {
6713893cb7fStomee 		mdb_free(stats.ks_usage,
6723893cb7fStomee 		    stats.ks_usage_len * sizeof (kmem_slab_usage_t));
6733893cb7fStomee 	}
6743893cb7fStomee 
6753893cb7fStomee 	return (DCMD_OK);
6763893cb7fStomee }
6773893cb7fStomee 
6783893cb7fStomee void
kmem_slabs_help(void)6793893cb7fStomee kmem_slabs_help(void)
6803893cb7fStomee {
681b5fca8f8Stomee 	mdb_printf("%s",
682b5fca8f8Stomee "Display slab usage per kmem cache.\n\n");
6833893cb7fStomee 	mdb_dec_indent(2);
6843893cb7fStomee 	mdb_printf("%<b>OPTIONS%</b>\n");
6853893cb7fStomee 	mdb_inc_indent(2);
6863893cb7fStomee 	mdb_printf("%s",
6873893cb7fStomee "  -n name\n"
6883893cb7fStomee "        name of kmem cache (or matching partial name)\n"
689b5fca8f8Stomee "  -N name\n"
690b5fca8f8Stomee "        exact name of kmem cache\n"
6913893cb7fStomee "  -b maxbins\n"
6923893cb7fStomee "        Print a distribution of allocated buffers per slab using at\n"
6933893cb7fStomee "        most maxbins bins. The first bin is reserved for completely\n"
6943893cb7fStomee "        allocated slabs. Setting maxbins to zero (-b 0) has the same\n"
6953893cb7fStomee "        effect as specifying the maximum allocated buffers per slab\n"
6963893cb7fStomee "        or setting minbinsize to 1 (-B 1).\n"
6973893cb7fStomee "  -B minbinsize\n"
6983893cb7fStomee "        Print a distribution of allocated buffers per slab, making\n"
6993893cb7fStomee "        all bins (except the first, reserved for completely allocated\n"
7003893cb7fStomee "        slabs) at least minbinsize buffers apart.\n"
7013893cb7fStomee "  -v    verbose output: List the allocated buffer count of each partial\n"
7023893cb7fStomee "        slab on the free list in order from front to back to show how\n"
7033893cb7fStomee "        closely the slabs are ordered by usage. For example\n"
7043893cb7fStomee "\n"
7053893cb7fStomee "          10 complete, 3 partial (8): 7 3 1\n"
7063893cb7fStomee "\n"
7073893cb7fStomee "        means there are thirteen slabs with eight buffers each, including\n"
7083893cb7fStomee "        three partially allocated slabs with less than all eight buffers\n"
7093893cb7fStomee "        allocated.\n"
7103893cb7fStomee "\n"
7113893cb7fStomee "        Buffer allocations are always from the front of the partial slab\n"
7123893cb7fStomee "        list. When a buffer is freed from a completely used slab, that\n"
7133893cb7fStomee "        slab is added to the front of the partial slab list. Assuming\n"
7143893cb7fStomee "        that all buffers are equally likely to be freed soon, the\n"
7153893cb7fStomee "        desired order of partial slabs is most-used at the front of the\n"
7163893cb7fStomee "        list and least-used at the back (as in the example above).\n"
7173893cb7fStomee "        However, if a slab contains an allocated buffer that will not\n"
7183893cb7fStomee "        soon be freed, it would be better for that slab to be at the\n"
719b5fca8f8Stomee "        front where all of its buffers can be allocated. Taking a slab\n"
720b5fca8f8Stomee "        off the partial slab list (either with all buffers freed or all\n"
721b5fca8f8Stomee "        buffers allocated) reduces cache fragmentation.\n"
722b5fca8f8Stomee "\n"
723b5fca8f8Stomee "        A slab's allocated buffer count representing a partial slab (9 in\n"
724b5fca8f8Stomee "        the example below) may be marked as follows:\n"
725b5fca8f8Stomee "\n"
726b5fca8f8Stomee "        9*   An asterisk indicates that kmem has marked the slab non-\n"
727b5fca8f8Stomee "        reclaimable because the kmem client refused to move one of the\n"
728b5fca8f8Stomee "        slab's buffers. Since kmem does not expect to completely free the\n"
729b5fca8f8Stomee "        slab, it moves it to the front of the list in the hope of\n"
730b5fca8f8Stomee "        completely allocating it instead. A slab marked with an asterisk\n"
731b5fca8f8Stomee "        stays marked for as long as it remains on the partial slab list.\n"
7323893cb7fStomee "\n"
7333893cb7fStomee "Column\t\tDescription\n"
7343893cb7fStomee "\n"
7353893cb7fStomee "Cache Name\t\tname of kmem cache\n"
7363893cb7fStomee "Slabs\t\t\ttotal slab count\n"
7373893cb7fStomee "Partial Slabs\t\tcount of partially allocated slabs on the free list\n"
7383893cb7fStomee "Buffers\t\ttotal buffer count (Slabs * (buffers per slab))\n"
7393893cb7fStomee "Unused Buffers\tcount of unallocated buffers across all partial slabs\n"
7403893cb7fStomee "Waste\t\t\t(Unused Buffers / Buffers) does not include space\n"
7413893cb7fStomee "\t\t\t  for accounting structures (debug mode), slab\n"
7423893cb7fStomee "\t\t\t  coloring (incremental small offsets to stagger\n"
7433893cb7fStomee "\t\t\t  buffer alignment), or the per-CPU magazine layer\n");
7443893cb7fStomee }
7453893cb7fStomee 
7467c478bd9Sstevel@tonic-gate static int
addrcmp(const void * lhs,const void * rhs)7477c478bd9Sstevel@tonic-gate addrcmp(const void *lhs, const void *rhs)
7487c478bd9Sstevel@tonic-gate {
7497c478bd9Sstevel@tonic-gate 	uintptr_t p1 = *((uintptr_t *)lhs);
7507c478bd9Sstevel@tonic-gate 	uintptr_t p2 = *((uintptr_t *)rhs);
7517c478bd9Sstevel@tonic-gate 
7527c478bd9Sstevel@tonic-gate 	if (p1 < p2)
7537c478bd9Sstevel@tonic-gate 		return (-1);
7547c478bd9Sstevel@tonic-gate 	if (p1 > p2)
7557c478bd9Sstevel@tonic-gate 		return (1);
7567c478bd9Sstevel@tonic-gate 	return (0);
7577c478bd9Sstevel@tonic-gate }
7587c478bd9Sstevel@tonic-gate 
7597c478bd9Sstevel@tonic-gate static int
bufctlcmp(const kmem_bufctl_audit_t ** lhs,const kmem_bufctl_audit_t ** rhs)7607c478bd9Sstevel@tonic-gate bufctlcmp(const kmem_bufctl_audit_t **lhs, const kmem_bufctl_audit_t **rhs)
7617c478bd9Sstevel@tonic-gate {
7627c478bd9Sstevel@tonic-gate 	const kmem_bufctl_audit_t *bcp1 = *lhs;
7637c478bd9Sstevel@tonic-gate 	const kmem_bufctl_audit_t *bcp2 = *rhs;
7647c478bd9Sstevel@tonic-gate 
7657c478bd9Sstevel@tonic-gate 	if (bcp1->bc_timestamp > bcp2->bc_timestamp)
7667c478bd9Sstevel@tonic-gate 		return (-1);
7677c478bd9Sstevel@tonic-gate 
7687c478bd9Sstevel@tonic-gate 	if (bcp1->bc_timestamp < bcp2->bc_timestamp)
7697c478bd9Sstevel@tonic-gate 		return (1);
7707c478bd9Sstevel@tonic-gate 
7717c478bd9Sstevel@tonic-gate 	return (0);
7727c478bd9Sstevel@tonic-gate }
7737c478bd9Sstevel@tonic-gate 
7747c478bd9Sstevel@tonic-gate typedef struct kmem_hash_walk {
7757c478bd9Sstevel@tonic-gate 	uintptr_t *kmhw_table;
7767c478bd9Sstevel@tonic-gate 	size_t kmhw_nelems;
7777c478bd9Sstevel@tonic-gate 	size_t kmhw_pos;
7787c478bd9Sstevel@tonic-gate 	kmem_bufctl_t kmhw_cur;
7797c478bd9Sstevel@tonic-gate } kmem_hash_walk_t;
7807c478bd9Sstevel@tonic-gate 
7817c478bd9Sstevel@tonic-gate int
kmem_hash_walk_init(mdb_walk_state_t * wsp)7827c478bd9Sstevel@tonic-gate kmem_hash_walk_init(mdb_walk_state_t *wsp)
7837c478bd9Sstevel@tonic-gate {
7847c478bd9Sstevel@tonic-gate 	kmem_hash_walk_t *kmhw;
7857c478bd9Sstevel@tonic-gate 	uintptr_t *hash;
7867c478bd9Sstevel@tonic-gate 	kmem_cache_t c;
7877c478bd9Sstevel@tonic-gate 	uintptr_t haddr, addr = wsp->walk_addr;
7887c478bd9Sstevel@tonic-gate 	size_t nelems;
7897c478bd9Sstevel@tonic-gate 	size_t hsize;
7907c478bd9Sstevel@tonic-gate 
791892ad162SToomas Soome 	if (addr == 0) {
7927c478bd9Sstevel@tonic-gate 		mdb_warn("kmem_hash doesn't support global walks\n");
7937c478bd9Sstevel@tonic-gate 		return (WALK_ERR);
7947c478bd9Sstevel@tonic-gate 	}
7957c478bd9Sstevel@tonic-gate 
7967c478bd9Sstevel@tonic-gate 	if (mdb_vread(&c, sizeof (c), addr) == -1) {
7977c478bd9Sstevel@tonic-gate 		mdb_warn("couldn't read cache at addr %p", addr);
7987c478bd9Sstevel@tonic-gate 		return (WALK_ERR);
7997c478bd9Sstevel@tonic-gate 	}
8007c478bd9Sstevel@tonic-gate 
8017c478bd9Sstevel@tonic-gate 	if (!(c.cache_flags & KMF_HASH)) {
8027c478bd9Sstevel@tonic-gate 		mdb_warn("cache %p doesn't have a hash table\n", addr);
8037c478bd9Sstevel@tonic-gate 		return (WALK_DONE);		/* nothing to do */
8047c478bd9Sstevel@tonic-gate 	}
8057c478bd9Sstevel@tonic-gate 
8067c478bd9Sstevel@tonic-gate 	kmhw = mdb_zalloc(sizeof (kmem_hash_walk_t), UM_SLEEP);
8077c478bd9Sstevel@tonic-gate 	kmhw->kmhw_cur.bc_next = NULL;
8087c478bd9Sstevel@tonic-gate 	kmhw->kmhw_pos = 0;
8097c478bd9Sstevel@tonic-gate 
8107c478bd9Sstevel@tonic-gate 	kmhw->kmhw_nelems = nelems = c.cache_hash_mask + 1;
8117c478bd9Sstevel@tonic-gate 	hsize = nelems * sizeof (uintptr_t);
8127c478bd9Sstevel@tonic-gate 	haddr = (uintptr_t)c.cache_hash_table;
8137c478bd9Sstevel@tonic-gate 
8147c478bd9Sstevel@tonic-gate 	kmhw->kmhw_table = hash = mdb_alloc(hsize, UM_SLEEP);
8157c478bd9Sstevel@tonic-gate 	if (mdb_vread(hash, hsize, haddr) == -1) {
8167c478bd9Sstevel@tonic-gate 		mdb_warn("failed to read hash table at %p", haddr);
8177c478bd9Sstevel@tonic-gate 		mdb_free(hash, hsize);
8187c478bd9Sstevel@tonic-gate 		mdb_free(kmhw, sizeof (kmem_hash_walk_t));
8197c478bd9Sstevel@tonic-gate 		return (WALK_ERR);
8207c478bd9Sstevel@tonic-gate 	}
8217c478bd9Sstevel@tonic-gate 
8227c478bd9Sstevel@tonic-gate 	wsp->walk_data = kmhw;
8237c478bd9Sstevel@tonic-gate 
8247c478bd9Sstevel@tonic-gate 	return (WALK_NEXT);
8257c478bd9Sstevel@tonic-gate }
8267c478bd9Sstevel@tonic-gate 
8277c478bd9Sstevel@tonic-gate int
kmem_hash_walk_step(mdb_walk_state_t * wsp)8287c478bd9Sstevel@tonic-gate kmem_hash_walk_step(mdb_walk_state_t *wsp)
8297c478bd9Sstevel@tonic-gate {
8307c478bd9Sstevel@tonic-gate 	kmem_hash_walk_t *kmhw = wsp->walk_data;
831892ad162SToomas Soome 	uintptr_t addr = 0;
8327c478bd9Sstevel@tonic-gate 
833892ad162SToomas Soome 	if ((addr = (uintptr_t)kmhw->kmhw_cur.bc_next) == 0) {
8347c478bd9Sstevel@tonic-gate 		while (kmhw->kmhw_pos < kmhw->kmhw_nelems) {
835892ad162SToomas Soome 			if ((addr = kmhw->kmhw_table[kmhw->kmhw_pos++]) != 0)
8367c478bd9Sstevel@tonic-gate 				break;
8377c478bd9Sstevel@tonic-gate 		}
8387c478bd9Sstevel@tonic-gate 	}
839892ad162SToomas Soome 	if (addr == 0)
8407c478bd9Sstevel@tonic-gate 		return (WALK_DONE);
8417c478bd9Sstevel@tonic-gate 
8427c478bd9Sstevel@tonic-gate 	if (mdb_vread(&kmhw->kmhw_cur, sizeof (kmem_bufctl_t), addr) == -1) {
8437c478bd9Sstevel@tonic-gate 		mdb_warn("couldn't read kmem_bufctl_t at addr %p", addr);
8447c478bd9Sstevel@tonic-gate 		return (WALK_ERR);
8457c478bd9Sstevel@tonic-gate 	}
8467c478bd9Sstevel@tonic-gate 
8477c478bd9Sstevel@tonic-gate 	return (wsp->walk_callback(addr, &kmhw->kmhw_cur, wsp->walk_cbdata));
8487c478bd9Sstevel@tonic-gate }
8497c478bd9Sstevel@tonic-gate 
8507c478bd9Sstevel@tonic-gate void
kmem_hash_walk_fini(mdb_walk_state_t * wsp)8517c478bd9Sstevel@tonic-gate kmem_hash_walk_fini(mdb_walk_state_t *wsp)
8527c478bd9Sstevel@tonic-gate {
8537c478bd9Sstevel@tonic-gate 	kmem_hash_walk_t *kmhw = wsp->walk_data;
8547c478bd9Sstevel@tonic-gate 
8557c478bd9Sstevel@tonic-gate 	if (kmhw == NULL)
8567c478bd9Sstevel@tonic-gate 		return;
8577c478bd9Sstevel@tonic-gate 
8587c478bd9Sstevel@tonic-gate 	mdb_free(kmhw->kmhw_table, kmhw->kmhw_nelems * sizeof (uintptr_t));
8597c478bd9Sstevel@tonic-gate 	mdb_free(kmhw, sizeof (kmem_hash_walk_t));
8607c478bd9Sstevel@tonic-gate }
8617c478bd9Sstevel@tonic-gate 
8627c478bd9Sstevel@tonic-gate /*
8637c478bd9Sstevel@tonic-gate  * Find the address of the bufctl structure for the address 'buf' in cache
8647c478bd9Sstevel@tonic-gate  * 'cp', which is at address caddr, and place it in *out.
8657c478bd9Sstevel@tonic-gate  */
8667c478bd9Sstevel@tonic-gate static int
kmem_hash_lookup(kmem_cache_t * cp,uintptr_t caddr,void * buf,uintptr_t * out)8677c478bd9Sstevel@tonic-gate kmem_hash_lookup(kmem_cache_t *cp, uintptr_t caddr, void *buf, uintptr_t *out)
8687c478bd9Sstevel@tonic-gate {
8697c478bd9Sstevel@tonic-gate 	uintptr_t bucket = (uintptr_t)KMEM_HASH(cp, buf);
8707c478bd9Sstevel@tonic-gate 	kmem_bufctl_t *bcp;
8717c478bd9Sstevel@tonic-gate 	kmem_bufctl_t bc;
8727c478bd9Sstevel@tonic-gate 
8737c478bd9Sstevel@tonic-gate 	if (mdb_vread(&bcp, sizeof (kmem_bufctl_t *), bucket) == -1) {
8747c478bd9Sstevel@tonic-gate 		mdb_warn("unable to read hash bucket for %p in cache %p",
8757c478bd9Sstevel@tonic-gate 		    buf, caddr);
8767c478bd9Sstevel@tonic-gate 		return (-1);
8777c478bd9Sstevel@tonic-gate 	}
8787c478bd9Sstevel@tonic-gate 
8797c478bd9Sstevel@tonic-gate 	while (bcp != NULL) {
8807c478bd9Sstevel@tonic-gate 		if (mdb_vread(&bc, sizeof (kmem_bufctl_t),
8817c478bd9Sstevel@tonic-gate 		    (uintptr_t)bcp) == -1) {
8827c478bd9Sstevel@tonic-gate 			mdb_warn("unable to read bufctl at %p", bcp);
8837c478bd9Sstevel@tonic-gate 			return (-1);
8847c478bd9Sstevel@tonic-gate 		}
8857c478bd9Sstevel@tonic-gate 		if (bc.bc_addr == buf) {
8867c478bd9Sstevel@tonic-gate 			*out = (uintptr_t)bcp;
8877c478bd9Sstevel@tonic-gate 			return (0);
8887c478bd9Sstevel@tonic-gate 		}
8897c478bd9Sstevel@tonic-gate 		bcp = bc.bc_next;
8907c478bd9Sstevel@tonic-gate 	}
8917c478bd9Sstevel@tonic-gate 
8927c478bd9Sstevel@tonic-gate 	mdb_warn("unable to find bufctl for %p in cache %p\n", buf, caddr);
8937c478bd9Sstevel@tonic-gate 	return (-1);
8947c478bd9Sstevel@tonic-gate }
8957c478bd9Sstevel@tonic-gate 
8967c478bd9Sstevel@tonic-gate int
kmem_get_magsize(const kmem_cache_t * cp)8977c478bd9Sstevel@tonic-gate kmem_get_magsize(const kmem_cache_t *cp)
8987c478bd9Sstevel@tonic-gate {
8997c478bd9Sstevel@tonic-gate 	uintptr_t addr = (uintptr_t)cp->cache_magtype;
9007c478bd9Sstevel@tonic-gate 	GElf_Sym mt_sym;
9017c478bd9Sstevel@tonic-gate 	kmem_magtype_t mt;
9027c478bd9Sstevel@tonic-gate 	int res;
9037c478bd9Sstevel@tonic-gate 
9047c478bd9Sstevel@tonic-gate 	/*
9057c478bd9Sstevel@tonic-gate 	 * if cpu 0 has a non-zero magsize, it must be correct.  caches
9067c478bd9Sstevel@tonic-gate 	 * with KMF_NOMAGAZINE have disabled their magazine layers, so
9077c478bd9Sstevel@tonic-gate 	 * it is okay to return 0 for them.
9087c478bd9Sstevel@tonic-gate 	 */
9097c478bd9Sstevel@tonic-gate 	if ((res = cp->cache_cpu[0].cc_magsize) != 0 ||
9107c478bd9Sstevel@tonic-gate 	    (cp->cache_flags & KMF_NOMAGAZINE))
9117c478bd9Sstevel@tonic-gate 		return (res);
9127c478bd9Sstevel@tonic-gate 
9137c478bd9Sstevel@tonic-gate 	if (mdb_lookup_by_name("kmem_magtype", &mt_sym) == -1) {
9147c478bd9Sstevel@tonic-gate 		mdb_warn("unable to read 'kmem_magtype'");
9157c478bd9Sstevel@tonic-gate 	} else if (addr < mt_sym.st_value ||
9167c478bd9Sstevel@tonic-gate 	    addr + sizeof (mt) - 1 > mt_sym.st_value + mt_sym.st_size - 1 ||
9177c478bd9Sstevel@tonic-gate 	    ((addr - mt_sym.st_value) % sizeof (mt)) != 0) {
9187c478bd9Sstevel@tonic-gate 		mdb_warn("cache '%s' has invalid magtype pointer (%p)\n",
9197c478bd9Sstevel@tonic-gate 		    cp->cache_name, addr);
9207c478bd9Sstevel@tonic-gate 		return (0);
9217c478bd9Sstevel@tonic-gate 	}
9227c478bd9Sstevel@tonic-gate 	if (mdb_vread(&mt, sizeof (mt), addr) == -1) {
9237c478bd9Sstevel@tonic-gate 		mdb_warn("unable to read magtype at %a", addr);
9247c478bd9Sstevel@tonic-gate 		return (0);
9257c478bd9Sstevel@tonic-gate 	}
9267c478bd9Sstevel@tonic-gate 	return (mt.mt_magsize);
9277c478bd9Sstevel@tonic-gate }
9287c478bd9Sstevel@tonic-gate 
9297c478bd9Sstevel@tonic-gate /*ARGSUSED*/
9307c478bd9Sstevel@tonic-gate static int
kmem_estimate_slab(uintptr_t addr,const kmem_slab_t * sp,size_t * est)9317c478bd9Sstevel@tonic-gate kmem_estimate_slab(uintptr_t addr, const kmem_slab_t *sp, size_t *est)
9327c478bd9Sstevel@tonic-gate {
9337c478bd9Sstevel@tonic-gate 	*est -= (sp->slab_chunks - sp->slab_refcnt);
9347c478bd9Sstevel@tonic-gate 
9357c478bd9Sstevel@tonic-gate 	return (WALK_NEXT);
9367c478bd9Sstevel@tonic-gate }
9377c478bd9Sstevel@tonic-gate 
9387c478bd9Sstevel@tonic-gate /*
9397c478bd9Sstevel@tonic-gate  * Returns an upper bound on the number of allocated buffers in a given
9407c478bd9Sstevel@tonic-gate  * cache.
9417c478bd9Sstevel@tonic-gate  */
9427c478bd9Sstevel@tonic-gate size_t
kmem_estimate_allocated(uintptr_t addr,const kmem_cache_t * cp)9437c478bd9Sstevel@tonic-gate kmem_estimate_allocated(uintptr_t addr, const kmem_cache_t *cp)
9447c478bd9Sstevel@tonic-gate {
9457c478bd9Sstevel@tonic-gate 	int magsize;
9467c478bd9Sstevel@tonic-gate 	size_t cache_est;
9477c478bd9Sstevel@tonic-gate 
9487c478bd9Sstevel@tonic-gate 	cache_est = cp->cache_buftotal;
9497c478bd9Sstevel@tonic-gate 
9507c478bd9Sstevel@tonic-gate 	(void) mdb_pwalk("kmem_slab_partial",
9517c478bd9Sstevel@tonic-gate 	    (mdb_walk_cb_t)kmem_estimate_slab, &cache_est, addr);
9527c478bd9Sstevel@tonic-gate 
9537c478bd9Sstevel@tonic-gate 	if ((magsize = kmem_get_magsize(cp)) != 0) {
9547c478bd9Sstevel@tonic-gate 		size_t mag_est = cp->cache_full.ml_total * magsize;
9557c478bd9Sstevel@tonic-gate 
9567c478bd9Sstevel@tonic-gate 		if (cache_est >= mag_est) {
9577c478bd9Sstevel@tonic-gate 			cache_est -= mag_est;
9587c478bd9Sstevel@tonic-gate 		} else {
9597c478bd9Sstevel@tonic-gate 			mdb_warn("cache %p's magazine layer holds more buffers "
9607c478bd9Sstevel@tonic-gate 			    "than the slab layer.\n", addr);
9617c478bd9Sstevel@tonic-gate 		}
9627c478bd9Sstevel@tonic-gate 	}
9637c478bd9Sstevel@tonic-gate 	return (cache_est);
9647c478bd9Sstevel@tonic-gate }
9657c478bd9Sstevel@tonic-gate 
9667c478bd9Sstevel@tonic-gate #define	READMAG_ROUNDS(rounds) { \
9677c478bd9Sstevel@tonic-gate 	if (mdb_vread(mp, magbsize, (uintptr_t)kmp) == -1) { \
9687c478bd9Sstevel@tonic-gate 		mdb_warn("couldn't read magazine at %p", kmp); \
9697c478bd9Sstevel@tonic-gate 		goto fail; \
9707c478bd9Sstevel@tonic-gate 	} \
9717c478bd9Sstevel@tonic-gate 	for (i = 0; i < rounds; i++) { \
9727c478bd9Sstevel@tonic-gate 		maglist[magcnt++] = mp->mag_round[i]; \
9737c478bd9Sstevel@tonic-gate 		if (magcnt == magmax) { \
9747c478bd9Sstevel@tonic-gate 			mdb_warn("%d magazines exceeds fudge factor\n", \
9757c478bd9Sstevel@tonic-gate 			    magcnt); \
9767c478bd9Sstevel@tonic-gate 			goto fail; \
9777c478bd9Sstevel@tonic-gate 		} \
9787c478bd9Sstevel@tonic-gate 	} \
9797c478bd9Sstevel@tonic-gate }
9807c478bd9Sstevel@tonic-gate 
9817c478bd9Sstevel@tonic-gate int
kmem_read_magazines(kmem_cache_t * cp,uintptr_t addr,int ncpus,void *** maglistp,size_t * magcntp,size_t * magmaxp,int alloc_flags)9827c478bd9Sstevel@tonic-gate kmem_read_magazines(kmem_cache_t *cp, uintptr_t addr, int ncpus,
9837c478bd9Sstevel@tonic-gate     void ***maglistp, size_t *magcntp, size_t *magmaxp, int alloc_flags)
9847c478bd9Sstevel@tonic-gate {
9857c478bd9Sstevel@tonic-gate 	kmem_magazine_t *kmp, *mp;
9867c478bd9Sstevel@tonic-gate 	void **maglist = NULL;
9877c478bd9Sstevel@tonic-gate 	int i, cpu;
9887c478bd9Sstevel@tonic-gate 	size_t magsize, magmax, magbsize;
9897c478bd9Sstevel@tonic-gate 	size_t magcnt = 0;
9907c478bd9Sstevel@tonic-gate 
9917c478bd9Sstevel@tonic-gate 	/*
9927c478bd9Sstevel@tonic-gate 	 * Read the magtype out of the cache, after verifying the pointer's
9937c478bd9Sstevel@tonic-gate 	 * correctness.
9947c478bd9Sstevel@tonic-gate 	 */
9957c478bd9Sstevel@tonic-gate 	magsize = kmem_get_magsize(cp);
996789d94c2Sjwadams 	if (magsize == 0) {
997789d94c2Sjwadams 		*maglistp = NULL;
998789d94c2Sjwadams 		*magcntp = 0;
999789d94c2Sjwadams 		*magmaxp = 0;
1000789d94c2Sjwadams 		return (WALK_NEXT);
1001789d94c2Sjwadams 	}
10027c478bd9Sstevel@tonic-gate 
10037c478bd9Sstevel@tonic-gate 	/*
10047c478bd9Sstevel@tonic-gate 	 * There are several places where we need to go buffer hunting:
10057c478bd9Sstevel@tonic-gate 	 * the per-CPU loaded magazine, the per-CPU spare full magazine,
10067c478bd9Sstevel@tonic-gate 	 * and the full magazine list in the depot.
10077c478bd9Sstevel@tonic-gate 	 *
10087c478bd9Sstevel@tonic-gate 	 * For an upper bound on the number of buffers in the magazine
10097c478bd9Sstevel@tonic-gate 	 * layer, we have the number of magazines on the cache_full
10107c478bd9Sstevel@tonic-gate 	 * list plus at most two magazines per CPU (the loaded and the
10117c478bd9Sstevel@tonic-gate 	 * spare).  Toss in 100 magazines as a fudge factor in case this
10127c478bd9Sstevel@tonic-gate 	 * is live (the number "100" comes from the same fudge factor in
10137c478bd9Sstevel@tonic-gate 	 * crash(1M)).
10147c478bd9Sstevel@tonic-gate 	 */
10157c478bd9Sstevel@tonic-gate 	magmax = (cp->cache_full.ml_total + 2 * ncpus + 100) * magsize;
10167c478bd9Sstevel@tonic-gate 	magbsize = offsetof(kmem_magazine_t, mag_round[magsize]);
10177c478bd9Sstevel@tonic-gate 
10187c478bd9Sstevel@tonic-gate 	if (magbsize >= PAGESIZE / 2) {
10197c478bd9Sstevel@tonic-gate 		mdb_warn("magazine size for cache %p unreasonable (%x)\n",
10207c478bd9Sstevel@tonic-gate 		    addr, magbsize);
1021789d94c2Sjwadams 		return (WALK_ERR);
10227c478bd9Sstevel@tonic-gate 	}
10237c478bd9Sstevel@tonic-gate 
10247c478bd9Sstevel@tonic-gate 	maglist = mdb_alloc(magmax * sizeof (void *), alloc_flags);
10257c478bd9Sstevel@tonic-gate 	mp = mdb_alloc(magbsize, alloc_flags);
10267c478bd9Sstevel@tonic-gate 	if (mp == NULL || maglist == NULL)
10277c478bd9Sstevel@tonic-gate 		goto fail;
10287c478bd9Sstevel@tonic-gate 
10297c478bd9Sstevel@tonic-gate 	/*
10307c478bd9Sstevel@tonic-gate 	 * First up: the magazines in the depot (i.e. on the cache_full list).
10317c478bd9Sstevel@tonic-gate 	 */
10327c478bd9Sstevel@tonic-gate 	for (kmp = cp->cache_full.ml_list; kmp != NULL; ) {
10337c478bd9Sstevel@tonic-gate 		READMAG_ROUNDS(magsize);
10347c478bd9Sstevel@tonic-gate 		kmp = mp->mag_next;
10357c478bd9Sstevel@tonic-gate 
10367c478bd9Sstevel@tonic-gate 		if (kmp == cp->cache_full.ml_list)
10377c478bd9Sstevel@tonic-gate 			break; /* cache_full list loop detected */
10387c478bd9Sstevel@tonic-gate 	}
10397c478bd9Sstevel@tonic-gate 
10407c478bd9Sstevel@tonic-gate 	dprintf(("cache_full list done\n"));
10417c478bd9Sstevel@tonic-gate 
10427c478bd9Sstevel@tonic-gate 	/*
10437c478bd9Sstevel@tonic-gate 	 * Now whip through the CPUs, snagging the loaded magazines
10447c478bd9Sstevel@tonic-gate 	 * and full spares.
10459dd77bc8SDave Plauger 	 *
10469dd77bc8SDave Plauger 	 * In order to prevent inconsistent dumps, rounds and prounds
10479dd77bc8SDave Plauger 	 * are copied aside before dumping begins.
10487c478bd9Sstevel@tonic-gate 	 */
10497c478bd9Sstevel@tonic-gate 	for (cpu = 0; cpu < ncpus; cpu++) {
10507c478bd9Sstevel@tonic-gate 		kmem_cpu_cache_t *ccp = &cp->cache_cpu[cpu];
10519dd77bc8SDave Plauger 		short rounds, prounds;
10529dd77bc8SDave Plauger 
10539dd77bc8SDave Plauger 		if (KMEM_DUMPCC(ccp)) {
10549dd77bc8SDave Plauger 			rounds = ccp->cc_dump_rounds;
10559dd77bc8SDave Plauger 			prounds = ccp->cc_dump_prounds;
10569dd77bc8SDave Plauger 		} else {
10579dd77bc8SDave Plauger 			rounds = ccp->cc_rounds;
10589dd77bc8SDave Plauger 			prounds = ccp->cc_prounds;
10599dd77bc8SDave Plauger 		}
10607c478bd9Sstevel@tonic-gate 
10617c478bd9Sstevel@tonic-gate 		dprintf(("reading cpu cache %p\n",
10627c478bd9Sstevel@tonic-gate 		    (uintptr_t)ccp - (uintptr_t)cp + addr));
10637c478bd9Sstevel@tonic-gate 
10649dd77bc8SDave Plauger 		if (rounds > 0 &&
10657c478bd9Sstevel@tonic-gate 		    (kmp = ccp->cc_loaded) != NULL) {
10669dd77bc8SDave Plauger 			dprintf(("reading %d loaded rounds\n", rounds));
10679dd77bc8SDave Plauger 			READMAG_ROUNDS(rounds);
10687c478bd9Sstevel@tonic-gate 		}
10697c478bd9Sstevel@tonic-gate 
10709dd77bc8SDave Plauger 		if (prounds > 0 &&
10717c478bd9Sstevel@tonic-gate 		    (kmp = ccp->cc_ploaded) != NULL) {
10727c478bd9Sstevel@tonic-gate 			dprintf(("reading %d previously loaded rounds\n",
10739dd77bc8SDave Plauger 			    prounds));
10749dd77bc8SDave Plauger 			READMAG_ROUNDS(prounds);
10757c478bd9Sstevel@tonic-gate 		}
10767c478bd9Sstevel@tonic-gate 	}
10777c478bd9Sstevel@tonic-gate 
10787c478bd9Sstevel@tonic-gate 	dprintf(("magazine layer: %d buffers\n", magcnt));
10797c478bd9Sstevel@tonic-gate 
10807c478bd9Sstevel@tonic-gate 	if (!(alloc_flags & UM_GC))
10817c478bd9Sstevel@tonic-gate 		mdb_free(mp, magbsize);
10827c478bd9Sstevel@tonic-gate 
10837c478bd9Sstevel@tonic-gate 	*maglistp = maglist;
10847c478bd9Sstevel@tonic-gate 	*magcntp = magcnt;
10857c478bd9Sstevel@tonic-gate 	*magmaxp = magmax;
10867c478bd9Sstevel@tonic-gate 
10877c478bd9Sstevel@tonic-gate 	return (WALK_NEXT);
10887c478bd9Sstevel@tonic-gate 
10897c478bd9Sstevel@tonic-gate fail:
10907c478bd9Sstevel@tonic-gate 	if (!(alloc_flags & UM_GC)) {
10917c478bd9Sstevel@tonic-gate 		if (mp)
10927c478bd9Sstevel@tonic-gate 			mdb_free(mp, magbsize);
10937c478bd9Sstevel@tonic-gate 		if (maglist)
10947c478bd9Sstevel@tonic-gate 			mdb_free(maglist, magmax * sizeof (void *));
1095