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
5ad23a2dbSjohansen  * Common Development and Distribution License (the "License").
6ad23a2dbSjohansen  * 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 /*
2264a3d88bSJonathan Adams  * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
233b442230SJordan Paige Hendricks  * Copyright 2019 Joyent, Inc.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
26cbdcbd05SJonathan Adams #include <mdb/mdb_param.h>
277c478bd9Sstevel@tonic-gate #include <mdb/mdb_modapi.h>
28cbdcbd05SJonathan Adams #include <mdb/mdb_ks.h>
297c478bd9Sstevel@tonic-gate #include <sys/types.h>
30cbdcbd05SJonathan Adams #include <sys/memlist.h>
31cbdcbd05SJonathan Adams #include <sys/swap.h>
32cbdcbd05SJonathan Adams #include <sys/systm.h>
33cbdcbd05SJonathan Adams #include <sys/thread.h>
34cbdcbd05SJonathan Adams #include <vm/anon.h>
35cbdcbd05SJonathan Adams #include <vm/as.h>
367c478bd9Sstevel@tonic-gate #include <vm/page.h>
377c478bd9Sstevel@tonic-gate #include <sys/thread.h>
387c478bd9Sstevel@tonic-gate #include <sys/swap.h>
397c478bd9Sstevel@tonic-gate #include <sys/memlist.h>
40af4c679fSSean McEnroe #include <sys/vnode.h>
41cbdcbd05SJonathan Adams #include <vm/seg_map.h>
42cbdcbd05SJonathan Adams #include <vm/seg_vn.h>
43284ce987SPatrick Mooney #include <vm/seg_hole.h>
44843e1988Sjohnlev #if defined(__i386) || defined(__amd64)
45843e1988Sjohnlev #include <sys/balloon_impl.h>
46843e1988Sjohnlev #endif
477c478bd9Sstevel@tonic-gate 
48cbdcbd05SJonathan Adams #include "avl.h"
4964a3d88bSJonathan Adams #include "memory.h"
50cbdcbd05SJonathan Adams 
517c478bd9Sstevel@tonic-gate /*
527c478bd9Sstevel@tonic-gate  * Page walker.
537c478bd9Sstevel@tonic-gate  * By default, this will walk all pages in the system.  If given an
547c478bd9Sstevel@tonic-gate  * address, it will walk all pages belonging to the vnode at that
557c478bd9Sstevel@tonic-gate  * address.
567c478bd9Sstevel@tonic-gate  */
577c478bd9Sstevel@tonic-gate 
587c478bd9Sstevel@tonic-gate /*
597c478bd9Sstevel@tonic-gate  * page_walk_data
607c478bd9Sstevel@tonic-gate  *
617c478bd9Sstevel@tonic-gate  * pw_hashleft is set to -1 when walking a vnode's pages, and holds the
627c478bd9Sstevel@tonic-gate  * number of hash locations remaining in the page hash table when
637c478bd9Sstevel@tonic-gate  * walking all pages.
647c478bd9Sstevel@tonic-gate  *
657c478bd9Sstevel@tonic-gate  * The astute reader will notice that pw_hashloc is only used when
667c478bd9Sstevel@tonic-gate  * reading all pages (to hold a pointer to our location in the page
677c478bd9Sstevel@tonic-gate  * hash table), and that pw_first is only used when reading the pages
687c478bd9Sstevel@tonic-gate  * belonging to a particular vnode (to hold a pointer to the first
697c478bd9Sstevel@tonic-gate  * page).  While these could be combined to be a single pointer, they
707c478bd9Sstevel@tonic-gate  * are left separate for clarity.
717c478bd9Sstevel@tonic-gate  */
727c478bd9Sstevel@tonic-gate typedef struct page_walk_data {
737c478bd9Sstevel@tonic-gate 	long		pw_hashleft;
747c478bd9Sstevel@tonic-gate 	void		**pw_hashloc;
757c478bd9Sstevel@tonic-gate 	uintptr_t	pw_first;
767c478bd9Sstevel@tonic-gate } page_walk_data_t;
777c478bd9Sstevel@tonic-gate 
787c478bd9Sstevel@tonic-gate int
page_walk_init(mdb_walk_state_t * wsp)797c478bd9Sstevel@tonic-gate page_walk_init(mdb_walk_state_t *wsp)
807c478bd9Sstevel@tonic-gate {
817c478bd9Sstevel@tonic-gate 	page_walk_data_t	*pwd;
827c478bd9Sstevel@tonic-gate 	void	**ptr;
837c478bd9Sstevel@tonic-gate 	size_t	hashsz;
847c478bd9Sstevel@tonic-gate 	vnode_t	vn;
857c478bd9Sstevel@tonic-gate 
86892ad162SToomas Soome 	if (wsp->walk_addr == 0) {
877c478bd9Sstevel@tonic-gate 
887c478bd9Sstevel@tonic-gate 		/*
897c478bd9Sstevel@tonic-gate 		 * Walk all pages
907c478bd9Sstevel@tonic-gate 		 */
917c478bd9Sstevel@tonic-gate 
927c478bd9Sstevel@tonic-gate 		if ((mdb_readvar(&ptr, "page_hash") == -1) ||
937c478bd9Sstevel@tonic-gate 		    (mdb_readvar(&hashsz, "page_hashsz") == -1) ||
947c478bd9Sstevel@tonic-gate 		    (ptr == NULL) || (hashsz == 0)) {
957c478bd9Sstevel@tonic-gate 			mdb_warn("page_hash, page_hashsz not found or invalid");
967c478bd9Sstevel@tonic-gate 			return (WALK_ERR);
977c478bd9Sstevel@tonic-gate 		}
987c478bd9Sstevel@tonic-gate 
997c478bd9Sstevel@tonic-gate 		/*
1007c478bd9Sstevel@tonic-gate 		 * Since we are walking all pages, initialize hashleft
1017c478bd9Sstevel@tonic-gate 		 * to be the remaining number of entries in the page
1027c478bd9Sstevel@tonic-gate 		 * hash.  hashloc is set the start of the page hash
1037c478bd9Sstevel@tonic-gate 		 * table.  Setting the walk address to 0 indicates that
1047c478bd9Sstevel@tonic-gate 		 * we aren't currently following a hash chain, and that
1057c478bd9Sstevel@tonic-gate 		 * we need to scan the page hash table for a page.
1067c478bd9Sstevel@tonic-gate 		 */
1077c478bd9Sstevel@tonic-gate 		pwd = mdb_alloc(sizeof (page_walk_data_t), UM_SLEEP);
1087c478bd9Sstevel@tonic-gate 		pwd->pw_hashleft = hashsz;
1097c478bd9Sstevel@tonic-gate 		pwd->pw_hashloc = ptr;
1107c478bd9Sstevel@tonic-gate 		wsp->walk_addr = 0;
1117c478bd9Sstevel@tonic-gate 	} else {
1127c478bd9Sstevel@tonic-gate 
1137c478bd9Sstevel@tonic-gate 		/*
1147c478bd9Sstevel@tonic-gate 		 * Walk just this vnode
1157c478bd9Sstevel@tonic-gate 		 */
1167c478bd9Sstevel@tonic-gate 
1177c478bd9Sstevel@tonic-gate 		if (mdb_vread(&vn, sizeof (vnode_t), wsp->walk_addr) == -1) {
1187c478bd9Sstevel@tonic-gate 			mdb_warn("unable to read vnode_t at %#lx",
1197c478bd9Sstevel@tonic-gate 			    wsp->walk_addr);
1207c478bd9Sstevel@tonic-gate 			return (WALK_ERR);
1217c478bd9Sstevel@tonic-gate 		}
1227c478bd9Sstevel@tonic-gate 
1237c478bd9Sstevel@tonic-gate 		/*
1247c478bd9Sstevel@tonic-gate 		 * We set hashleft to -1 to indicate that we are
1257c478bd9Sstevel@tonic-gate 		 * walking a vnode, and initialize first to 0 (it is
1267c478bd9Sstevel@tonic-gate 		 * used to terminate the walk, so it must not be set
1277c478bd9Sstevel@tonic-gate 		 * until after we have walked the first page).  The
1287c478bd9Sstevel@tonic-gate 		 * walk address is set to the first page.
1297c478bd9Sstevel@tonic-gate 		 */
1307c478bd9Sstevel@tonic-gate 		pwd = mdb_alloc(sizeof (page_walk_data_t), UM_SLEEP);
1317c478bd9Sstevel@tonic-gate 		pwd->pw_hashleft = -1;
1327c478bd9Sstevel@tonic-gate 		pwd->pw_first = 0;
1337c478bd9Sstevel@tonic-gate 
1347c478bd9Sstevel@tonic-gate 		wsp->walk_addr = (uintptr_t)vn.v_pages;
1357c478bd9Sstevel@tonic-gate 	}
1367c478bd9Sstevel@tonic-gate 
1377c478bd9Sstevel@tonic-gate 	wsp->walk_data = pwd;
1387c478bd9Sstevel@tonic-gate 
1397c478bd9Sstevel@tonic-gate 	return (WALK_NEXT);
1407c478bd9Sstevel@tonic-gate }
1417c478bd9Sstevel@tonic-gate 
1427c478bd9Sstevel@tonic-gate int
page_walk_step(mdb_walk_state_t * wsp)1437c478bd9Sstevel@tonic-gate page_walk_step(mdb_walk_state_t *wsp)
1447c478bd9Sstevel@tonic-gate {
1457c478bd9Sstevel@tonic-gate 	page_walk_data_t	*pwd = wsp->walk_data;
1467c478bd9Sstevel@tonic-gate 	page_t		page;
1477c478bd9Sstevel@tonic-gate 	uintptr_t	pp;
1487c478bd9Sstevel@tonic-gate 
1497c478bd9Sstevel@tonic-gate 	pp = wsp->walk_addr;
1507c478bd9Sstevel@tonic-gate 
1517c478bd9Sstevel@tonic-gate 	if (pwd->pw_hashleft < 0) {
1527c478bd9Sstevel@tonic-gate 
1537c478bd9Sstevel@tonic-gate 		/* We're walking a vnode's pages */
1547c478bd9Sstevel@tonic-gate 
1557c478bd9Sstevel@tonic-gate 		/*
1567c478bd9Sstevel@tonic-gate 		 * If we don't have any pages to walk, we have come
1577c478bd9Sstevel@tonic-gate 		 * back around to the first one (we finished), or we
1587c478bd9Sstevel@tonic-gate 		 * can't read the page we're looking at, we are done.
1597c478bd9Sstevel@tonic-gate 		 */
160892ad162SToomas Soome 		if (pp == 0 || pp == pwd->pw_first)
1617c478bd9Sstevel@tonic-gate 			return (WALK_DONE);
1627c478bd9Sstevel@tonic-gate 		if (mdb_vread(&page, sizeof (page_t), pp) == -1) {
1637c478bd9Sstevel@tonic-gate 			mdb_warn("unable to read page_t at %#lx", pp);
1647c478bd9Sstevel@tonic-gate 			return (WALK_ERR);
1657c478bd9Sstevel@tonic-gate 		}
1667c478bd9Sstevel@tonic-gate 
1677c478bd9Sstevel@tonic-gate 		/*
1687c478bd9Sstevel@tonic-gate 		 * Set the walk address to the next page, and if the
1697c478bd9Sstevel@tonic-gate 		 * first page hasn't been set yet (i.e. we are on the
1707c478bd9Sstevel@tonic-gate 		 * first page), set it.
1717c478bd9Sstevel@tonic-gate 		 */
1727c478bd9Sstevel@tonic-gate 		wsp->walk_addr = (uintptr_t)page.p_vpnext;
173892ad162SToomas Soome 		if (pwd->pw_first == 0)
1747c478bd9Sstevel@tonic-gate 			pwd->pw_first = pp;
1757c478bd9Sstevel@tonic-gate 
1767c478bd9Sstevel@tonic-gate 	} else if (pwd->pw_hashleft > 0) {
1777c478bd9Sstevel@tonic-gate 
1787c478bd9Sstevel@tonic-gate 		/* We're walking all pages */
1797c478bd9Sstevel@tonic-gate 
1807c478bd9Sstevel@tonic-gate 		/*
1817c478bd9Sstevel@tonic-gate 		 * If pp (the walk address) is NULL, we scan through
1827c478bd9Sstevel@tonic-gate 		 * the page hash table until we find a page.
1837c478bd9Sstevel@tonic-gate 		 */
184892ad162SToomas Soome 		if (pp == 0) {
1857c478bd9Sstevel@tonic-gate 
1867c478bd9Sstevel@tonic-gate 			/*
1877c478bd9Sstevel@tonic-gate 			 * Iterate through the page hash table until we
1887c478bd9Sstevel@tonic-gate 			 * find a page or reach the end.
1897c478bd9Sstevel@tonic-gate 			 */
1907c478bd9Sstevel@tonic-gate 			do {
1917c478bd9Sstevel@tonic-gate 				if (mdb_vread(&pp, sizeof (uintptr_t),
1927c478bd9Sstevel@tonic-gate 				    (uintptr_t)pwd->pw_hashloc) == -1) {
1937c478bd9Sstevel@tonic-gate 					mdb_warn("unable to read from %#p",
1947c478bd9Sstevel@tonic-gate 					    pwd->pw_hashloc);
1957c478bd9Sstevel@tonic-gate 					return (WALK_ERR);
1967c478bd9Sstevel@tonic-gate 				}
1977c478bd9Sstevel@tonic-gate 				pwd->pw_hashleft--;
1987c478bd9Sstevel@tonic-gate 				pwd->pw_hashloc++;
199892ad162SToomas Soome 			} while (pwd->pw_hashleft && (pp == 0));
2007c478bd9Sstevel@tonic-gate 
2017c478bd9Sstevel@tonic-gate 			/*
2027c478bd9Sstevel@tonic-gate 			 * We've reached the end; exit.
2037c478bd9Sstevel@tonic-gate 			 */
204892ad162SToomas Soome 			if (pp == 0)
2057c478bd9Sstevel@tonic-gate 				return (WALK_DONE);
2067c478bd9Sstevel@tonic-gate 		}
2077c478bd9Sstevel@tonic-gate 
2087c478bd9Sstevel@tonic-gate 		if (mdb_vread(&page, sizeof (page_t), pp) == -1) {
2097c478bd9Sstevel@tonic-gate 			mdb_warn("unable to read page_t at %#lx", pp);
2107c478bd9Sstevel@tonic-gate 			return (WALK_ERR);
2117c478bd9Sstevel@tonic-gate 		}
2127c478bd9Sstevel@tonic-gate 
2137c478bd9Sstevel@tonic-gate 		/*
2147c478bd9Sstevel@tonic-gate 		 * Set the walk address to the next page.
2157c478bd9Sstevel@tonic-gate 		 */
2167c478bd9Sstevel@tonic-gate 		wsp->walk_addr = (uintptr_t)page.p_hash;
2177c478bd9Sstevel@tonic-gate 
2187c478bd9Sstevel@tonic-gate 	} else {
2197c478bd9Sstevel@tonic-gate 		/* We've finished walking all pages. */
2207c478bd9Sstevel@tonic-gate 		return (WALK_DONE);
2217c478bd9Sstevel@tonic-gate 	}
2227c478bd9Sstevel@tonic-gate 
2237c478bd9Sstevel@tonic-gate 	return (wsp->walk_callback(pp, &page, wsp->walk_cbdata));
2247c478bd9Sstevel@tonic-gate }
2257c478bd9Sstevel@tonic-gate 
2267c478bd9Sstevel@tonic-gate void
page_walk_fini(mdb_walk_state_t * wsp)2277c478bd9Sstevel@tonic-gate page_walk_fini(mdb_walk_state_t *wsp)
2287c478bd9Sstevel@tonic-gate {
2297c478bd9Sstevel@tonic-gate 	mdb_free(wsp->walk_data, sizeof (page_walk_data_t));
2307c478bd9Sstevel@tonic-gate }
2317c478bd9Sstevel@tonic-gate 
232d20abfaaSPavel Tatashin /*
233d20abfaaSPavel Tatashin  * allpages walks all pages in the system in order they appear in
234d20abfaaSPavel Tatashin  * the memseg structure
235d20abfaaSPavel Tatashin  */
236d20abfaaSPavel Tatashin 
237d20abfaaSPavel Tatashin #define	PAGE_BUFFER	128
238d20abfaaSPavel Tatashin 
239d20abfaaSPavel Tatashin int
allpages_walk_init(mdb_walk_state_t * wsp)240d20abfaaSPavel Tatashin allpages_walk_init(mdb_walk_state_t *wsp)
241d20abfaaSPavel Tatashin {
242d20abfaaSPavel Tatashin 	if (wsp->walk_addr != 0) {
243d20abfaaSPavel Tatashin 		mdb_warn("allpages only supports global walks.\n");
244d20abfaaSPavel Tatashin 		return (WALK_ERR);
245d20abfaaSPavel Tatashin 	}
246d20abfaaSPavel Tatashin 
247d20abfaaSPavel Tatashin 	if (mdb_layered_walk("memseg", wsp) == -1) {
248d20abfaaSPavel Tatashin 		mdb_warn("couldn't walk 'memseg'");
249d20abfaaSPavel Tatashin 		return (WALK_ERR);
250d20abfaaSPavel Tatashin 	}
251d20abfaaSPavel Tatashin 
252d20abfaaSPavel Tatashin 	wsp->walk_data = mdb_alloc(sizeof (page_t) * PAGE_BUFFER, UM_SLEEP);
253d20abfaaSPavel Tatashin 	return (WALK_NEXT);
254d20abfaaSPavel Tatashin }
255d20abfaaSPavel Tatashin 
256d20abfaaSPavel Tatashin int
allpages_walk_step(mdb_walk_state_t * wsp)257d20abfaaSPavel Tatashin allpages_walk_step(mdb_walk_state_t *wsp)
258d20abfaaSPavel Tatashin {
259d20abfaaSPavel Tatashin 	const struct memseg *msp = wsp->walk_layer;
260d20abfaaSPavel Tatashin 	page_t *buf = wsp->walk_data;
261d20abfaaSPavel Tatashin 	size_t pg_read, i;
262d20abfaaSPavel Tatashin 	size_t pg_num = msp->pages_end - msp->pages_base;
263d20abfaaSPavel Tatashin 	const page_t *pg_addr = msp->pages;
264d20abfaaSPavel Tatashin 
265d20abfaaSPavel Tatashin 	while (pg_num > 0) {
266d20abfaaSPavel Tatashin 		pg_read = MIN(pg_num, PAGE_BUFFER);
267d20abfaaSPavel Tatashin 
268d20abfaaSPavel Tatashin 		if (mdb_vread(buf, pg_read * sizeof (page_t),
269d20abfaaSPavel Tatashin 		    (uintptr_t)pg_addr) == -1) {
270d20abfaaSPavel Tatashin 			mdb_warn("can't read page_t's at %#lx", pg_addr);
271d20abfaaSPavel Tatashin 			return (WALK_ERR);
272d20abfaaSPavel Tatashin 		}
273d20abfaaSPavel Tatashin 		for (i = 0; i < pg_read; i++) {
274d20abfaaSPavel Tatashin 			int ret = wsp->walk_callback((uintptr_t)&pg_addr[i],
275d20abfaaSPavel Tatashin 			    &buf[i], wsp->walk_cbdata);
276d20abfaaSPavel Tatashin 
277d20abfaaSPavel Tatashin 			if (ret != WALK_NEXT)
278d20abfaaSPavel Tatashin 				return (ret);
279d20abfaaSPavel Tatashin 		}
280d20abfaaSPavel Tatashin 		pg_num -= pg_read;
281d20abfaaSPavel Tatashin 		pg_addr += pg_read;
282d20abfaaSPavel Tatashin 	}
283d20abfaaSPavel Tatashin 
284d20abfaaSPavel Tatashin 	return (WALK_NEXT);
285d20abfaaSPavel Tatashin }
286d20abfaaSPavel Tatashin 
287d20abfaaSPavel Tatashin void
allpages_walk_fini(mdb_walk_state_t * wsp)288d20abfaaSPavel Tatashin allpages_walk_fini(mdb_walk_state_t *wsp)
289d20abfaaSPavel Tatashin {
290d20abfaaSPavel Tatashin 	mdb_free(wsp->walk_data, sizeof (page_t) * PAGE_BUFFER);
291d20abfaaSPavel Tatashin }
292d20abfaaSPavel Tatashin 
293d20abfaaSPavel Tatashin /*
294d20abfaaSPavel Tatashin  * Hash table + LRU queue.
295d20abfaaSPavel Tatashin  * This table is used to cache recently read vnodes for the memstat
296d20abfaaSPavel Tatashin  * command, to reduce the number of mdb_vread calls.  This greatly
297d20abfaaSPavel Tatashin  * speeds the memstat command on on live, large CPU count systems.
298d20abfaaSPavel Tatashin  */
299d20abfaaSPavel Tatashin 
300d20abfaaSPavel Tatashin #define	VN_SMALL	401
301d20abfaaSPavel Tatashin #define	VN_LARGE	10007
302d20abfaaSPavel Tatashin #define	VN_HTABLE_KEY(p, hp)	((p) % ((hp)->vn_htable_buckets))
303d20abfaaSPavel Tatashin 
304d20abfaaSPavel Tatashin struct vn_htable_list {
305d20abfaaSPavel Tatashin 	uint_t vn_flag;				/* v_flag from vnode	*/
306d20abfaaSPavel Tatashin 	uintptr_t vn_ptr;			/* pointer to vnode	*/
307d20abfaaSPavel Tatashin 	struct vn_htable_list *vn_q_next;	/* queue next pointer	*/
308d20abfaaSPavel Tatashin 	struct vn_htable_list *vn_q_prev;	/* queue prev pointer	*/
309d20abfaaSPavel Tatashin 	struct vn_htable_list *vn_h_next;	/* hash table pointer	*/
310d20abfaaSPavel Tatashin };
311d20abfaaSPavel Tatashin 
312d20abfaaSPavel Tatashin /*
313d20abfaaSPavel Tatashin  * vn_q_first        -> points to to head of queue: the vnode that was most
314d20abfaaSPavel Tatashin  *                      recently used
315d20abfaaSPavel Tatashin  * vn_q_last         -> points to the oldest used vnode, and is freed once a new
316d20abfaaSPavel Tatashin  *                      vnode is read.
317d20abfaaSPavel Tatashin  * vn_htable         -> hash table
318d20abfaaSPavel Tatashin  * vn_htable_buf     -> contains htable objects
319d20abfaaSPavel Tatashin  * vn_htable_size    -> total number of items in the hash table
320d20abfaaSPavel Tatashin  * vn_htable_buckets -> number of buckets in the hash table
321d20abfaaSPavel Tatashin  */
322d20abfaaSPavel Tatashin typedef struct vn_htable {
323d20abfaaSPavel Tatashin 	struct vn_htable_list  *vn_q_first;
324d20abfaaSPavel Tatashin 	struct vn_htable_list  *vn_q_last;
325d20abfaaSPavel Tatashin 	struct vn_htable_list **vn_htable;
326d20abfaaSPavel Tatashin 	struct vn_htable_list  *vn_htable_buf;
327d20abfaaSPavel Tatashin 	int vn_htable_size;
328d20abfaaSPavel Tatashin 	int vn_htable_buckets;
329d20abfaaSPavel Tatashin } vn_htable_t;
330d20abfaaSPavel Tatashin 
331d20abfaaSPavel Tatashin 
332d20abfaaSPavel Tatashin /* allocate memory, initilize hash table and LRU queue */
333d20abfaaSPavel Tatashin static void
vn_htable_init(vn_htable_t * hp,size_t vn_size)334d20abfaaSPavel Tatashin vn_htable_init(vn_htable_t *hp, size_t vn_size)
335d20abfaaSPavel Tatashin {
336d20abfaaSPavel Tatashin 	int i;
337d20abfaaSPavel Tatashin 	int htable_size = MAX(vn_size, VN_LARGE);
338d20abfaaSPavel Tatashin 
339d20abfaaSPavel Tatashin 	if ((hp->vn_htable_buf = mdb_zalloc(sizeof (struct vn_htable_list)
340d20abfaaSPavel Tatashin 	    * htable_size, UM_NOSLEEP|UM_GC)) == NULL) {
341d20abfaaSPavel Tatashin 		htable_size = VN_SMALL;
342d20abfaaSPavel Tatashin 		hp->vn_htable_buf = mdb_zalloc(sizeof (struct vn_htable_list)
343d20abfaaSPavel Tatashin 		    * htable_size, UM_SLEEP|UM_GC);
344d20abfaaSPavel Tatashin 	}
345d20abfaaSPavel Tatashin 
346d20abfaaSPavel Tatashin 	hp->vn_htable = mdb_zalloc(sizeof (struct vn_htable_list *)
347d20abfaaSPavel Tatashin 	    * htable_size, UM_SLEEP|UM_GC);
348d20abfaaSPavel Tatashin 
349d20abfaaSPavel Tatashin 	hp->vn_q_first  = &hp->vn_htable_buf[0];
350d20abfaaSPavel Tatashin 	hp->vn_q_last   = &hp->vn_htable_buf[htable_size - 1];
351d20abfaaSPavel Tatashin 	hp->vn_q_first->vn_q_next = &hp->vn_htable_buf[1];
352d20abfaaSPavel Tatashin 	hp->vn_q_last->vn_q_prev = &hp->vn_htable_buf[htable_size - 2];
353d20abfaaSPavel Tatashin 
354d20abfaaSPavel Tatashin 	for (i = 1; i < (htable_size-1); i++) {
355d20abfaaSPavel Tatashin 		hp->vn_htable_buf[i].vn_q_next = &hp->vn_htable_buf[i + 1];
356d20abfaaSPavel Tatashin 		hp->vn_htable_buf[i].vn_q_prev = &hp->vn_htable_buf[i - 1];
357d20abfaaSPavel Tatashin 	}
358d20abfaaSPavel Tatashin 
359d20abfaaSPavel Tatashin 	hp->vn_htable_size = htable_size;
360d20abfaaSPavel Tatashin 	hp->vn_htable_buckets = htable_size;
361d20abfaaSPavel Tatashin }
362d20abfaaSPavel Tatashin 
363d20abfaaSPavel Tatashin 
364d20abfaaSPavel Tatashin /*
365d20abfaaSPavel Tatashin  * Find the vnode whose address is ptr, and return its v_flag in vp->v_flag.
366d20abfaaSPavel Tatashin  * The function tries to find needed information in the following order:
367d20abfaaSPavel Tatashin  *
368d20abfaaSPavel Tatashin  * 1. check if ptr is the first in queue
369d20abfaaSPavel Tatashin  * 2. check if ptr is in hash table (if so move it to the top of queue)
370d20abfaaSPavel Tatashin  * 3. do mdb_vread, remove last queue item from queue and hash table.
371d20abfaaSPavel Tatashin  *    Insert new information to freed object, and put this object in to the
372d20abfaaSPavel Tatashin  *    top of the queue.
373d20abfaaSPavel Tatashin  */
374d20abfaaSPavel Tatashin static int
vn_get(vn_htable_t * hp,struct vnode * vp,uintptr_t ptr)375d20abfaaSPavel Tatashin vn_get(vn_htable_t *hp, struct vnode *vp, uintptr_t ptr)
376d20abfaaSPavel Tatashin {
377d20abfaaSPavel Tatashin 	int hkey;
378d20abfaaSPavel Tatashin 	struct vn_htable_list *hent, **htmp, *q_next, *q_prev;
379d20abfaaSPavel Tatashin 	struct vn_htable_list  *q_first = hp->vn_q_first;
380d20abfaaSPavel Tatashin 
381d20abfaaSPavel Tatashin 	/* 1. vnode ptr is the first in queue, just get v_flag and return */
382d20abfaaSPavel Tatashin 	if (q_first->vn_ptr == ptr) {
383d20abfaaSPavel Tatashin 		vp->v_flag = q_first->vn_flag;
384d20abfaaSPavel Tatashin 
385d20abfaaSPavel Tatashin 		return (0);
386d20abfaaSPavel Tatashin 	}
387d20abfaaSPavel Tatashin 
388d20abfaaSPavel Tatashin 	/* 2. search the hash table for this ptr */
389d20abfaaSPavel Tatashin 	hkey = VN_HTABLE_KEY(ptr, hp);
390d20abfaaSPavel Tatashin 	hent = hp->vn_htable[hkey];
391d20abfaaSPavel Tatashin 	while (hent && (hent->vn_ptr != ptr))
392d20abfaaSPavel Tatashin 		hent = hent->vn_h_next;
393d20abfaaSPavel Tatashin 
394d20abfaaSPavel Tatashin 	/* 3. if hent is NULL, we did not find in hash table, do mdb_vread */
395d20abfaaSPavel Tatashin 	if (hent == NULL) {
396d20abfaaSPavel Tatashin 		struct vnode vn;
397d20abfaaSPavel Tatashin 
398d20abfaaSPavel Tatashin 		if (mdb_vread(&vn, sizeof (vnode_t), ptr) == -1) {
399d20abfaaSPavel Tatashin 			mdb_warn("unable to read vnode_t at %#lx", ptr);
400d20abfaaSPavel Tatashin 			return (-1);
401d20abfaaSPavel Tatashin 		}
402d20abfaaSPavel Tatashin 
403d20abfaaSPavel Tatashin 		/* we will insert read data into the last element in queue */
404d20abfaaSPavel Tatashin 		hent = hp->vn_q_last;
405d20abfaaSPavel Tatashin 
406d20abfaaSPavel Tatashin 		/* remove last hp->vn_q_last object from hash table */
407d20abfaaSPavel Tatashin 		if (hent->vn_ptr) {
408d20abfaaSPavel Tatashin 			htmp = &hp->vn_htable[VN_HTABLE_KEY(hent->vn_ptr, hp)];
409d20abfaaSPavel Tatashin 			while (*htmp != hent)
410d20abfaaSPavel Tatashin 				htmp = &(*htmp)->vn_h_next;
411d20abfaaSPavel Tatashin 			*htmp = hent->vn_h_next;
412d20abfaaSPavel Tatashin 		}
413d20abfaaSPavel Tatashin 
414d20abfaaSPavel Tatashin 		/* insert data into new free object */
415d20abfaaSPavel Tatashin 		hent->vn_ptr  = ptr;
416d20abfaaSPavel Tatashin 		hent->vn_flag = vn.v_flag;
417d20abfaaSPavel Tatashin 
418d20abfaaSPavel Tatashin 		/* insert new object into hash table */
419d20abfaaSPavel Tatashin 		hent->vn_h_next = hp->vn_htable[hkey];
420d20abfaaSPavel Tatashin 		hp->vn_htable[hkey] = hent;
421d20abfaaSPavel Tatashin 	}
422d20abfaaSPavel Tatashin 
423d20abfaaSPavel Tatashin 	/* Remove from queue. hent is not first, vn_q_prev is not NULL */
424d20abfaaSPavel Tatashin 	q_next = hent->vn_q_next;
425d20abfaaSPavel Tatashin 	q_prev = hent->vn_q_prev;
426d20abfaaSPavel Tatashin 	if (q_next == NULL)
427d20abfaaSPavel Tatashin 		hp->vn_q_last = q_prev;
428d20abfaaSPavel Tatashin 	else
429d20abfaaSPavel Tatashin 		q_next->vn_q_prev = q_prev;
430d20abfaaSPavel Tatashin 	q_prev->vn_q_next = q_next;
431d20abfaaSPavel Tatashin 
432d20abfaaSPavel Tatashin 	/* Add to the front of queue */
433d20abfaaSPavel Tatashin 	hent->vn_q_prev = NULL;
434d20abfaaSPavel Tatashin 	hent->vn_q_next = q_first;
435d20abfaaSPavel Tatashin 	q_first->vn_q_prev = hent;
436d20abfaaSPavel Tatashin 	hp->vn_q_first = hent;
437d20abfaaSPavel Tatashin 
438d20abfaaSPavel Tatashin 	/* Set v_flag in vnode pointer from hent */
439d20abfaaSPavel Tatashin 	vp->v_flag = hent->vn_flag;
440d20abfaaSPavel Tatashin 
441d20abfaaSPavel Tatashin 	return (0);
442d20abfaaSPavel Tatashin }
443d20abfaaSPavel Tatashin 
4447c478bd9Sstevel@tonic-gate /* Summary statistics of pages */
4457c478bd9Sstevel@tonic-gate typedef struct memstat {
446d20abfaaSPavel Tatashin 	struct vnode    *ms_unused_vp;	/* Unused pages vnode pointer	  */
447*04909c8cSJohn Levon 	struct vnode    *ms_kvps;	/* Cached address of vnode array  */
4487c478bd9Sstevel@tonic-gate 	uint64_t	ms_kmem;	/* Pages of kernel memory	  */
4490fab61baSJonathan W Adams 	uint64_t	ms_zfs_data;	/* Pages of zfs data		  */
450*04909c8cSJohn Levon 	uint64_t	ms_vmm_mem;	/* Pages of VMM mem		  */
4517c478bd9Sstevel@tonic-gate 	uint64_t	ms_anon;	/* Pages of anonymous memory	  */
4527c478bd9Sstevel@tonic-gate 	uint64_t	ms_vnode;	/* Pages of named (vnode) memory  */
4537c478bd9Sstevel@tonic-gate 	uint64_t	ms_exec;	/* Pages of exec/library memory	  */
4547c478bd9Sstevel@tonic-gate 	uint64_t	ms_cachelist;	/* Pages on the cachelist (free)  */
455176f4288SJoshua M. Clulow 	uint64_t	ms_bootpages;	/* Pages on the bootpages list    */
4567c478bd9Sstevel@tonic-gate 	uint64_t	ms_total;	/* Pages on page hash		  */
457d20abfaaSPavel Tatashin 	vn_htable_t	*ms_vn_htable;	/* Pointer to hash table	  */
458d20abfaaSPavel Tatashin 	struct vnode	ms_vn;		/* vnode buffer			  */
4597c478bd9Sstevel@tonic-gate } memstat_t;
4607c478bd9Sstevel@tonic-gate 
461*04909c8cSJohn Levon #define	MS_PP_ISTYPE(pp, stats, index) \
462*04909c8cSJohn Levon 	((pp)->p_vnode == &(stats->ms_kvps[index]))
463ad23a2dbSjohansen 
4647c478bd9Sstevel@tonic-gate /*
465d20abfaaSPavel Tatashin  * Summarize pages by type and update stat information
4667c478bd9Sstevel@tonic-gate  */
4677c478bd9Sstevel@tonic-gate 
4687c478bd9Sstevel@tonic-gate /* ARGSUSED */
4697c478bd9Sstevel@tonic-gate static int
memstat_callback(page_t * page,page_t * pp,memstat_t * stats)4707c478bd9Sstevel@tonic-gate memstat_callback(page_t *page, page_t *pp, memstat_t *stats)
4717c478bd9Sstevel@tonic-gate {
472d20abfaaSPavel Tatashin 	struct vnode *vp = &stats->ms_vn;
4737c478bd9Sstevel@tonic-gate 
474176f4288SJoshua M. Clulow 	if (PP_ISBOOTPAGES(pp))
475176f4288SJoshua M. Clulow 		stats->ms_bootpages++;
476176f4288SJoshua M. Clulow 	else if (pp->p_vnode == NULL || pp->p_vnode == stats->ms_unused_vp)
477d20abfaaSPavel Tatashin 		return (WALK_NEXT);
478*04909c8cSJohn Levon 	else if (MS_PP_ISTYPE(pp, stats, KV_KVP))
4797c478bd9Sstevel@tonic-gate 		stats->ms_kmem++;
480*04909c8cSJohn Levon 	else if (MS_PP_ISTYPE(pp, stats, KV_ZVP))
481d20abfaaSPavel Tatashin 		stats->ms_zfs_data++;
482*04909c8cSJohn Levon 	else if (MS_PP_ISTYPE(pp, stats, KV_VVP))
483*04909c8cSJohn Levon 		stats->ms_vmm_mem++;
484d20abfaaSPavel Tatashin 	else if (PP_ISFREE(pp))
485d20abfaaSPavel Tatashin 		stats->ms_cachelist++;
486d20abfaaSPavel Tatashin 	else if (vn_get(stats->ms_vn_htable, vp, (uintptr_t)pp->p_vnode))
487d20abfaaSPavel Tatashin 		return (WALK_ERR);
488d20abfaaSPavel Tatashin 	else if (IS_SWAPFSVP(vp))
489d20abfaaSPavel Tatashin 		stats->ms_anon++;
490d20abfaaSPavel Tatashin 	else if ((vp->v_flag & VVMEXEC) != 0)
4917c478bd9Sstevel@tonic-gate 		stats->ms_exec++;
4927c478bd9Sstevel@tonic-gate 	else
4937c478bd9Sstevel@tonic-gate 		stats->ms_vnode++;
4947c478bd9Sstevel@tonic-gate 
4957c478bd9Sstevel@tonic-gate 	stats->ms_total++;
4967c478bd9Sstevel@tonic-gate 
4977c478bd9Sstevel@tonic-gate 	return (WALK_NEXT);
4987c478bd9Sstevel@tonic-gate }
4997c478bd9Sstevel@tonic-gate 
5007c478bd9Sstevel@tonic-gate /* ARGSUSED */
5017c478bd9Sstevel@tonic-gate int
memstat(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)5027c478bd9Sstevel@tonic-gate memstat(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
5037c478bd9Sstevel@tonic-gate {
504a727d47aSjwadams 	pgcnt_t total_pages, physmem;
505a727d47aSjwadams 	ulong_t freemem;
5067c478bd9Sstevel@tonic-gate 	memstat_t stats;
5077c478bd9Sstevel@tonic-gate 	GElf_Sym sym;
508d20abfaaSPavel Tatashin 	vn_htable_t ht;
509d20abfaaSPavel Tatashin 	uintptr_t vn_size = 0;
510843e1988Sjohnlev #if defined(__i386) || defined(__amd64)
511843e1988Sjohnlev 	bln_stats_t bln_stats;
512843e1988Sjohnlev 	ssize_t bln_size;
513843e1988Sjohnlev #endif
5147c478bd9Sstevel@tonic-gate 
5157c478bd9Sstevel@tonic-gate 	bzero(&stats, sizeof (memstat_t));
5167c478bd9Sstevel@tonic-gate 
517d20abfaaSPavel Tatashin 	/*
518d20abfaaSPavel Tatashin 	 * -s size, is an internal option. It specifies the size of vn_htable.
519d20abfaaSPavel Tatashin 	 * Hash table size is set in the following order:
520d20abfaaSPavel Tatashin 	 * If user has specified the size that is larger than VN_LARGE: try it,
521d20abfaaSPavel Tatashin 	 * but if malloc failed default to VN_SMALL. Otherwise try VN_LARGE, if
522d20abfaaSPavel Tatashin 	 * failed to allocate default to VN_SMALL.
523d20abfaaSPavel Tatashin 	 * For a better efficiency of hash table it is highly recommended to
524d20abfaaSPavel Tatashin 	 * set size to a prime number.
525d20abfaaSPavel Tatashin 	 */
526d20abfaaSPavel Tatashin 	if ((flags & DCMD_ADDRSPEC) || mdb_getopts(argc, argv,
527d20abfaaSPavel Tatashin 	    's', MDB_OPT_UINTPTR, &vn_size, NULL) != argc)
5287c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
5297c478bd9Sstevel@tonic-gate 
530d20abfaaSPavel Tatashin 	/* Initialize vnode hash list and queue */
531d20abfaaSPavel Tatashin 	vn_htable_init(&ht, vn_size);
532d20abfaaSPavel Tatashin 	stats.ms_vn_htable = &ht;
533d20abfaaSPavel Tatashin 
5347c478bd9Sstevel@tonic-gate 	/* Total physical memory */
5357c478bd9Sstevel@tonic-gate 	if (mdb_readvar(&total_pages, "total_pages") == -1) {
5367c478bd9Sstevel@tonic-gate 		mdb_warn("unable to read total_pages");
5377c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
5387c478bd9Sstevel@tonic-gate 	}
5397c478bd9Sstevel@tonic-gate 
5407c478bd9Sstevel@tonic-gate 	/* Artificially limited memory */
5417c478bd9Sstevel@tonic-gate 	if (mdb_readvar(&physmem, "physmem") == -1) {
5427c478bd9Sstevel@tonic-gate 		mdb_warn("unable to read physmem");
5437c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
5447c478bd9Sstevel@tonic-gate 	}
5457c478bd9Sstevel@tonic-gate 
546af4c679fSSean McEnroe 	/* read kernel vnode array pointer */
547af4c679fSSean McEnroe 	if (mdb_lookup_by_obj(MDB_OBJ_EXEC, "kvps",
548843e1988Sjohnlev 	    (GElf_Sym *)&sym) == -1) {
549*04909c8cSJohn Levon 		mdb_warn("unable to look up kvps");
5507c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
5517c478bd9Sstevel@tonic-gate 	}
552*04909c8cSJohn Levon 	stats.ms_kvps = (struct vnode *)(uintptr_t)sym.st_value;
553ad23a2dbSjohansen 
554d20abfaaSPavel Tatashin 	/*
555d20abfaaSPavel Tatashin 	 * If physmem != total_pages, then the administrator has limited the
556d20abfaaSPavel Tatashin 	 * number of pages available in the system.  Excluded pages are
557d20abfaaSPavel Tatashin 	 * associated with the unused pages vnode.  Read this vnode so the
558d20abfaaSPavel Tatashin 	 * pages can be excluded in the page accounting.
559d20abfaaSPavel Tatashin 	 */
5607c478bd9Sstevel@tonic-gate 	if (mdb_lookup_by_obj(MDB_OBJ_EXEC, "unused_pages_vp",
561843e1988Sjohnlev 	    (GElf_Sym *)&sym) == -1) {
5627c478bd9Sstevel@tonic-gate 		mdb_warn("unable to read unused_pages_vp");
5637c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
5647c478bd9Sstevel@tonic-gate 	}
565d20abfaaSPavel Tatashin 	stats.ms_unused_vp = (struct vnode *)(uintptr_t)sym.st_value;
5667c478bd9Sstevel@tonic-gate 
567d20abfaaSPavel Tatashin 	/* walk all pages, collect statistics */
5682c687d68SToomas Soome 	if (mdb_walk("allpages", (mdb_walk_cb_t)(uintptr_t)memstat_callback,
569d20abfaaSPavel Tatashin 	    &stats) == -1) {
570d20abfaaSPavel Tatashin 		mdb_warn("can't walk memseg");
5717c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
5727c478bd9Sstevel@tonic-gate 	}
5737c478bd9Sstevel@tonic-gate 
574a727d47aSjwadams #define	MS_PCT_TOTAL(x)	((ulong_t)((((5 * total_pages) + ((x) * 1000ull))) / \
575a727d47aSjwadams 		((physmem) * 10)))
5767c478bd9Sstevel@tonic-gate 
5777c478bd9Sstevel@tonic-gate 	mdb_printf("Page Summary                Pages                MB"
5787c478bd9Sstevel@tonic-gate 	    "  %%Tot\n");
5797c478bd9Sstevel@tonic-gate 	mdb_printf("------------     ----------------  ----------------"
580843e1988Sjohnlev 	    "  ----\n");
581a727d47aSjwadams 	mdb_printf("Kernel           %16llu  %16llu  %3lu%%\n",
5827c478bd9Sstevel@tonic-gate 	    stats.ms_kmem,
583cbdcbd05SJonathan Adams 	    (uint64_t)stats.ms_kmem * PAGESIZE / (1024 * 1024),
5847c478bd9Sstevel@tonic-gate 	    MS_PCT_TOTAL(stats.ms_kmem));
5850fab61baSJonathan W Adams 
586176f4288SJoshua M. Clulow 	if (stats.ms_bootpages != 0) {
587176f4288SJoshua M. Clulow 		mdb_printf("Boot pages       %16llu  %16llu  %3lu%%\n",
588176f4288SJoshua M. Clulow 		    stats.ms_bootpages,
589176f4288SJoshua M. Clulow 		    (uint64_t)stats.ms_bootpages * PAGESIZE / (1024 * 1024),
590176f4288SJoshua M. Clulow 		    MS_PCT_TOTAL(stats.ms_bootpages));
591176f4288SJoshua M. Clulow 	}
592176f4288SJoshua M. Clulow 
593176f4288SJoshua M. Clulow 	if (stats.ms_zfs_data != 0) {
5940fab61baSJonathan W Adams 		mdb_printf("ZFS File Data    %16llu  %16llu  %3lu%%\n",
5950fab61baSJonathan W Adams 		    stats.ms_zfs_data,
596cbdcbd05SJonathan Adams 		    (uint64_t)stats.ms_zfs_data * PAGESIZE / (1024 * 1024),
5970fab61baSJonathan W Adams 		    MS_PCT_TOTAL(stats.ms_zfs_data));
598176f4288SJoshua M. Clulow 	}
5990fab61baSJonathan W Adams 
600*04909c8cSJohn Levon 	if (stats.ms_vmm_mem != 0) {
601*04909c8cSJohn Levon 		mdb_printf("VMM Memory       %16llu  %16llu  %3lu%%\n",
602*04909c8cSJohn Levon 		    stats.ms_vmm_mem,
603*04909c8cSJohn Levon 		    (uint64_t)stats.ms_vmm_mem * PAGESIZE / (1024 * 1024),
604*04909c8cSJohn Levon 		    MS_PCT_TOTAL(stats.ms_vmm_mem));
605*04909c8cSJohn Levon 	}
606*04909c8cSJohn Levon 
607a727d47aSjwadams 	mdb_printf("Anon             %16llu  %16llu  %3lu%%\n",
6087c478bd9Sstevel@tonic-gate 	    stats.ms_anon,
609cbdcbd05SJonathan Adams 	    (uint64_t)stats.ms_anon * PAGESIZE / (1024 * 1024),
6107c478bd9Sstevel@tonic-gate 	    MS_PCT_TOTAL(stats.ms_anon));
611a727d47aSjwadams 	mdb_printf("Exec and libs    %16llu  %16llu  %3lu%%\n",
6127c478bd9Sstevel@tonic-gate 	    stats.ms_exec,
613cbdcbd05SJonathan Adams 	    (uint64_t)stats.ms_exec * PAGESIZE / (1024 * 1024),
6147c478bd9Sstevel@tonic-gate 	    MS_PCT_TOTAL(stats.ms_exec));
615a727d47aSjwadams 	mdb_printf("Page cache       %16llu  %16llu  %3lu%%\n",
6167c478bd9Sstevel@tonic-gate 	    stats.ms_vnode,
617cbdcbd05SJonathan Adams 	    (uint64_t)stats.ms_vnode * PAGESIZE / (1024 * 1024),
6187c478bd9Sstevel@tonic-gate 	    MS_PCT_TOTAL(stats.ms_vnode));
619a727d47aSjwadams 	mdb_printf("Free (cachelist) %16llu  %16llu  %3lu%%\n",
6207c478bd9Sstevel@tonic-gate 	    stats.ms_cachelist,
621cbdcbd05SJonathan Adams 	    (uint64_t)stats.ms_cachelist * PAGESIZE / (1024 * 1024),
6227c478bd9Sstevel@tonic-gate 	    MS_PCT_TOTAL(stats.ms_cachelist));
623843e1988Sjohnlev 
624a727d47aSjwadams 	/*
625a727d47aSjwadams 	 * occasionally, we double count pages above.  To avoid printing
626a727d47aSjwadams 	 * absurdly large values for freemem, we clamp it at zero.
627a727d47aSjwadams 	 */
628a727d47aSjwadams 	if (physmem > stats.ms_total)
629a727d47aSjwadams 		freemem = physmem - stats.ms_total;
630a727d47aSjwadams 	else
631a727d47aSjwadams 		freemem = 0;
632843e1988Sjohnlev 
633843e1988Sjohnlev #if defined(__i386) || defined(__amd64)
634843e1988Sjohnlev 	/* Are we running under Xen?  If so, get balloon memory usage. */
635843e1988Sjohnlev 	if ((bln_size = mdb_readvar(&bln_stats, "bln_stats")) != -1) {
636a727d47aSjwadams 		if (freemem > bln_stats.bln_hv_pages)
637a727d47aSjwadams 			freemem -= bln_stats.bln_hv_pages;
638a727d47aSjwadams 		else
639a727d47aSjwadams 			freemem = 0;
640843e1988Sjohnlev 	}
641843e1988Sjohnlev #endif
642843e1988Sjohnlev 
643a727d47aSjwadams 	mdb_printf("Free (freelist)  %16lu  %16llu  %3lu%%\n", freemem,
644cbdcbd05SJonathan Adams 	    (uint64_t)freemem * PAGESIZE / (1024 * 1024),
645843e1988Sjohnlev 	    MS_PCT_TOTAL(freemem));
646843e1988Sjohnlev 
647843e1988Sjohnlev #if defined(__i386) || defined(__amd64)
648843e1988Sjohnlev 	if (bln_size != -1) {
649a727d47aSjwadams 		mdb_printf("Balloon          %16lu  %16llu  %3lu%%\n",
650843e1988Sjohnlev 		    bln_stats.bln_hv_pages,
651cbdcbd05SJonathan Adams 		    (uint64_t)bln_stats.bln_hv_pages * PAGESIZE / (1024 * 1024),
652843e1988Sjohnlev 		    MS_PCT_TOTAL(bln_stats.bln_hv_pages));
653843e1988Sjohnlev 	}
654843e1988Sjohnlev #endif
655843e1988Sjohnlev 
6567c478bd9Sstevel@tonic-gate 	mdb_printf("\nTotal            %16lu  %16lu\n",
6577c478bd9Sstevel@tonic-gate 	    physmem,
658cbdcbd05SJonathan Adams 	    (uint64_t)physmem * PAGESIZE / (1024 * 1024));
6597c478bd9Sstevel@tonic-gate 
6607c478bd9Sstevel@tonic-gate 	if (physmem != total_pages) {
6617c478bd9Sstevel@tonic-gate 		mdb_printf("Physical         %16lu  %16lu\n",
6627c478bd9Sstevel@tonic-gate 		    total_pages,
663cbdcbd05SJonathan Adams 		    (uint64_t)total_pages * PAGESIZE / (1024 * 1024));
6647c478bd9Sstevel@tonic-gate 	}
6657c478bd9Sstevel@tonic-gate 
6667c478bd9Sstevel@tonic-gate #undef MS_PCT_TOTAL
6677c478bd9Sstevel@tonic-gate 
6687c478bd9Sstevel@tonic-gate 	return (DCMD_OK);
6697c478bd9Sstevel@tonic-gate }
6707c478bd9Sstevel@tonic-gate 
671cbdcbd05SJonathan Adams void
pagelookup_help(void)672cbdcbd05SJonathan Adams pagelookup_help(void)
673cbdcbd05SJonathan Adams {
674cbdcbd05SJonathan Adams 	mdb_printf(
675cbdcbd05SJonathan Adams 	    "Finds the page with name { %<b>vp%</b>, %<b>offset%</b> }.\n"
676cbdcbd05SJonathan Adams 	    "\n"
677cbdcbd05SJonathan Adams 	    "Can be invoked three different ways:\n\n"
678cbdcbd05SJonathan Adams 	    "    ::pagelookup -v %<b>vp%</b> -o %<b>offset%</b>\n"
679cbdcbd05SJonathan Adams 	    "    %<b>vp%</b>::pagelookup -o %<b>offset%</b>\n"
680cbdcbd05SJonathan Adams 	    "    %<b>offset%</b>::pagelookup -v %<b>vp%</b>\n"
681cbdcbd05SJonathan Adams 	    "\n"
68256f33205SJonathan Adams 	    "The latter two forms are useful in pipelines.\n");
683cbdcbd05SJonathan Adams }
684cbdcbd05SJonathan Adams 
685cbdcbd05SJonathan Adams int
pagelookup(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)686cbdcbd05SJonathan Adams pagelookup(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
687cbdcbd05SJonathan Adams {
688cbdcbd05SJonathan Adams 	uintptr_t vp = -(uintptr_t)1;
689cbdcbd05SJonathan Adams 	uint64_t offset = -(uint64_t)1;
690cbdcbd05SJonathan Adams 
691cbdcbd05SJonathan Adams 	uintptr_t pageaddr;
692cbdcbd05SJonathan Adams 	int hasaddr = (flags & DCMD_ADDRSPEC);
693cbdcbd05SJonathan Adams 	int usedaddr = 0;
694cbdcbd05SJonathan Adams 
695cbdcbd05SJonathan Adams 	if (mdb_getopts(argc, argv,
696cbdcbd05SJonathan Adams 	    'v', MDB_OPT_UINTPTR, &vp,
697cbdcbd05SJonathan Adams 	    'o', MDB_OPT_UINT64, &offset,
6983b442230SJordan Paige Hendricks 	    NULL) != argc) {
699cbdcbd05SJonathan Adams 		return (DCMD_USAGE);
700cbdcbd05SJonathan Adams 	}
701cbdcbd05SJonathan Adams 
702cbdcbd05SJonathan Adams 	if (vp == -(uintptr_t)1) {
703cbdcbd05SJonathan Adams 		if (offset == -(uint64_t)1) {
704cbdcbd05SJonathan Adams 			mdb_warn(
705cbdcbd05SJonathan Adams 			    "pagelookup: at least one of -v vp or -o offset "
706cbdcbd05SJonathan Adams 			    "required.\n");
707cbdcbd05SJonathan Adams 			return (DCMD_USAGE);
708cbdcbd05SJonathan Adams 		}
709cbdcbd05SJonathan Adams 		vp = addr;
710cbdcbd05SJonathan Adams 		usedaddr = 1;
711cbdcbd05SJonathan Adams 	} else if (offset == -(uint64_t)1) {
712cbdcbd05SJonathan Adams 		offset = mdb_get_dot();
713cbdcbd05SJonathan Adams 		usedaddr = 1;
714cbdcbd05SJonathan Adams 	}
715cbdcbd05SJonathan Adams 	if (usedaddr && !hasaddr) {
716cbdcbd05SJonathan Adams 		mdb_warn("pagelookup: address required\n");
717cbdcbd05SJonathan Adams 		return (DCMD_USAGE);
718cbdcbd05SJonathan Adams 	}
719cbdcbd05SJonathan Adams 	if (!usedaddr && hasaddr) {
720cbdcbd05SJonathan Adams 		mdb_warn(
721cbdcbd05SJonathan Adams 		    "pagelookup: address specified when both -v and -o were "
722cbdcbd05SJonathan Adams 		    "passed");
723cbdcbd05SJonathan Adams 		return (DCMD_USAGE);
724cbdcbd05SJonathan Adams 	}
725cbdcbd05SJonathan Adams 
726cbdcbd05SJonathan Adams 	pageaddr = mdb_page_lookup(vp, offset);
727cbdcbd05SJonathan Adams 	if (pageaddr == 0) {
728cbdcbd05SJonathan Adams 		mdb_warn("pagelookup: no page for {vp = %p, offset = %llp)\n",
729cbdcbd05SJonathan Adams 		    vp, offset);
730cbdcbd05SJonathan Adams 		return (DCMD_OK);
731cbdcbd05SJonathan Adams 	}
732cbdcbd05SJonathan Adams 	mdb_printf("%#lr\n", pageaddr);		/* this is PIPE_OUT friendly */
733cbdcbd05SJonathan Adams 	return (DCMD_OK);
734cbdcbd05SJonathan Adams }
735cbdcbd05SJonathan Adams 
736cbdcbd05SJonathan Adams /*ARGSUSED*/
737cbdcbd05SJonathan Adams int
page_num2pp(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)738cbdcbd05SJonathan Adams page_num2pp(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
739cbdcbd05SJonathan Adams {
740cbdcbd05SJonathan Adams 	uintptr_t pp;
741cbdcbd05SJonathan Adams 
742cbdcbd05SJonathan Adams 	if (argc != 0 || !(flags & DCMD_ADDRSPEC)) {
743cbdcbd05SJonathan Adams 		return (DCMD_USAGE);
744cbdcbd05SJonathan Adams 	}
745cbdcbd05SJonathan Adams 
746cbdcbd05SJonathan Adams 	pp = mdb_pfn2page((pfn_t)addr);
747cbdcbd05SJonathan Adams 	if (pp == 0) {
748cbdcbd05SJonathan Adams 		return (DCMD_ERR);
749cbdcbd05SJonathan Adams 	}
750cbdcbd05SJonathan Adams 
751cbdcbd05SJonathan Adams 	if (flags & DCMD_PIPE_OUT) {
752cbdcbd05SJonathan Adams 		mdb_printf("%#lr\n", pp);
753cbdcbd05SJonathan Adams 	} else {
754cbdcbd05SJonathan Adams 		mdb_printf("%lx has page_t at %#lx\n", (pfn_t)addr, pp);
755cbdcbd05SJonathan Adams 	}
756cbdcbd05SJonathan Adams 
757cbdcbd05SJonathan Adams 	return (DCMD_OK);
758cbdcbd05SJonathan Adams }
759cbdcbd05SJonathan Adams 
7607c478bd9Sstevel@tonic-gate int
page(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)7617c478bd9Sstevel@tonic-gate page(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
7627c478bd9Sstevel@tonic-gate {
7637c478bd9Sstevel@tonic-gate 	page_t	p;
7647c478bd9Sstevel@tonic-gate 
7657c478bd9Sstevel@tonic-gate 	if (!(flags & DCMD_ADDRSPEC)) {
7667c478bd9Sstevel@tonic-gate 		if (mdb_walk_dcmd("page", "page", argc, argv) == -1) {
7677c478bd9Sstevel@tonic-gate 			mdb_warn("can't walk pages");
7687c478bd9Sstevel@tonic-gate 			return (DCMD_ERR);
7697c478bd9Sstevel@tonic-gate 		}
7707c478bd9Sstevel@tonic-gate 		return (DCMD_OK);
7717c478bd9Sstevel@tonic-gate 	}
7727c478bd9Sstevel@tonic-gate 
7737c478bd9Sstevel@tonic-gate 	if (DCMD_HDRSPEC(flags)) {
7747c478bd9Sstevel@tonic-gate 		mdb_printf("%<u>%?s %?s %16s %8s %3s %3s %2s %2s %2s%</u>\n",
7757c478bd9Sstevel@tonic-gate 		    "PAGE", "VNODE", "OFFSET", "SELOCK",
7767c478bd9Sstevel@tonic-gate 		    "LCT", "COW", "IO", "FS", "ST");
7777c478bd9Sstevel@tonic-gate 	}
7787c478bd9Sstevel@tonic-gate 
7797c478bd9Sstevel@tonic-gate 	if (mdb_vread(&p, sizeof (page_t), addr) == -1) {
7807c478bd9Sstevel@tonic-gate 		mdb_warn("can't read page_t at %#lx", addr);
7817c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
7827c478bd9Sstevel@tonic-gate 	}
7837c478bd9Sstevel@tonic-gate 
7847c478bd9Sstevel@tonic-gate 	mdb_printf("%0?lx %?p %16llx %8x %3d %3d %2x %2x %2x\n",
7857c478bd9Sstevel@tonic-gate 	    addr, p.p_vnode, p.p_offset, p.p_selock, p.p_lckcnt, p.p_cowcnt,
7867c478bd9Sstevel@tonic-gate 	    p.p_iolock_state, p.p_fsdata, p.p_state);
7877c478bd9Sstevel@tonic-gate 
7887c478bd9Sstevel@tonic-gate 	return (DCMD_OK);
7897c478bd9Sstevel@tonic-gate }
7907c478bd9Sstevel@tonic-gate 
7917c478bd9Sstevel@tonic-gate int
swap_walk_init(mdb_walk_state_t * wsp)7927c478bd9Sstevel@tonic-gate swap_walk_init(mdb_walk_state_t *wsp)
7937c478bd9Sstevel@tonic-gate {
7947c478bd9Sstevel@tonic-gate 	void	*ptr;
7957c478bd9Sstevel@tonic-gate 
7967c478bd9Sstevel@tonic-gate 	if ((mdb_readvar(&ptr, "swapinfo") == -1) || ptr == NULL) {
7977c478bd9Sstevel@tonic-gate 		mdb_warn("swapinfo not found or invalid");
7987c478bd9Sstevel@tonic-gate 		return (WALK_ERR);
7997c478bd9Sstevel@tonic-gate 	}
8007c478bd9Sstevel@tonic-gate 
8017c478bd9Sstevel@tonic-gate 	wsp->walk_addr = (uintptr_t)ptr;
8027c478bd9Sstevel@tonic-gate 
8037c478bd9Sstevel@tonic-gate 	return (WALK_NEXT);
8047c478bd9Sstevel@tonic-gate }
8057c478bd9Sstevel@tonic-gate 
8067c478bd9Sstevel@tonic-gate int
swap_walk_step(mdb_walk_state_t * wsp)8077c478bd9Sstevel@tonic-gate swap_walk_step(mdb_walk_state_t *wsp)
8087c478bd9Sstevel@tonic-gate {
8097c478bd9Sstevel@tonic-gate 	uintptr_t	sip;
8107c478bd9Sstevel@tonic-gate 	struct swapinfo	si;
8117c478bd9Sstevel@tonic-gate 
8127c478bd9Sstevel@tonic-gate 	sip = wsp->walk_addr;
8137c478bd9Sstevel@tonic-gate 
814892ad162SToomas Soome 	if (sip == 0)
8157c478bd9Sstevel@tonic-gate 		return (WALK_DONE);
8167c478bd9Sstevel@tonic-gate 
8177c478bd9Sstevel@tonic-gate 	if (mdb_vread(&si, sizeof (struct swapinfo), sip) == -1) {
8187c478bd9Sstevel@tonic-gate 		mdb_warn("unable to read swapinfo at %#lx", sip);
8197c478bd9Sstevel@tonic-gate 		return (WALK_ERR);
8207c478bd9Sstevel@tonic-gate 	}
8217c478bd9Sstevel@tonic-gate 
8227c478bd9Sstevel@tonic-gate 	wsp->walk_addr = (uintptr_t)si.si_next;
8237c478bd9Sstevel@tonic-gate 
8247c478bd9Sstevel@tonic-gate 	return (wsp->walk_callback(sip, &si, wsp->walk_cbdata));
8257c478bd9Sstevel@tonic-gate }
8267c478bd9Sstevel@tonic-gate 
8277c478bd9Sstevel@tonic-gate int
swapinfof(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)8287c478bd9Sstevel@tonic-gate swapinfof(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
8297c478bd9Sstevel@tonic-gate {
8307c478bd9Sstevel@tonic-gate 	struct swapinfo	si;
8317c478bd9Sstevel@tonic-gate 	char		*name;
8327c478bd9Sstevel@tonic-gate 
8337c478bd9Sstevel@tonic-gate 	if (!(flags & DCMD_ADDRSPEC)) {
8347c478bd9Sstevel@tonic-gate 		if (mdb_walk_dcmd("swapinfo", "swapinfo", argc, argv) == -1) {
8357c478bd9Sstevel@tonic-gate 			mdb_warn("can't walk swapinfo");
8367c478bd9Sstevel@tonic-gate 			return (DCMD_ERR);
8377c478bd9Sstevel@tonic-gate 		}
8387c478bd9Sstevel@tonic-gate 		return (DCMD_OK);
8397c478bd9Sstevel@tonic-gate 	}
8407c478bd9Sstevel@tonic-gate 
8417c478bd9Sstevel@tonic-gate 	if (DCMD_HDRSPEC(flags)) {
8427c478bd9Sstevel@tonic-gate 		mdb_printf("%<u>%?s %?s %9s %9s %s%</u>\n",
8437c478bd9Sstevel@tonic-gate 		    "ADDR", "VNODE", "PAGES", "FREE", "NAME");
8447c478bd9Sstevel@tonic-gate 	}
8457c478bd9Sstevel@tonic-gate 
8467c478bd9Sstevel@tonic-gate 	if (mdb_vread(&si, sizeof (struct swapinfo), addr) == -1) {
8477c478bd9Sstevel@tonic-gate 		mdb_warn("can't read swapinfo at %#lx", addr);
8487c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
8497c478bd9Sstevel@tonic-gate 	}
8507c478bd9Sstevel@tonic-gate 
8517c478bd9Sstevel@tonic-gate 	name = mdb_alloc(si.si_pnamelen, UM_SLEEP | UM_GC);
8527c478bd9Sstevel@tonic-gate 	if (mdb_vread(name, si.si_pnamelen, (uintptr_t)si.si_pname) == -1)
8537c478bd9Sstevel@tonic-gate 		name = "*error*";
8547c478bd9Sstevel@tonic-gate 
8557c478bd9Sstevel@tonic-gate 	mdb_printf("%0?lx %?p %9d %9d %s\n",
8567c478bd9Sstevel@tonic-gate 	    addr, si.si_vp, si.si_npgs, si.si_nfpgs, name);
8577c478bd9Sstevel@tonic-gate 
8587c478bd9Sstevel@tonic-gate 	return (DCMD_OK);
8597c478bd9Sstevel@tonic-gate }
8607c478bd9Sstevel@tonic-gate 
8617c478bd9Sstevel@tonic-gate int
memlist_walk_step(mdb_walk_state_t * wsp)8627c478bd9Sstevel@tonic-gate memlist_walk_step(mdb_walk_state_t *wsp)
8637c478bd9Sstevel@tonic-gate {
8647c478bd9Sstevel@tonic-gate 	uintptr_t	mlp;
8657c478bd9Sstevel@tonic-gate 	struct memlist	ml;
8667c478bd9Sstevel@tonic-gate 
8677c478bd9Sstevel@tonic-gate 	mlp = wsp->walk_addr;
8687c478bd9Sstevel@tonic-gate 
869892ad162SToomas Soome 	if (mlp == 0)
8707c478bd9Sstevel@tonic-gate 		return (WALK_DONE);
8717c478bd9Sstevel@tonic-gate 
8727c478bd9Sstevel@tonic-gate 	if (mdb_vread(&ml, sizeof (struct memlist), mlp) == -1) {
8737c478bd9Sstevel@tonic-gate 		mdb_warn("unable to read memlist at %#lx", mlp);
8747c478bd9Sstevel@tonic-gate 		return (WALK_ERR);
8757c478bd9Sstevel@tonic-gate 	}
8767c478bd9Sstevel@tonic-gate 
87756f33205SJonathan Adams 	wsp->walk_addr = (uintptr_t)ml.ml_next;
8787c478bd9Sstevel@tonic-gate 
8797c478bd9Sstevel@tonic-gate 	return (wsp->walk_callback(mlp, &ml, wsp->walk_cbdata));
8807c478bd9Sstevel@tonic-gate }
8817c478bd9Sstevel@tonic-gate 
8827c478bd9Sstevel@tonic-gate int
memlist(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)8837c478bd9Sstevel@tonic-gate memlist(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
8847c478bd9Sstevel@tonic-gate {
8857c478bd9Sstevel@tonic-gate 	struct memlist	ml;
8867c478bd9Sstevel@tonic-gate 
8877c478bd9Sstevel@tonic-gate 	if (!(flags & DCMD_ADDRSPEC)) {
8887c478bd9Sstevel@tonic-gate 		uintptr_t ptr;
8897c478bd9Sstevel@tonic-gate 		uint_t list = 0;
8907c478bd9Sstevel@tonic-gate 		int i;
8917c478bd9Sstevel@tonic-gate 		static const char *lists[] = {
8927c478bd9Sstevel@tonic-gate 			"phys_install",
8937c478bd9Sstevel@tonic-gate 			"phys_avail",
8947c478bd9Sstevel@tonic-gate 			"virt_avail"
8957c478bd9Sstevel@tonic-gate 		};
8967c478bd9Sstevel@tonic-gate 
8977c478bd9Sstevel@tonic-gate 		if (mdb_getopts(argc, argv,
8987c478bd9Sstevel@tonic-gate 		    'i', MDB_OPT_SETBITS, (1 << 0), &list,
8997c478bd9Sstevel@tonic-gate 		    'a', MDB_OPT_SETBITS, (1 << 1), &list,
9007c478bd9Sstevel@tonic-gate 		    'v', MDB_OPT_SETBITS, (1 << 2), &list, NULL) != argc)
9017c478bd9Sstevel@tonic-gate 			return (DCMD_USAGE);
9027c478bd9Sstevel@tonic-gate 
9037c478bd9Sstevel@tonic-gate 		if (!list)
9047c478bd9Sstevel@tonic-gate 			list = 1;
9057c478bd9Sstevel@tonic-gate 
9067c478bd9Sstevel@tonic-gate 		for (i = 0; list; i++, list >>= 1) {
9077c478bd9Sstevel@tonic-gate 			if (!(list & 1))
9087c478bd9Sstevel@tonic-gate 				continue;
9097c478bd9Sstevel@tonic-gate 			if ((mdb_readvar(&ptr, lists[i]) == -1) ||
910892ad162SToomas Soome 			    (ptr == 0)) {
9117c478bd9Sstevel@tonic-gate 				mdb_warn("%s not found or invalid", lists[i]);
9127c478bd9Sstevel@tonic-gate 				return (DCMD_ERR);
9137c478bd9Sstevel@tonic-gate 			}
9147c478bd9Sstevel@tonic-gate 
9157c478bd9Sstevel@tonic-gate 			mdb_printf("%s:\n", lists[i]);
9167c478bd9Sstevel@tonic-gate 			if (mdb_pwalk_dcmd("memlist", "memlist", 0, NULL,
9177c478bd9Sstevel@tonic-gate 			    ptr) == -1) {
9187c478bd9Sstevel@tonic-gate 				mdb_warn("can't walk memlist");
9197c478bd9Sstevel@tonic-gate 				return (DCMD_ERR);
9207c478bd9Sstevel@tonic-gate 			}
9217c478bd9Sstevel@tonic-gate 		}
9227c478bd9Sstevel@tonic-gate 		return (DCMD_OK);
9237c478bd9Sstevel@tonic-gate 	}
9247c478bd9Sstevel@tonic-gate 
9257c478bd9Sstevel@tonic-gate 	if (DCMD_HDRSPEC(flags))
9267c478bd9Sstevel@tonic-gate 		mdb_printf("%<u>%?s %16s %16s%</u>\n", "ADDR", "BASE", "SIZE");
9277c478bd9Sstevel@tonic-gate 
9287c478bd9Sstevel@tonic-gate 	if (mdb_vread(&ml, sizeof (struct memlist), addr) == -1) {
9297c478bd9Sstevel@tonic-gate 		mdb_warn("can't read memlist at %#lx", addr);
9307c478bd9Sstevel@tonic-gate 		return (DCMD_ERR);
9317c478bd9Sstevel@tonic-gate 	}
9327c478bd9Sstevel@tonic-gate 
93356f33205SJonathan Adams 	mdb_printf("%0?lx %16llx %16llx\n", addr, ml.ml_address, ml.ml_size);
9347c478bd9Sstevel@tonic-gate 
9357c478bd9Sstevel@tonic-gate 	return (DCMD_OK);
9367c478bd9Sstevel@tonic-gate }
937cbdcbd05SJonathan Adams 
938cbdcbd05SJonathan Adams int
seg_walk_init(mdb_walk_state_t * wsp)939cbdcbd05SJonathan Adams seg_walk_init(mdb_walk_state_t *wsp)
940cbdcbd05SJonathan Adams {
941892ad162SToomas Soome 	if (wsp->walk_addr == 0) {
942cbdcbd05SJonathan Adams 		mdb_warn("seg walk must begin at struct as *\n");
943cbdcbd05SJonathan Adams 		return (WALK_ERR);
944cbdcbd05SJonathan Adams 	}
945cbdcbd05SJonathan Adams 
946cbdcbd05SJonathan Adams 	/*
947cbdcbd05SJonathan Adams 	 * this is really just a wrapper to AVL tree walk
948cbdcbd05SJonathan Adams 	 */
949cbdcbd05SJonathan Adams 	wsp->walk_addr = (uintptr_t)&((struct as *)wsp->walk_addr)->a_segtree;
950cbdcbd05SJonathan Adams 	return (avl_walk_init(wsp));
951cbdcbd05SJonathan Adams }
952cbdcbd05SJonathan Adams 
953cbdcbd05SJonathan Adams /*ARGSUSED*/
954cbdcbd05SJonathan Adams int
seg(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)955cbdcbd05SJonathan Adams seg(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
956cbdcbd05SJonathan Adams {
957cbdcbd05SJonathan Adams 	struct seg s;
958cbdcbd05SJonathan Adams 
959cbdcbd05SJonathan Adams 	if (argc != 0)
960cbdcbd05SJonathan Adams 		return (DCMD_USAGE);
961cbdcbd05SJonathan Adams 
962cbdcbd05SJonathan Adams 	if ((flags & DCMD_LOOPFIRST) || !(flags & DCMD_LOOP)) {
963cbdcbd05SJonathan Adams 		mdb_printf("%<u>%?s %?s %?s %?s %s%</u>\n",
964cbdcbd05SJonathan Adams 		    "SEG", "BASE", "SIZE", "DATA", "OPS");
965cbdcbd05SJonathan Adams 	}
966cbdcbd05SJonathan Adams 
967cbdcbd05SJonathan Adams 	if (mdb_vread(&s, sizeof (s), addr) == -1) {
968cbdcbd05SJonathan Adams 		mdb_warn("failed to read seg at %p", addr);
969cbdcbd05SJonathan Adams 		return (DCMD_ERR);
970cbdcbd05SJonathan Adams 	}
971cbdcbd05SJonathan Adams 
972cbdcbd05SJonathan Adams 	mdb_printf("%?p %?p %?lx %?p %a\n",
973cbdcbd05SJonathan Adams 	    addr, s.s_base, s.s_size, s.s_data, s.s_ops);
974cbdcbd05SJonathan Adams 
975cbdcbd05SJonathan Adams 	return (DCMD_OK);
976cbdcbd05SJonathan Adams }
977cbdcbd05SJonathan Adams 
978284ce987SPatrick Mooney typedef struct pmap_walk_types {
979284ce987SPatrick Mooney 	uintptr_t pwt_segvn;
980284ce987SPatrick Mooney 	uintptr_t pwt_seghole;
981284ce987SPatrick Mooney } pmap_walk_types_t;
982284ce987SPatrick Mooney 
983cbdcbd05SJonathan Adams /*ARGSUSED*/
984cbdcbd05SJonathan Adams static int
pmap_walk_count_pages(uintptr_t addr,const void * data,void * out)98564a3d88bSJonathan Adams pmap_walk_count_pages(uintptr_t addr, const void *data, void *out)
986cbdcbd05SJonathan Adams {
98764a3d88bSJonathan Adams 	pgcnt_t *nres = out;
988cbdcbd05SJonathan Adams 
98964a3d88bSJonathan Adams 	(*nres)++;
990cbdcbd05SJonathan Adams 
991cbdcbd05SJonathan Adams 	return (WALK_NEXT);
992cbdcbd05SJonathan Adams }
993cbdcbd05SJonathan Adams 
994cbdcbd05SJonathan Adams static int
pmap_walk_seg(uintptr_t addr,const struct seg * seg,const pmap_walk_types_t * types)995284ce987SPatrick Mooney pmap_walk_seg(uintptr_t addr, const struct seg *seg,
996284ce987SPatrick Mooney     const pmap_walk_types_t *types)
997cbdcbd05SJonathan Adams {
998284ce987SPatrick Mooney 	const uintptr_t ops = (uintptr_t)seg->s_ops;
999cbdcbd05SJonathan Adams 
1000cbdcbd05SJonathan Adams 	mdb_printf("%0?p %0?p %7dk", addr, seg->s_base, seg->s_size / 1024);
1001cbdcbd05SJonathan Adams 
1002284ce987SPatrick Mooney 	if (ops == types->pwt_segvn && seg->s_data != NULL) {
1003cbdcbd05SJonathan Adams 		struct segvn_data svn;
100464a3d88bSJonathan Adams 		pgcnt_t nres = 0;
1005cbdcbd05SJonathan Adams 
100664a3d88bSJonathan Adams 		svn.vp = NULL;
1007cbdcbd05SJonathan Adams 		(void) mdb_vread(&svn, sizeof (svn), (uintptr_t)seg->s_data);
1008cbdcbd05SJonathan Adams 
1009cbdcbd05SJonathan Adams 		/*
101064a3d88bSJonathan Adams 		 * Use the segvn_pages walker to find all of the in-core pages
101164a3d88bSJonathan Adams 		 * for this mapping.
1012cbdcbd05SJonathan Adams 		 */
101364a3d88bSJonathan Adams 		if (mdb_pwalk("segvn_pages", pmap_walk_count_pages, &nres,
101464a3d88bSJonathan Adams 		    (uintptr_t)seg->s_data) == -1) {
101564a3d88bSJonathan Adams 			mdb_warn("failed to walk segvn_pages (s_data=%p)",
101664a3d88bSJonathan Adams 			    seg->s_data);
101764a3d88bSJonathan Adams 		}
101864a3d88bSJonathan Adams 		mdb_printf(" %7ldk", (nres * PAGESIZE) / 1024);
1019cbdcbd05SJonathan Adams 
1020cbdcbd05SJonathan Adams 		if (svn.vp != NULL) {
1021cbdcbd05SJonathan Adams 			char buf[29];
1022cbdcbd05SJonathan Adams 
1023cbdcbd05SJonathan Adams 			mdb_vnode2path((uintptr_t)svn.vp, buf, sizeof (buf));
1024cbdcbd05SJonathan Adams 			mdb_printf(" %s", buf);
102564a3d88bSJonathan Adams 		} else {
1026cbdcbd05SJonathan Adams 			mdb_printf(" [ anon ]");
102764a3d88bSJonathan Adams 		}
1028284ce987SPatrick Mooney 	} else if (ops == types->pwt_seghole && seg->s_data != NULL) {
1029284ce987SPatrick Mooney 		seghole_data_t shd;
1030284ce987SPatrick Mooney 		char name[16];
1031284ce987SPatrick Mooney 
1032284ce987SPatrick Mooney 		(void) mdb_vread(&shd, sizeof (shd), (uintptr_t)seg->s_data);
1033284ce987SPatrick Mooney 		if (shd.shd_name == NULL || mdb_readstr(name, sizeof (name),
1034284ce987SPatrick Mooney 		    (uintptr_t)shd.shd_name) == 0) {
1035284ce987SPatrick Mooney 			name[0] = '\0';
1036284ce987SPatrick Mooney 		}
1037284ce987SPatrick Mooney 
1038284ce987SPatrick Mooney 		mdb_printf(" %8s [ hole%s%s ]", "-",
1039284ce987SPatrick Mooney 		    name[0] == '0' ? "" : ":", name);
104064a3d88bSJonathan Adams 	} else {
104164a3d88bSJonathan Adams 		mdb_printf(" %8s [ &%a ]", "?", seg->s_ops);
1042cbdcbd05SJonathan Adams 	}
1043cbdcbd05SJonathan Adams 
1044cbdcbd05SJonathan Adams 	mdb_printf("\n");
1045cbdcbd05SJonathan Adams 	return (WALK_NEXT);
1046cbdcbd05SJonathan Adams }
1047cbdcbd05SJonathan Adams 
1048cbdcbd05SJonathan Adams static int
pmap_walk_seg_quick(uintptr_t addr,const struct seg * seg,const pmap_walk_types_t * types)1049284ce987SPatrick Mooney pmap_walk_seg_quick(uintptr_t addr, const struct seg *seg,
1050284ce987SPatrick Mooney     const pmap_walk_types_t *types)
1051cbdcbd05SJonathan Adams {
1052284ce987SPatrick Mooney 	const uintptr_t ops = (uintptr_t)seg->s_ops;
1053284ce987SPatrick Mooney 
1054cbdcbd05SJonathan Adams 	mdb_printf("%0?p %0?p %7dk", addr, seg->s_base, seg->s_size / 1024);
1055cbdcbd05SJonathan Adams 
1056284ce987SPatrick Mooney 	if (ops == types->pwt_segvn && seg->s_data != NULL) {
1057cbdcbd05SJonathan Adams 		struct segvn_data svn;
1058cbdcbd05SJonathan Adams 
105964a3d88bSJonathan Adams 		svn.vp = NULL;
1060cbdcbd05SJonathan Adams 		(void) mdb_vread(&svn, sizeof (svn), (uintptr_t)seg->s_data);
1061cbdcbd05SJonathan Adams 
1062cbdcbd05SJonathan Adams 		if (svn.vp != NULL) {
1063cbdcbd05SJonathan Adams 			mdb_printf(" %0?p", svn.vp);
1064cbdcbd05SJonathan Adams 		} else {
1065cbdcbd05SJonathan Adams 			mdb_printf(" [ anon ]");
1066cbdcbd05SJonathan Adams 		}
106764a3d88bSJonathan Adams 	} else {
106864a3d88bSJonathan Adams 		mdb_printf(" [ &%a ]", seg->s_ops);
1069cbdcbd05SJonathan Adams 	}
1070cbdcbd05SJonathan Adams 
1071cbdcbd05SJonathan Adams 	mdb_printf("\n");
1072cbdcbd05SJonathan Adams 	return (WALK_NEXT);
1073cbdcbd05SJonathan Adams }
1074cbdcbd05SJonathan Adams 
1075cbdcbd05SJonathan Adams /*ARGSUSED*/
1076cbdcbd05SJonathan Adams int
pmap(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1077cbdcbd05SJonathan Adams pmap(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1078cbdcbd05SJonathan Adams {
1079cbdcbd05SJonathan Adams 	proc_t proc;
1080cbdcbd05SJonathan Adams 	uint_t quick = FALSE;
1081cbdcbd05SJonathan Adams 	mdb_walk_cb_t cb = (mdb_walk_cb_t)pmap_walk_seg;
1082284ce987SPatrick Mooney 	pmap_walk_types_t wtypes = { 0 };
1083cbdcbd05SJonathan Adams 
1084cbdcbd05SJonathan Adams 	GElf_Sym sym;
1085cbdcbd05SJonathan Adams 
1086cbdcbd05SJonathan Adams 	if (!(flags & DCMD_ADDRSPEC))
1087cbdcbd05SJonathan Adams 		return (DCMD_USAGE);
1088cbdcbd05SJonathan Adams 
1089cbdcbd05SJonathan Adams 	if (mdb_getopts(argc, argv,
1090cbdcbd05SJonathan Adams 	    'q', MDB_OPT_SETBITS, TRUE, &quick, NULL) != argc)
1091cbdcbd05SJonathan Adams 		return (DCMD_USAGE);
1092cbdcbd05SJonathan Adams 
1093cbdcbd05SJonathan Adams 	if (mdb_vread(&proc, sizeof (proc), addr) == -1) {
1094cbdcbd05SJonathan Adams 		mdb_warn("failed to read proc at %p", addr);
1095cbdcbd05SJonathan Adams 		return (DCMD_ERR);
1096cbdcbd05SJonathan Adams 	}
1097cbdcbd05SJonathan Adams 
1098cbdcbd05SJonathan Adams 	if (mdb_lookup_by_name("segvn_ops", &sym) == 0)
1099284ce987SPatrick Mooney 		wtypes.pwt_segvn = (uintptr_t)sym.st_value;
1100284ce987SPatrick Mooney 	if (mdb_lookup_by_name("seghole_ops", &sym) == 0)
1101284ce987SPatrick Mooney 		wtypes.pwt_seghole = (uintptr_t)sym.st_value;
1102cbdcbd05SJonathan Adams 
1103cbdcbd05SJonathan Adams 	mdb_printf("%?s %?s %8s ", "SEG", "BASE", "SIZE");
1104cbdcbd05SJonathan Adams 
1105cbdcbd05SJonathan Adams 	if (quick) {
1106cbdcbd05SJonathan Adams 		mdb_printf("VNODE\n");
1107cbdcbd05SJonathan Adams 		cb = (mdb_walk_cb_t)pmap_walk_seg_quick;
1108cbdcbd05SJonathan Adams 	} else {
1109cbdcbd05SJonathan Adams 		mdb_printf("%8s %s\n", "RES", "PATH");
1110cbdcbd05SJonathan Adams 	}
1111cbdcbd05SJonathan Adams 
1112284ce987SPatrick Mooney 	if (mdb_pwalk("seg", cb, (void *)&wtypes, (uintptr_t)proc.p_as) == -1) {
1113cbdcbd05SJonathan Adams 		mdb_warn("failed to walk segments of as %p", proc.p_as);
1114cbdcbd05SJonathan Adams 		return (DCMD_ERR);
1115cbdcbd05SJonathan Adams 	}
1116cbdcbd05SJonathan Adams 
1117cbdcbd05SJonathan Adams 	return (DCMD_OK);
1118cbdcbd05SJonathan Adams }
1119cbdcbd05SJonathan Adams 
1120cbdcbd05SJonathan Adams typedef struct anon_walk_data {
1121cbdcbd05SJonathan Adams 	uintptr_t *aw_levone;
1122cbdcbd05SJonathan Adams 	uintptr_t *aw_levtwo;
112364a3d88bSJonathan Adams 	size_t aw_minslot;
112464a3d88bSJonathan Adams 	size_t aw_maxslot;
112564a3d88bSJonathan Adams 	pgcnt_t aw_nlevone;
112664a3d88bSJonathan Adams 	pgcnt_t aw_levone_ndx;
112764a3d88bSJonathan Adams 	size_t aw_levtwo_ndx;
112864a3d88bSJonathan Adams 	struct anon_map	*aw_ampp;
1129cbdcbd05SJonathan Adams 	struct anon_map aw_amp;
113064a3d88bSJonathan Adams 	struct anon_hdr	aw_ahp;
113164a3d88bSJonathan Adams 	int		aw_all;	/* report all anon pointers, even NULLs */
1132cbdcbd05SJonathan Adams } anon_walk_data_t;
1133cbdcbd05SJonathan Adams 
1134cbdcbd05SJonathan Adams int
anon_walk_init_common(mdb_walk_state_t * wsp,ulong_t minslot,ulong_t maxslot)113564a3d88bSJonathan Adams anon_walk_init_common(mdb_walk_state_t *wsp, ulong_t minslot, ulong_t maxslot)
1136cbdcbd05SJonathan Adams {
1137cbdcbd05SJonathan Adams 	anon_walk_data_t *aw;
1138cbdcbd05SJonathan Adams 
1139892ad162SToomas Soome 	if (wsp->walk_addr == 0) {
1140cbdcbd05SJonathan Adams 		mdb_warn("anon walk doesn't support global walks\n");
1141cbdcbd05SJonathan Adams 		return (WALK_ERR);
1142cbdcbd05SJonathan Adams 	}
1143cbdcbd05SJonathan Adams 
1144cbdcbd05SJonathan Adams 	aw = mdb_alloc(sizeof (anon_walk_data_t), UM_SLEEP);
114564a3d88bSJonathan Adams 	aw->aw_ampp = (struct anon_map *)wsp->walk_addr;
1146cbdcbd05SJonathan Adams 
1147cbdcbd05SJonathan Adams 	if (mdb_vread(&aw->aw_amp, sizeof (aw->aw_amp), wsp->walk_addr) == -1) {
1148cbdcbd05SJonathan Adams 		mdb_warn("failed to read anon map at %p", wsp->walk_addr);
1149cbdcbd05SJonathan Adams 		mdb_free(aw, sizeof (anon_walk_data_t));
1150cbdcbd05SJonathan Adams 		return (WALK_ERR);
1151cbdcbd05SJonathan Adams 	}
1152cbdcbd05SJonathan Adams 
1153cbdcbd05SJonathan Adams 	if (mdb_vread(&aw->aw_ahp, sizeof (aw->aw_ahp),
1154cbdcbd05SJonathan Adams 	    (uintptr_t)(aw->aw_amp.ahp)) == -1) {
1155cbdcbd05SJonathan Adams 		mdb_warn("failed to read anon hdr ptr at %p", aw->aw_amp.ahp);
1156cbdcbd05SJonathan Adams 		mdb_free(aw, sizeof (anon_walk_data_t));
1157cbdcbd05SJonathan Adams 		return (WALK_ERR);
1158cbdcbd05SJonathan Adams 	}
1159cbdcbd05SJonathan Adams 
116064a3d88bSJonathan Adams 	/* update min and maxslot with the given constraints */
116164a3d88bSJonathan Adams 	maxslot = MIN(maxslot, aw->aw_ahp.size);
116264a3d88bSJonathan Adams 	minslot = MIN(minslot, maxslot);
116364a3d88bSJonathan Adams 
1164cbdcbd05SJonathan Adams 	if (aw->aw_ahp.size <= ANON_CHUNK_SIZE ||
1165cbdcbd05SJonathan Adams 	    (aw->aw_ahp.flags & ANON_ALLOC_FORCE)) {
116664a3d88bSJonathan Adams 		aw->aw_nlevone = maxslot;
116764a3d88bSJonathan Adams 		aw->aw_levone_ndx = minslot;
1168cbdcbd05SJonathan Adams 		aw->aw_levtwo = NULL;
1169cbdcbd05SJonathan Adams 	} else {
1170cbdcbd05SJonathan Adams 		aw->aw_nlevone =
117164a3d88bSJonathan Adams 		    (maxslot + ANON_CHUNK_OFF) >> ANON_CHUNK_SHIFT;
117264a3d88bSJonathan Adams 		aw->aw_levone_ndx = 0;
1173cbdcbd05SJonathan Adams 		aw->aw_levtwo =
1174cbdcbd05SJonathan Adams 		    mdb_zalloc(ANON_CHUNK_SIZE * sizeof (uintptr_t), UM_SLEEP);
1175cbdcbd05SJonathan Adams 	}
1176cbdcbd05SJonathan Adams 
1177cbdcbd05SJonathan Adams 	aw->aw_levone =
1178cbdcbd05SJonathan Adams 	    mdb_alloc(aw->aw_nlevone * sizeof (uintptr_t), UM_SLEEP);
117964a3d88bSJonathan Adams 	aw->aw_all = (wsp->walk_arg == ANON_WALK_ALL);
1180cbdcbd05SJonathan Adams 
1181cbdcbd05SJonathan Adams 	mdb_vread(aw->aw_levone, aw->aw_nlevone * sizeof (uintptr_t),
1182cbdcbd05SJonathan Adams 	    (uintptr_t)aw->aw_ahp.array_chunk);
1183cbdcbd05SJonathan Adams 
118464a3d88bSJonathan Adams 	aw->aw_levtwo_ndx = 0;
118564a3d88bSJonathan Adams 	aw->aw_minslot = minslot;
118664a3d88bSJonathan Adams 	aw->aw_maxslot = maxslot;
1187cbdcbd05SJonathan Adams 
1188cbdcbd05SJonathan Adams out:
1189cbdcbd05SJonathan Adams 	wsp->walk_data = aw;
1190cbdcbd05SJonathan Adams 	return (0);
1191cbdcbd05SJonathan Adams }
1192cbdcbd05SJonathan Adams 
1193cbdcbd05SJonathan Adams int
anon_walk_step(mdb_walk_state_t * wsp)1194cbdcbd05SJonathan Adams anon_walk_step(mdb_walk_state_t *wsp)
1195cbdcbd05SJonathan Adams {
1196cbdcbd05SJonathan Adams 	anon_walk_data_t *aw = (anon_walk_data_t *)wsp->walk_data;
1197cbdcbd05SJonathan Adams 	struct anon anon;
1198cbdcbd05SJonathan Adams 	uintptr_t anonptr;
119964a3d88bSJonathan Adams 	ulong_t slot;
1200cbdcbd05SJonathan Adams 
1201cbdcbd05SJonathan Adams 	/*
1202cbdcbd05SJonathan Adams 	 * Once we've walked through level one, we're done.
1203cbdcbd05SJonathan Adams 	 */
120464a3d88bSJonathan Adams 	if (aw->aw_levone_ndx >= aw->aw_nlevone) {
1205cbdcbd05SJonathan Adams 		return (WALK_DONE);
120664a3d88bSJonathan Adams 	}
1207cbdcbd05SJonathan Adams 
1208cbdcbd05SJonathan Adams 	if (aw->aw_levtwo == NULL) {
1209cbdcbd05SJonathan Adams 		anonptr = aw->aw_levone[aw->aw_levone_ndx];
1210cbdcbd05SJonathan Adams 		aw->aw_levone_ndx++;
1211cbdcbd05SJonathan Adams 	} else {
121264a3d88bSJonathan Adams 		if (aw->aw_levtwo_ndx == 0) {
121364a3d88bSJonathan Adams 			uintptr_t levtwoptr;
121464a3d88bSJonathan Adams 
121564a3d88bSJonathan Adams 			/* The first time through, skip to our first index. */
121664a3d88bSJonathan Adams 			if (aw->aw_levone_ndx == 0) {
121764a3d88bSJonathan Adams 				aw->aw_levone_ndx =
121864a3d88bSJonathan Adams 				    aw->aw_minslot / ANON_CHUNK_SIZE;
121964a3d88bSJonathan Adams 				aw->aw_levtwo_ndx =
122064a3d88bSJonathan Adams 				    aw->aw_minslot % ANON_CHUNK_SIZE;
122164a3d88bSJonathan Adams 			}
122264a3d88bSJonathan Adams 
122364a3d88bSJonathan Adams 			levtwoptr = (uintptr_t)aw->aw_levone[aw->aw_levone_ndx];
122464a3d88bSJonathan Adams 
1225892ad162SToomas Soome 			if (levtwoptr == 0) {
122664a3d88bSJonathan Adams 				if (!aw->aw_all) {
122764a3d88bSJonathan Adams 					aw->aw_levtwo_ndx = 0;
122864a3d88bSJonathan Adams 					aw->aw_levone_ndx++;
122964a3d88bSJonathan Adams 					return (WALK_NEXT);
123064a3d88bSJonathan Adams 				}
123164a3d88bSJonathan Adams 				bzero(aw->aw_levtwo,
123264a3d88bSJonathan Adams 				    ANON_CHUNK_SIZE * sizeof (uintptr_t));
123364a3d88bSJonathan Adams 
123464a3d88bSJonathan Adams 			} else if (mdb_vread(aw->aw_levtwo,
123564a3d88bSJonathan Adams 			    ANON_CHUNK_SIZE * sizeof (uintptr_t), levtwoptr) ==
123664a3d88bSJonathan Adams 			    -1) {
123764a3d88bSJonathan Adams 				mdb_warn("unable to read anon_map %p's "
123864a3d88bSJonathan Adams 				    "second-level map %d at %p",
123964a3d88bSJonathan Adams 				    aw->aw_ampp, aw->aw_levone_ndx,
124064a3d88bSJonathan Adams 				    levtwoptr);
124164a3d88bSJonathan Adams 				return (WALK_ERR);
124264a3d88bSJonathan Adams 			}
124364a3d88bSJonathan Adams 		}
124464a3d88bSJonathan Adams 		slot = aw->aw_levone_ndx * ANON_CHUNK_SIZE + aw->aw_levtwo_ndx;
1245cbdcbd05SJonathan Adams 		anonptr = aw->aw_levtwo[aw->aw_levtwo_ndx];
1246cbdcbd05SJonathan Adams 
124764a3d88bSJonathan Adams 		/* update the indices for next time */
124864a3d88bSJonathan Adams 		aw->aw_levtwo_ndx++;
1249cbdcbd05SJonathan Adams 		if (aw->aw_levtwo_ndx == ANON_CHUNK_SIZE) {
1250cbdcbd05SJonathan Adams 			aw->aw_levtwo_ndx = 0;
125164a3d88bSJonathan Adams 			aw->aw_levone_ndx++;
125264a3d88bSJonathan Adams 		}
1253cbdcbd05SJonathan Adams 
125464a3d88bSJonathan Adams 		/* make sure the slot # is in the requested range */
125564a3d88bSJonathan Adams 		if (slot >= aw->aw_maxslot) {
125664a3d88bSJonathan Adams 			return (WALK_DONE);
1257cbdcbd05SJonathan Adams 		}
1258cbdcbd05SJonathan Adams 	}
1259cbdcbd05SJonathan Adams 
1260892ad162SToomas Soome 	if (anonptr != 0) {
1261cbdcbd05SJonathan Adams 		mdb_vread(&anon, sizeof (anon), anonptr);
126264a3d88bSJonathan Adams 		return (wsp->walk_callback(anonptr, &anon, wsp->walk_cbdata));
126364a3d88bSJonathan Adams 	}
126464a3d88bSJonathan Adams 	if (aw->aw_all) {
1265892ad162SToomas Soome 		return (wsp->walk_callback(0, NULL, wsp->walk_cbdata));
126664a3d88bSJonathan Adams 	}
126764a3d88bSJonathan Adams 	return (WALK_NEXT);
1268cbdcbd05SJonathan Adams }
1269cbdcbd05SJonathan Adams 
1270cbdcbd05SJonathan Adams void
anon_walk_fini(mdb_walk_state_t * wsp)1271cbdcbd05SJonathan Adams anon_walk_fini(mdb_walk_state_t *wsp)
1272cbdcbd05SJonathan Adams {
1273cbdcbd05SJonathan Adams 	anon_walk_data_t *aw = (anon_walk_data_t *)wsp->walk_data;
1274cbdcbd05SJonathan Adams 
1275cbdcbd05SJonathan Adams 	if (aw->aw_levtwo != NULL)
1276cbdcbd05SJonathan Adams 		mdb_free(aw->aw_levtwo, ANON_CHUNK_SIZE * sizeof (uintptr_t));
1277cbdcbd05SJonathan Adams 
1278cbdcbd05SJonathan Adams 	mdb_free(aw->aw_levone, aw->aw_nlevone * sizeof (uintptr_t));
1279cbdcbd05SJonathan Adams 	mdb_free(aw, sizeof (anon_walk_data_t));
1280cbdcbd05SJonathan Adams }
1281cbdcbd05SJonathan Adams 
128264a3d88bSJonathan Adams int
anon_walk_init(mdb_walk_state_t * wsp)128364a3d88bSJonathan Adams anon_walk_init(mdb_walk_state_t *wsp)
128464a3d88bSJonathan Adams {
128564a3d88bSJonathan Adams 	return (anon_walk_init_common(wsp, 0, ULONG_MAX));
128664a3d88bSJonathan Adams }
128764a3d88bSJonathan Adams 
128864a3d88bSJonathan Adams int
segvn_anon_walk_init(mdb_walk_state_t * wsp)128964a3d88bSJonathan Adams segvn_anon_walk_init(mdb_walk_state_t *wsp)
129064a3d88bSJonathan Adams {
129164a3d88bSJonathan Adams 	const uintptr_t		svd_addr = wsp->walk_addr;
129264a3d88bSJonathan Adams 	uintptr_t		amp_addr;
129364a3d88bSJonathan Adams 	uintptr_t		seg_addr;
129464a3d88bSJonathan Adams 	struct segvn_data	svd;
129564a3d88bSJonathan Adams 	struct anon_map		amp;
129664a3d88bSJonathan Adams 	struct seg		seg;
129764a3d88bSJonathan Adams 
1298892ad162SToomas Soome 	if (svd_addr == 0) {
129964a3d88bSJonathan Adams 		mdb_warn("segvn_anon walk doesn't support global walks\n");
130064a3d88bSJonathan Adams 		return (WALK_ERR);
130164a3d88bSJonathan Adams 	}
130264a3d88bSJonathan Adams 	if (mdb_vread(&svd, sizeof (svd), svd_addr) == -1) {
130364a3d88bSJonathan Adams 		mdb_warn("segvn_anon walk: unable to read segvn_data at %p",
130464a3d88bSJonathan Adams 		    svd_addr);
130564a3d88bSJonathan Adams 		return (WALK_ERR);
130664a3d88bSJonathan Adams 	}
130764a3d88bSJonathan Adams 	if (svd.amp == NULL) {
130864a3d88bSJonathan Adams 		mdb_warn("segvn_anon walk: segvn_data at %p has no anon map\n",
130964a3d88bSJonathan Adams 		    svd_addr);
131064a3d88bSJonathan Adams 		return (WALK_ERR);
131164a3d88bSJonathan Adams 	}
131264a3d88bSJonathan Adams 	amp_addr = (uintptr_t)svd.amp;
131364a3d88bSJonathan Adams 	if (mdb_vread(&amp, sizeof (amp), amp_addr) == -1) {
131464a3d88bSJonathan Adams 		mdb_warn("segvn_anon walk: unable to read amp %p for "
131564a3d88bSJonathan Adams 		    "segvn_data %p", amp_addr, svd_addr);
131664a3d88bSJonathan Adams 		return (WALK_ERR);
131764a3d88bSJonathan Adams 	}
131864a3d88bSJonathan Adams 	seg_addr = (uintptr_t)svd.seg;
131964a3d88bSJonathan Adams 	if (mdb_vread(&seg, sizeof (seg), seg_addr) == -1) {
132064a3d88bSJonathan Adams 		mdb_warn("segvn_anon walk: unable to read seg %p for "
132164a3d88bSJonathan Adams 		    "segvn_data %p", seg_addr, svd_addr);
132264a3d88bSJonathan Adams 		return (WALK_ERR);
132364a3d88bSJonathan Adams 	}
132464a3d88bSJonathan Adams 	if ((seg.s_size + (svd.anon_index << PAGESHIFT)) > amp.size) {
132564a3d88bSJonathan Adams 		mdb_warn("anon map %p is too small for segment %p\n",
132664a3d88bSJonathan Adams 		    amp_addr, seg_addr);
132764a3d88bSJonathan Adams 		return (WALK_ERR);
132864a3d88bSJonathan Adams 	}
132964a3d88bSJonathan Adams 
133064a3d88bSJonathan Adams 	wsp->walk_addr = amp_addr;
133164a3d88bSJonathan Adams 	return (anon_walk_init_common(wsp,
133264a3d88bSJonathan Adams 	    svd.anon_index, svd.anon_index + (seg.s_size >> PAGESHIFT)));
133364a3d88bSJonathan Adams }
133464a3d88bSJonathan Adams 
133564a3d88bSJonathan Adams 
133664a3d88bSJonathan Adams typedef struct {
133764a3d88bSJonathan Adams 	u_offset_t		svs_offset;
133864a3d88bSJonathan Adams 	uintptr_t		svs_page;
133964a3d88bSJonathan Adams } segvn_sparse_t;
134064a3d88bSJonathan Adams #define	SEGVN_MAX_SPARSE	((128 * 1024) / sizeof (segvn_sparse_t))
134164a3d88bSJonathan Adams 
134264a3d88bSJonathan Adams typedef struct {
134364a3d88bSJonathan Adams 	uintptr_t		svw_svdp;
134464a3d88bSJonathan Adams 	struct segvn_data	svw_svd;
134564a3d88bSJonathan Adams 	struct seg		svw_seg;
134664a3d88bSJonathan Adams 	size_t			svw_walkoff;
134764a3d88bSJonathan Adams 	ulong_t			svw_anonskip;
134864a3d88bSJonathan Adams 	segvn_sparse_t		*svw_sparse;
134964a3d88bSJonathan Adams 	size_t			svw_sparse_idx;
135064a3d88bSJonathan Adams 	size_t			svw_sparse_count;
135164a3d88bSJonathan Adams 	size_t			svw_sparse_size;
135264a3d88bSJonathan Adams 	uint8_t			svw_sparse_overflow;
135364a3d88bSJonathan Adams 	uint8_t			svw_all;
135464a3d88bSJonathan Adams } segvn_walk_data_t;
135564a3d88bSJonathan Adams 
135664a3d88bSJonathan Adams static int
segvn_sparse_fill(uintptr_t addr,const void * pp_arg,void * arg)135764a3d88bSJonathan Adams segvn_sparse_fill(uintptr_t addr, const void *pp_arg, void *arg)
135864a3d88bSJonathan Adams {
135964a3d88bSJonathan Adams 	segvn_walk_data_t	*const	svw = arg;
136064a3d88bSJonathan Adams 	const page_t		*const	pp = pp_arg;
136164a3d88bSJonathan Adams 	const u_offset_t		offset = pp->p_offset;
136264a3d88bSJonathan Adams 	segvn_sparse_t		*const	cur =
136364a3d88bSJonathan Adams 	    &svw->svw_sparse[svw->svw_sparse_count];
136464a3d88bSJonathan Adams 
136564a3d88bSJonathan Adams 	/* See if the page is of interest */
136664a3d88bSJonathan Adams 	if ((u_offset_t)(offset - svw->svw_svd.offset) >= svw->svw_seg.s_size) {
136764a3d88bSJonathan Adams 		return (WALK_NEXT);
136864a3d88bSJonathan Adams 	}
136964a3d88bSJonathan Adams 	/* See if we have space for the new entry, then add it. */
137064a3d88bSJonathan Adams 	if (svw->svw_sparse_count >= svw->svw_sparse_size) {
137164a3d88bSJonathan Adams 		svw->svw_sparse_overflow = 1;
137264a3d88bSJonathan Adams 		return (WALK_DONE);
137364a3d88bSJonathan Adams 	}
137464a3d88bSJonathan Adams 	svw->svw_sparse_count++;
137564a3d88bSJonathan Adams 	cur->svs_offset = offset;
137664a3d88bSJonathan Adams 	cur->svs_page = addr;
137764a3d88bSJonathan Adams 	return (WALK_NEXT);
137864a3d88bSJonathan Adams }
137964a3d88bSJonathan Adams 
138064a3d88bSJonathan Adams static int
segvn_sparse_cmp(const void * lp,const void * rp)138164a3d88bSJonathan Adams segvn_sparse_cmp(const void *lp, const void *rp)
138264a3d88bSJonathan Adams {
138364a3d88bSJonathan Adams 	const segvn_sparse_t *const	l = lp;
138464a3d88bSJonathan Adams 	const segvn_sparse_t *const	r = rp;
138564a3d88bSJonathan Adams 
138664a3d88bSJonathan Adams 	if (l->svs_offset < r->svs_offset) {
138764a3d88bSJonathan Adams 		return (-1);
138864a3d88bSJonathan Adams 	}
138964a3d88bSJonathan Adams 	if (l->svs_offset > r->svs_offset) {
139064a3d88bSJonathan Adams 		return (1);
139164a3d88bSJonathan Adams 	}
139264a3d88bSJonathan Adams 	return (0);
139364a3d88bSJonathan Adams }
139464a3d88bSJonathan Adams 
139564a3d88bSJonathan Adams /*
139664a3d88bSJonathan Adams  * Builds on the "anon_all" walker to walk all resident pages in a segvn_data
139764a3d88bSJonathan Adams  * structure.  For segvn_datas without an anon structure, it just looks up
139864a3d88bSJonathan Adams  * pages in the vnode.  For segvn_datas with an anon structure, NULL slots
139964a3d88bSJonathan Adams  * pass through to the vnode, and non-null slots are checked for residency.
140064a3d88bSJonathan Adams  */
140164a3d88bSJonathan Adams int
segvn_pages_walk_init(mdb_walk_state_t * wsp)140264a3d88bSJonathan Adams segvn_pages_walk_init(mdb_walk_state_t *wsp)
140364a3d88bSJonathan Adams {
140464a3d88bSJonathan Adams 	segvn_walk_data_t	*svw;
140564a3d88bSJonathan Adams 	struct segvn_data	*svd;
140664a3d88bSJonathan Adams 
1407892ad162SToomas Soome 	if (wsp->walk_addr == 0) {
140864a3d88bSJonathan Adams 		mdb_warn("segvn walk doesn't support global walks\n");
140964a3d88bSJonathan Adams 		return (WALK_ERR);
141064a3d88bSJonathan Adams 	}
141164a3d88bSJonathan Adams 
141264a3d88bSJonathan Adams 	svw = mdb_zalloc(sizeof (*svw), UM_SLEEP);
141364a3d88bSJonathan Adams 	svw->svw_svdp = wsp->walk_addr;
141464a3d88bSJonathan Adams 	svw->svw_anonskip = 0;
141564a3d88bSJonathan Adams 	svw->svw_sparse_idx = 0;
141664a3d88bSJonathan Adams 	svw->svw_walkoff = 0;
141764a3d88bSJonathan Adams 	svw->svw_all = (wsp->walk_arg == SEGVN_PAGES_ALL);
141864a3d88bSJonathan Adams 
141964a3d88bSJonathan Adams 	if (mdb_vread(&svw->svw_svd, sizeof (svw->svw_svd), wsp->walk_addr) ==
142064a3d88bSJonathan Adams 	    -1) {
142164a3d88bSJonathan Adams 		mdb_warn("failed to read segvn_data at %p", wsp->walk_addr);
142264a3d88bSJonathan Adams 		mdb_free(svw, sizeof (*svw));
142364a3d88bSJonathan Adams 		return (WALK_ERR);
142464a3d88bSJonathan Adams 	}
142564a3d88bSJonathan Adams 
142664a3d88bSJonathan Adams 	svd = &svw->svw_svd;
142764a3d88bSJonathan Adams 	if (mdb_vread(&svw->svw_seg, sizeof (svw->svw_seg),
142864a3d88bSJonathan Adams 	    (uintptr_t)svd->seg) == -1) {
142964a3d88bSJonathan Adams 		mdb_warn("failed to read seg at %p (from %p)",
143064a3d88bSJonathan Adams 		    svd->seg, &((struct segvn_data *)(wsp->walk_addr))->seg);
143164a3d88bSJonathan Adams 		mdb_free(svw, sizeof (*svw));
143264a3d88bSJonathan Adams 		return (WALK_ERR);
143364a3d88bSJonathan Adams 	}
143464a3d88bSJonathan Adams 
143564a3d88bSJonathan Adams 	if (svd->amp == NULL && svd->vp == NULL) {
143664a3d88bSJonathan Adams 		/* make the walk terminate immediately;  no pages */
143764a3d88bSJonathan Adams 		svw->svw_walkoff = svw->svw_seg.s_size;
143864a3d88bSJonathan Adams 
143964a3d88bSJonathan Adams 	} else if (svd->amp == NULL &&
144064a3d88bSJonathan Adams 	    (svw->svw_seg.s_size >> PAGESHIFT) >= SEGVN_MAX_SPARSE) {
144164a3d88bSJonathan Adams 		/*
144264a3d88bSJonathan Adams 		 * If we don't have an anon pointer, and the segment is large,
144364a3d88bSJonathan Adams 		 * we try to load the in-memory pages into a fixed-size array,
144464a3d88bSJonathan Adams 		 * which is then sorted and reported directly.  This is much
144564a3d88bSJonathan Adams 		 * faster than doing a mdb_page_lookup() for each possible
144664a3d88bSJonathan Adams 		 * offset.
144764a3d88bSJonathan Adams 		 *
144864a3d88bSJonathan Adams 		 * If the allocation fails, or there are too many pages
144964a3d88bSJonathan Adams 		 * in-core, we fall back to looking up the pages individually.
145064a3d88bSJonathan Adams 		 */
145164a3d88bSJonathan Adams 		svw->svw_sparse = mdb_alloc(
145264a3d88bSJonathan Adams 		    SEGVN_MAX_SPARSE * sizeof (*svw->svw_sparse), UM_NOSLEEP);
145364a3d88bSJonathan Adams 		if (svw->svw_sparse != NULL) {
145464a3d88bSJonathan Adams 			svw->svw_sparse_size = SEGVN_MAX_SPARSE;
145564a3d88bSJonathan Adams 
145664a3d88bSJonathan Adams 			if (mdb_pwalk("page", segvn_sparse_fill, svw,
145764a3d88bSJonathan Adams 			    (uintptr_t)svd->vp) == -1 ||
145864a3d88bSJonathan Adams 			    svw->svw_sparse_overflow) {
145964a3d88bSJonathan Adams 				mdb_free(svw->svw_sparse, SEGVN_MAX_SPARSE *
146064a3d88bSJonathan Adams 				    sizeof (*svw->svw_sparse));
146164a3d88bSJonathan Adams 				svw->svw_sparse = NULL;
146264a3d88bSJonathan Adams 			} else {
146364a3d88bSJonathan Adams 				qsort(svw->svw_sparse, svw->svw_sparse_count,
146464a3d88bSJonathan Adams 				    sizeof (*svw->svw_sparse),
146564a3d88bSJonathan Adams 				    segvn_sparse_cmp);
146664a3d88bSJonathan Adams 			}
146764a3d88bSJonathan Adams 		}
146864a3d88bSJonathan Adams 
146964a3d88bSJonathan Adams 	} else if (svd->amp != NULL) {
147064a3d88bSJonathan Adams 		const char *const layer = (!svw->svw_all && svd->vp == NULL) ?
147164a3d88bSJonathan Adams 		    "segvn_anon" : "segvn_anon_all";
147264a3d88bSJonathan Adams 		/*
147364a3d88bSJonathan Adams 		 * If we're not printing all offsets, and the segvn_data has
147464a3d88bSJonathan Adams 		 * no backing VP, we can use the "segvn_anon" walker, which
147564a3d88bSJonathan Adams 		 * efficiently skips NULL slots.
147664a3d88bSJonathan Adams 		 *
147764a3d88bSJonathan Adams 		 * Otherwise, we layer over the "segvn_anon_all" walker
147864a3d88bSJonathan Adams 		 * (which reports all anon slots, even NULL ones), so that
147964a3d88bSJonathan Adams 		 * segvn_pages_walk_step() knows the precise offset for each
148064a3d88bSJonathan Adams 		 * element.  It uses that offset information to look up the
148164a3d88bSJonathan Adams 		 * backing pages for NULL anon slots.
148264a3d88bSJonathan Adams 		 */
148364a3d88bSJonathan Adams 		if (mdb_layered_walk(layer, wsp) == -1) {
148464a3d88bSJonathan Adams 			mdb_warn("segvn_pages: failed to layer \"%s\" "
148564a3d88bSJonathan Adams 			    "for segvn_data %p", layer, svw->svw_svdp);
148664a3d88bSJonathan Adams 			mdb_free(svw, sizeof (*svw));
148764a3d88bSJonathan Adams 			return (WALK_ERR);
148864a3d88bSJonathan Adams 		}
148964a3d88bSJonathan Adams 	}
149064a3d88bSJonathan Adams 
149164a3d88bSJonathan Adams 	wsp->walk_data = svw;
149264a3d88bSJonathan Adams 	return (WALK_NEXT);
149364a3d88bSJonathan Adams }
149464a3d88bSJonathan Adams 
149564a3d88bSJonathan Adams int
segvn_pages_walk_step(mdb_walk_state_t * wsp)149664a3d88bSJonathan Adams segvn_pages_walk_step(mdb_walk_state_t *wsp)
149764a3d88bSJonathan Adams {
149864a3d88bSJonathan Adams 	segvn_walk_data_t	*const	svw = wsp->walk_data;
149964a3d88bSJonathan Adams 	struct seg		*const	seg = &svw->svw_seg;
150064a3d88bSJonathan Adams 	struct segvn_data	*const	svd = &svw->svw_svd;
150164a3d88bSJonathan Adams 	uintptr_t		pp;
150264a3d88bSJonathan Adams 	page_t			page;
150364a3d88bSJonathan Adams 
150464a3d88bSJonathan Adams 	/* If we've walked off the end of the segment, we're done. */
150564a3d88bSJonathan Adams 	if (svw->svw_walkoff >= seg->s_size) {
150664a3d88bSJonathan Adams 		return (WALK_DONE);
150764a3d88bSJonathan Adams 	}
150864a3d88bSJonathan Adams 
150964a3d88bSJonathan Adams 	/*
151064a3d88bSJonathan Adams 	 * If we've got a sparse page array, just send it directly.
151164a3d88bSJonathan Adams 	 */
151264a3d88bSJonathan Adams 	if (svw->svw_sparse != NULL) {
151364a3d88bSJonathan Adams 		u_offset_t off;
151464a3d88bSJonathan Adams 
151564a3d88bSJonathan Adams 		if (svw->svw_sparse_idx >= svw->svw_sparse_count) {
1516892ad162SToomas Soome 			pp = 0;
151764a3d88bSJonathan Adams 			if (!svw->svw_all) {
151864a3d88bSJonathan Adams 				return (WALK_DONE);
151964a3d88bSJonathan Adams 			}
152064a3d88bSJonathan Adams 		} else {
152164a3d88bSJonathan Adams 			segvn_sparse_t	*const svs =
152264a3d88bSJonathan Adams 			    &svw->svw_sparse[svw->svw_sparse_idx];
152364a3d88bSJonathan Adams 			off = svs->svs_offset - svd->offset;
152464a3d88bSJonathan Adams 			if (svw->svw_all && svw->svw_walkoff != off) {
1525892ad162SToomas Soome 				pp = 0;
152664a3d88bSJonathan Adams 			} else {
152764a3d88bSJonathan Adams 				pp = svs->svs_page;
152864a3d88bSJonathan Adams 				svw->svw_sparse_idx++;
152964a3d88bSJonathan Adams 			}
153064a3d88bSJonathan Adams 		}
153164a3d88bSJonathan Adams 
1532892ad162SToomas Soome 	} else if (svd->amp == NULL || wsp->walk_addr == 0) {
153364a3d88bSJonathan Adams 		/*
153464a3d88bSJonathan Adams 		 * If there's no anon, or the anon slot is NULL, look up
153564a3d88bSJonathan Adams 		 * <vp, offset>.
153664a3d88bSJonathan Adams 		 */
153764a3d88bSJonathan Adams 		if (svd->vp != NULL) {
153864a3d88bSJonathan Adams 			pp = mdb_page_lookup((uintptr_t)svd->vp,
153964a3d88bSJonathan Adams 			    svd->offset + svw->svw_walkoff);
154064a3d88bSJonathan Adams 		} else {
1541892ad162SToomas Soome 			pp = 0;
154264a3d88bSJonathan Adams 		}
154364a3d88bSJonathan Adams 
154464a3d88bSJonathan Adams 	} else {
154564a3d88bSJonathan Adams 		const struct anon	*const	anon = wsp->walk_layer;
154664a3d88bSJonathan Adams 
154764a3d88bSJonathan Adams 		/*
154864a3d88bSJonathan Adams 		 * We have a "struct anon"; if it's not swapped out,
154964a3d88bSJonathan Adams 		 * look up the page.
155064a3d88bSJonathan Adams 		 */
155164a3d88bSJonathan Adams 		if (anon->an_vp != NULL || anon->an_off != 0) {
155264a3d88bSJonathan Adams 			pp = mdb_page_lookup((uintptr_t)anon->an_vp,
155364a3d88bSJonathan Adams 			    anon->an_off);
155464a3d88bSJonathan Adams 			if (pp == 0 && mdb_get_state() != MDB_STATE_RUNNING) {
155564a3d88bSJonathan Adams 				mdb_warn("walk segvn_pages: segvn_data %p "
155664a3d88bSJonathan Adams 				    "offset %ld, anon page <%p, %llx> not "
155764a3d88bSJonathan Adams 				    "found.\n", svw->svw_svdp, svw->svw_walkoff,
155864a3d88bSJonathan Adams 				    anon->an_vp, anon->an_off);
155964a3d88bSJonathan Adams 			}
156064a3d88bSJonathan Adams 		} else {
156164a3d88bSJonathan Adams 			if (anon->an_pvp == NULL) {
156264a3d88bSJonathan Adams 				mdb_warn("walk segvn_pages: useless struct "
156364a3d88bSJonathan Adams 				    "anon at %p\n", wsp->walk_addr);
156464a3d88bSJonathan Adams 			}
1565892ad162SToomas Soome 			pp = 0;	/* nothing at this offset */
156664a3d88bSJonathan Adams 		}
156764a3d88bSJonathan Adams 	}
156864a3d88bSJonathan Adams 
156964a3d88bSJonathan Adams 	svw->svw_walkoff += PAGESIZE;	/* Update for the next call */
1570892ad162SToomas Soome 	if (pp != 0) {
157164a3d88bSJonathan Adams 		if (mdb_vread(&page, sizeof (page_t), pp) == -1) {
157264a3d88bSJonathan Adams 			mdb_warn("unable to read page_t at %#lx", pp);
157364a3d88bSJonathan Adams 			return (WALK_ERR);
157464a3d88bSJonathan Adams 		}
157564a3d88bSJonathan Adams 		return (wsp->walk_callback(pp, &page, wsp->walk_cbdata));
157664a3d88bSJonathan Adams 	}
157764a3d88bSJonathan Adams 	if (svw->svw_all) {
1578892ad162SToomas Soome 		return (wsp->walk_callback(0, NULL, wsp->walk_cbdata));
157964a3d88bSJonathan Adams 	}
158064a3d88bSJonathan Adams 	return (WALK_NEXT);
158164a3d88bSJonathan Adams }
158264a3d88bSJonathan Adams 
158364a3d88bSJonathan Adams void
segvn_pages_walk_fini(mdb_walk_state_t * wsp)158464a3d88bSJonathan Adams segvn_pages_walk_fini(mdb_walk_state_t *wsp)
158564a3d88bSJonathan Adams {
158664a3d88bSJonathan Adams 	segvn_walk_data_t	*const	svw = wsp->walk_data;
158764a3d88bSJonathan Adams 
158864a3d88bSJonathan Adams 	if (svw->svw_sparse != NULL) {
158964a3d88bSJonathan Adams 		mdb_free(svw->svw_sparse, SEGVN_MAX_SPARSE *
159064a3d88bSJonathan Adams 		    sizeof (*svw->svw_sparse));
159164a3d88bSJonathan Adams 	}
159264a3d88bSJonathan Adams 	mdb_free(svw, sizeof (*svw));
159364a3d88bSJonathan Adams }
159464a3d88bSJonathan Adams 
1595cbdcbd05SJonathan Adams /*
1596cbdcbd05SJonathan Adams  * Grumble, grumble.
1597cbdcbd05SJonathan Adams  */
1598cbdcbd05SJonathan Adams #define	SMAP_HASHFUNC(vp, off)	\
1599cbdcbd05SJonathan Adams 	((((uintptr_t)(vp) >> 6) + ((uintptr_t)(vp) >> 3) + \
1600cbdcbd05SJonathan Adams 	((off) >> MAXBSHIFT)) & smd_hashmsk)
1601cbdcbd05SJonathan Adams 
1602cbdcbd05SJonathan Adams int
vnode2smap(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1603cbdcbd05SJonathan Adams vnode2smap(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1604cbdcbd05SJonathan Adams {
1605cbdcbd05SJonathan Adams 	long smd_hashmsk;
1606cbdcbd05SJonathan Adams 	int hash;
1607cbdcbd05SJonathan Adams 	uintptr_t offset = 0;
1608cbdcbd05SJonathan Adams 	struct smap smp;
1609cbdcbd05SJonathan Adams 	uintptr_t saddr, kaddr;
1610cbdcbd05SJonathan Adams 	uintptr_t smd_hash, smd_smap;
1611cbdcbd05SJonathan Adams 	struct seg seg;
1612cbdcbd05SJonathan Adams 
1613cbdcbd05SJonathan Adams 	if (!(flags & DCMD_ADDRSPEC))
1614cbdcbd05SJonathan Adams 		return (DCMD_USAGE);
1615cbdcbd05SJonathan Adams 
1616cbdcbd05SJonathan Adams 	if (mdb_readvar(&smd_hashmsk, "smd_hashmsk") == -1) {
1617cbdcbd05SJonathan Adams 		mdb_warn("failed to read smd_hashmsk");
1618cbdcbd05SJonathan Adams 		return (DCMD_ERR);
1619cbdcbd05SJonathan Adams 	}
1620cbdcbd05SJonathan Adams 
1621cbdcbd05SJonathan Adams 	if (mdb_readvar(&smd_hash, "smd_hash") == -1) {
1622cbdcbd05SJonathan Adams 		mdb_warn("failed to read smd_hash");
1623cbdcbd05SJonathan Adams 		return (DCMD_ERR);
1624cbdcbd05SJonathan Adams 	}
1625cbdcbd05SJonathan Adams 
1626cbdcbd05SJonathan Adams 	if (mdb_readvar(&smd_smap, "smd_smap") == -1) {
1627cbdcbd05SJonathan Adams 		mdb_warn("failed to read smd_hash");
1628cbdcbd05SJonathan Adams 		return (DCMD_ERR);
1629cbdcbd05SJonathan Adams 	}
1630cbdcbd05SJonathan Adams 
1631cbdcbd05SJonathan Adams 	if (mdb_readvar(&kaddr, "segkmap") == -1) {
1632cbdcbd05SJonathan Adams 		mdb_warn("failed to read segkmap");
1633cbdcbd05SJonathan Adams 		return (DCMD_ERR);
1634cbdcbd05SJonathan Adams 	}
1635cbdcbd05SJonathan Adams 
1636cbdcbd05SJonathan Adams 	if (mdb_vread(&seg, sizeof (seg), kaddr) == -1) {
1637cbdcbd05SJonathan Adams 		mdb_warn("failed to read segkmap at %p", kaddr);
1638cbdcbd05SJonathan Adams 		return (DCMD_ERR);
1639cbdcbd05SJonathan Adams 	}
1640cbdcbd05SJonathan Adams 
1641cbdcbd05SJonathan Adams 	if (argc != 0) {
1642cbdcbd05SJonathan Adams 		const mdb_arg_t *arg = &argv[0];
1643cbdcbd05SJonathan Adams 
1644cbdcbd05SJonathan Adams 		if (arg->a_type == MDB_TYPE_IMMEDIATE)
1645cbdcbd05SJonathan Adams 			offset = arg->a_un.a_val;
1646cbdcbd05SJonathan Adams 		else
1647cbdcbd05SJonathan Adams 			offset = (uintptr_t)mdb_strtoull(arg->a_un.a_str);
1648cbdcbd05SJonathan Adams 	}
1649cbdcbd05SJonathan Adams 
1650cbdcbd05SJonathan Adams 	hash = SMAP_HASHFUNC(addr, offset);
1651cbdcbd05SJonathan Adams 
1652cbdcbd05SJonathan Adams 	if (mdb_vread(&saddr, sizeof (saddr),
1653cbdcbd05SJonathan Adams 	    smd_hash + hash * sizeof (uintptr_t)) == -1) {
1654cbdcbd05SJonathan Adams 		mdb_warn("couldn't read smap at %p",
1655cbdcbd05SJonathan Adams 		    smd_hash + hash * sizeof (uintptr_t));
1656cbdcbd05SJonathan Adams 		return (DCMD_ERR);
1657cbdcbd05SJonathan Adams 	}
1658cbdcbd05SJonathan Adams 
1659cbdcbd05SJonathan Adams 	do {
1660cbdcbd05SJonathan Adams 		if (mdb_vread(&smp, sizeof (smp), saddr) == -1) {
1661cbdcbd05SJonathan Adams 			mdb_warn("couldn't read smap at %p", saddr);
1662cbdcbd05SJonathan Adams 			return (DCMD_ERR);
1663cbdcbd05SJonathan Adams 		}
1664cbdcbd05SJonathan Adams 
1665cbdcbd05SJonathan Adams 		if ((uintptr_t)smp.sm_vp == addr && smp.sm_off == offset) {
1666cbdcbd05SJonathan Adams 			mdb_printf("vnode %p, offs %p is smap %p, vaddr %p\n",
1667cbdcbd05SJonathan Adams 			    addr, offset, saddr, ((saddr - smd_smap) /
1668cbdcbd05SJonathan Adams 			    sizeof (smp)) * MAXBSIZE + seg.s_base);
1669cbdcbd05SJonathan Adams 			return (DCMD_OK);
1670cbdcbd05SJonathan Adams 		}
1671cbdcbd05SJonathan Adams 
1672cbdcbd05SJonathan Adams 		saddr = (uintptr_t)smp.sm_hash;
1673892ad162SToomas Soome 	} while (saddr != 0);
1674cbdcbd05SJonathan Adams 
1675cbdcbd05SJonathan Adams 	mdb_printf("no smap for vnode %p, offs %p\n", addr, offset);
1676cbdcbd05SJonathan Adams 	return (DCMD_OK);
1677cbdcbd05SJonathan Adams }
1678cbdcbd05SJonathan Adams 
1679cbdcbd05SJonathan Adams /*ARGSUSED*/
1680cbdcbd05SJonathan Adams int
addr2smap(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1681cbdcbd05SJonathan Adams addr2smap(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1682cbdcbd05SJonathan Adams {
1683cbdcbd05SJonathan Adams 	uintptr_t kaddr;
1684cbdcbd05SJonathan Adams 	struct seg seg;
1685cbdcbd05SJonathan Adams 	struct segmap_data sd;
1686cbdcbd05SJonathan Adams 
1687cbdcbd05SJonathan Adams 	if (!(flags & DCMD_ADDRSPEC))
1688cbdcbd05SJonathan Adams 		return (DCMD_USAGE);
1689cbdcbd05SJonathan Adams 
1690cbdcbd05SJonathan Adams 	if (mdb_readvar(&kaddr, "segkmap") == -1) {
1691cbdcbd05SJonathan Adams 		mdb_warn("failed to read segkmap");
1692cbdcbd05SJonathan Adams 		return (DCMD_ERR);
1693cbdcbd05SJonathan Adams 	}
1694cbdcbd05SJonathan Adams 
1695cbdcbd05SJonathan Adams 	if (mdb_vread(&seg, sizeof (seg), kaddr) == -1) {
1696cbdcbd05SJonathan Adams 		mdb_warn("failed to read segkmap at %p", kaddr);
1697cbdcbd05SJonathan Adams 		return (DCMD_ERR);
1698cbdcbd05SJonathan Adams 	}
1699cbdcbd05SJonathan Adams 
1700cbdcbd05SJonathan Adams 	if (mdb_vread(&sd, sizeof (sd), (uintptr_t)seg.s_data) == -1) {
1701cbdcbd05SJonathan Adams 		mdb_warn("failed to read segmap_data at %p", seg.s_data);
1702cbdcbd05SJonathan Adams 		return (DCMD_ERR);
1703cbdcbd05SJonathan Adams 	}
1704cbdcbd05SJonathan Adams 
1705cbdcbd05SJonathan Adams 	mdb_printf("%p is smap %p\n", addr,
1706cbdcbd05SJonathan Adams 	    ((addr - (uintptr_t)seg.s_base) >> MAXBSHIFT) *
1707cbdcbd05SJonathan Adams 	    sizeof (struct smap) + (uintptr_t)sd.smd_sm);
1708cbdcbd05SJonathan Adams 
1709cbdcbd05SJonathan Adams 	return (DCMD_OK);
1710cbdcbd05SJonathan Adams }
1711