xref: /illumos-gate/usr/src/cmd/mdb/common/modules/genunix/memory.c (revision b51e13bf985efd1ff98249cad2824f2952f13ecb)
1  /*
2   * CDDL HEADER START
3   *
4   * The contents of this file are subject to the terms of the
5   * Common Development and Distribution License (the "License").
6   * You may not use this file except in compliance with the License.
7   *
8   * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9   * or http://www.opensolaris.org/os/licensing.
10   * See the License for the specific language governing permissions
11   * and limitations under the License.
12   *
13   * When distributing Covered Code, include this CDDL HEADER in each
14   * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15   * If applicable, add the following below this CDDL HEADER, with the
16   * fields enclosed by brackets "[]" replaced with your own identifying
17   * information: Portions Copyright [yyyy] [name of copyright owner]
18   *
19   * CDDL HEADER END
20   */
21  /*
22   * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23   * Use is subject to license terms.
24   */
25  
26  #include <mdb/mdb_modapi.h>
27  #include <sys/types.h>
28  #include <vm/page.h>
29  #include <sys/thread.h>
30  #include <sys/swap.h>
31  #include <sys/memlist.h>
32  #if defined(__i386) || defined(__amd64)
33  #include <sys/balloon_impl.h>
34  #endif
35  
36  /*
37   * Page walker.
38   * By default, this will walk all pages in the system.  If given an
39   * address, it will walk all pages belonging to the vnode at that
40   * address.
41   */
42  
43  /*
44   * page_walk_data
45   *
46   * pw_hashleft is set to -1 when walking a vnode's pages, and holds the
47   * number of hash locations remaining in the page hash table when
48   * walking all pages.
49   *
50   * The astute reader will notice that pw_hashloc is only used when
51   * reading all pages (to hold a pointer to our location in the page
52   * hash table), and that pw_first is only used when reading the pages
53   * belonging to a particular vnode (to hold a pointer to the first
54   * page).  While these could be combined to be a single pointer, they
55   * are left separate for clarity.
56   */
57  typedef struct page_walk_data {
58  	long		pw_hashleft;
59  	void		**pw_hashloc;
60  	uintptr_t	pw_first;
61  } page_walk_data_t;
62  
63  int
64  page_walk_init(mdb_walk_state_t *wsp)
65  {
66  	page_walk_data_t	*pwd;
67  	void	**ptr;
68  	size_t	hashsz;
69  	vnode_t	vn;
70  
71  	if (wsp->walk_addr == NULL) {
72  
73  		/*
74  		 * Walk all pages
75  		 */
76  
77  		if ((mdb_readvar(&ptr, "page_hash") == -1) ||
78  		    (mdb_readvar(&hashsz, "page_hashsz") == -1) ||
79  		    (ptr == NULL) || (hashsz == 0)) {
80  			mdb_warn("page_hash, page_hashsz not found or invalid");
81  			return (WALK_ERR);
82  		}
83  
84  		/*
85  		 * Since we are walking all pages, initialize hashleft
86  		 * to be the remaining number of entries in the page
87  		 * hash.  hashloc is set the start of the page hash
88  		 * table.  Setting the walk address to 0 indicates that
89  		 * we aren't currently following a hash chain, and that
90  		 * we need to scan the page hash table for a page.
91  		 */
92  		pwd = mdb_alloc(sizeof (page_walk_data_t), UM_SLEEP);
93  		pwd->pw_hashleft = hashsz;
94  		pwd->pw_hashloc = ptr;
95  		wsp->walk_addr = 0;
96  	} else {
97  
98  		/*
99  		 * Walk just this vnode
100  		 */
101  
102  		if (mdb_vread(&vn, sizeof (vnode_t), wsp->walk_addr) == -1) {
103  			mdb_warn("unable to read vnode_t at %#lx",
104  			    wsp->walk_addr);
105  			return (WALK_ERR);
106  		}
107  
108  		/*
109  		 * We set hashleft to -1 to indicate that we are
110  		 * walking a vnode, and initialize first to 0 (it is
111  		 * used to terminate the walk, so it must not be set
112  		 * until after we have walked the first page).  The
113  		 * walk address is set to the first page.
114  		 */
115  		pwd = mdb_alloc(sizeof (page_walk_data_t), UM_SLEEP);
116  		pwd->pw_hashleft = -1;
117  		pwd->pw_first = 0;
118  
119  		wsp->walk_addr = (uintptr_t)vn.v_pages;
120  	}
121  
122  	wsp->walk_data = pwd;
123  
124  	return (WALK_NEXT);
125  }
126  
127  int
128  page_walk_step(mdb_walk_state_t *wsp)
129  {
130  	page_walk_data_t	*pwd = wsp->walk_data;
131  	page_t		page;
132  	uintptr_t	pp;
133  
134  	pp = wsp->walk_addr;
135  
136  	if (pwd->pw_hashleft < 0) {
137  
138  		/* We're walking a vnode's pages */
139  
140  		/*
141  		 * If we don't have any pages to walk, we have come
142  		 * back around to the first one (we finished), or we
143  		 * can't read the page we're looking at, we are done.
144  		 */
145  		if (pp == NULL || pp == pwd->pw_first)
146  			return (WALK_DONE);
147  		if (mdb_vread(&page, sizeof (page_t), pp) == -1) {
148  			mdb_warn("unable to read page_t at %#lx", pp);
149  			return (WALK_ERR);
150  		}
151  
152  		/*
153  		 * Set the walk address to the next page, and if the
154  		 * first page hasn't been set yet (i.e. we are on the
155  		 * first page), set it.
156  		 */
157  		wsp->walk_addr = (uintptr_t)page.p_vpnext;
158  		if (pwd->pw_first == NULL)
159  			pwd->pw_first = pp;
160  
161  	} else if (pwd->pw_hashleft > 0) {
162  
163  		/* We're walking all pages */
164  
165  		/*
166  		 * If pp (the walk address) is NULL, we scan through
167  		 * the page hash table until we find a page.
168  		 */
169  		if (pp == NULL) {
170  
171  			/*
172  			 * Iterate through the page hash table until we
173  			 * find a page or reach the end.
174  			 */
175  			do {
176  				if (mdb_vread(&pp, sizeof (uintptr_t),
177  				    (uintptr_t)pwd->pw_hashloc) == -1) {
178  					mdb_warn("unable to read from %#p",
179  					    pwd->pw_hashloc);
180  					return (WALK_ERR);
181  				}
182  				pwd->pw_hashleft--;
183  				pwd->pw_hashloc++;
184  			} while (pwd->pw_hashleft && (pp == NULL));
185  
186  			/*
187  			 * We've reached the end; exit.
188  			 */
189  			if (pp == NULL)
190  				return (WALK_DONE);
191  		}
192  
193  		if (mdb_vread(&page, sizeof (page_t), pp) == -1) {
194  			mdb_warn("unable to read page_t at %#lx", pp);
195  			return (WALK_ERR);
196  		}
197  
198  		/*
199  		 * Set the walk address to the next page.
200  		 */
201  		wsp->walk_addr = (uintptr_t)page.p_hash;
202  
203  	} else {
204  		/* We've finished walking all pages. */
205  		return (WALK_DONE);
206  	}
207  
208  	return (wsp->walk_callback(pp, &page, wsp->walk_cbdata));
209  }
210  
211  void
212  page_walk_fini(mdb_walk_state_t *wsp)
213  {
214  	mdb_free(wsp->walk_data, sizeof (page_walk_data_t));
215  }
216  
217  /*
218   * allpages walks all pages in the system in order they appear in
219   * the memseg structure
220   */
221  
222  #define	PAGE_BUFFER	128
223  
224  int
225  allpages_walk_init(mdb_walk_state_t *wsp)
226  {
227  	if (wsp->walk_addr != 0) {
228  		mdb_warn("allpages only supports global walks.\n");
229  		return (WALK_ERR);
230  	}
231  
232  	if (mdb_layered_walk("memseg", wsp) == -1) {
233  		mdb_warn("couldn't walk 'memseg'");
234  		return (WALK_ERR);
235  	}
236  
237  	wsp->walk_data = mdb_alloc(sizeof (page_t) * PAGE_BUFFER, UM_SLEEP);
238  	return (WALK_NEXT);
239  }
240  
241  int
242  allpages_walk_step(mdb_walk_state_t *wsp)
243  {
244  	const struct memseg *msp = wsp->walk_layer;
245  	page_t *buf = wsp->walk_data;
246  	size_t pg_read, i;
247  	size_t pg_num = msp->pages_end - msp->pages_base;
248  	const page_t *pg_addr = msp->pages;
249  
250  	while (pg_num > 0) {
251  		pg_read = MIN(pg_num, PAGE_BUFFER);
252  
253  		if (mdb_vread(buf, pg_read * sizeof (page_t),
254  		    (uintptr_t)pg_addr) == -1) {
255  			mdb_warn("can't read page_t's at %#lx", pg_addr);
256  			return (WALK_ERR);
257  		}
258  		for (i = 0; i < pg_read; i++) {
259  			int ret = wsp->walk_callback((uintptr_t)&pg_addr[i],
260  			    &buf[i], wsp->walk_cbdata);
261  
262  			if (ret != WALK_NEXT)
263  				return (ret);
264  		}
265  		pg_num -= pg_read;
266  		pg_addr += pg_read;
267  	}
268  
269  	return (WALK_NEXT);
270  }
271  
272  void
273  allpages_walk_fini(mdb_walk_state_t *wsp)
274  {
275  	mdb_free(wsp->walk_data, sizeof (page_t) * PAGE_BUFFER);
276  }
277  
278  /*
279   * Hash table + LRU queue.
280   * This table is used to cache recently read vnodes for the memstat
281   * command, to reduce the number of mdb_vread calls.  This greatly
282   * speeds the memstat command on on live, large CPU count systems.
283   */
284  
285  #define	VN_SMALL	401
286  #define	VN_LARGE	10007
287  #define	VN_HTABLE_KEY(p, hp)	((p) % ((hp)->vn_htable_buckets))
288  
289  struct vn_htable_list {
290  	uint_t vn_flag;				/* v_flag from vnode	*/
291  	uintptr_t vn_ptr;			/* pointer to vnode	*/
292  	struct vn_htable_list *vn_q_next;	/* queue next pointer	*/
293  	struct vn_htable_list *vn_q_prev;	/* queue prev pointer	*/
294  	struct vn_htable_list *vn_h_next;	/* hash table pointer	*/
295  };
296  
297  /*
298   * vn_q_first        -> points to to head of queue: the vnode that was most
299   *                      recently used
300   * vn_q_last         -> points to the oldest used vnode, and is freed once a new
301   *                      vnode is read.
302   * vn_htable         -> hash table
303   * vn_htable_buf     -> contains htable objects
304   * vn_htable_size    -> total number of items in the hash table
305   * vn_htable_buckets -> number of buckets in the hash table
306   */
307  typedef struct vn_htable {
308  	struct vn_htable_list  *vn_q_first;
309  	struct vn_htable_list  *vn_q_last;
310  	struct vn_htable_list **vn_htable;
311  	struct vn_htable_list  *vn_htable_buf;
312  	int vn_htable_size;
313  	int vn_htable_buckets;
314  } vn_htable_t;
315  
316  
317  /* allocate memory, initilize hash table and LRU queue */
318  static void
319  vn_htable_init(vn_htable_t *hp, size_t vn_size)
320  {
321  	int i;
322  	int htable_size = MAX(vn_size, VN_LARGE);
323  
324  	if ((hp->vn_htable_buf = mdb_zalloc(sizeof (struct vn_htable_list)
325  	    * htable_size, UM_NOSLEEP|UM_GC)) == NULL) {
326  		htable_size = VN_SMALL;
327  		hp->vn_htable_buf = mdb_zalloc(sizeof (struct vn_htable_list)
328  		    * htable_size, UM_SLEEP|UM_GC);
329  	}
330  
331  	hp->vn_htable = mdb_zalloc(sizeof (struct vn_htable_list *)
332  	    * htable_size, UM_SLEEP|UM_GC);
333  
334  	hp->vn_q_first  = &hp->vn_htable_buf[0];
335  	hp->vn_q_last   = &hp->vn_htable_buf[htable_size - 1];
336  	hp->vn_q_first->vn_q_next = &hp->vn_htable_buf[1];
337  	hp->vn_q_last->vn_q_prev = &hp->vn_htable_buf[htable_size - 2];
338  
339  	for (i = 1; i < (htable_size-1); i++) {
340  		hp->vn_htable_buf[i].vn_q_next = &hp->vn_htable_buf[i + 1];
341  		hp->vn_htable_buf[i].vn_q_prev = &hp->vn_htable_buf[i - 1];
342  	}
343  
344  	hp->vn_htable_size = htable_size;
345  	hp->vn_htable_buckets = htable_size;
346  }
347  
348  
349  /*
350   * Find the vnode whose address is ptr, and return its v_flag in vp->v_flag.
351   * The function tries to find needed information in the following order:
352   *
353   * 1. check if ptr is the first in queue
354   * 2. check if ptr is in hash table (if so move it to the top of queue)
355   * 3. do mdb_vread, remove last queue item from queue and hash table.
356   *    Insert new information to freed object, and put this object in to the
357   *    top of the queue.
358   */
359  static int
360  vn_get(vn_htable_t *hp, struct vnode *vp, uintptr_t ptr)
361  {
362  	int hkey;
363  	struct vn_htable_list *hent, **htmp, *q_next, *q_prev;
364  	struct vn_htable_list  *q_first = hp->vn_q_first;
365  
366  	/* 1. vnode ptr is the first in queue, just get v_flag and return */
367  	if (q_first->vn_ptr == ptr) {
368  		vp->v_flag = q_first->vn_flag;
369  
370  		return (0);
371  	}
372  
373  	/* 2. search the hash table for this ptr */
374  	hkey = VN_HTABLE_KEY(ptr, hp);
375  	hent = hp->vn_htable[hkey];
376  	while (hent && (hent->vn_ptr != ptr))
377  		hent = hent->vn_h_next;
378  
379  	/* 3. if hent is NULL, we did not find in hash table, do mdb_vread */
380  	if (hent == NULL) {
381  		struct vnode vn;
382  
383  		if (mdb_vread(&vn, sizeof (vnode_t), ptr) == -1) {
384  			mdb_warn("unable to read vnode_t at %#lx", ptr);
385  			return (-1);
386  		}
387  
388  		/* we will insert read data into the last element in queue */
389  		hent = hp->vn_q_last;
390  
391  		/* remove last hp->vn_q_last object from hash table */
392  		if (hent->vn_ptr) {
393  			htmp = &hp->vn_htable[VN_HTABLE_KEY(hent->vn_ptr, hp)];
394  			while (*htmp != hent)
395  				htmp = &(*htmp)->vn_h_next;
396  			*htmp = hent->vn_h_next;
397  		}
398  
399  		/* insert data into new free object */
400  		hent->vn_ptr  = ptr;
401  		hent->vn_flag = vn.v_flag;
402  
403  		/* insert new object into hash table */
404  		hent->vn_h_next = hp->vn_htable[hkey];
405  		hp->vn_htable[hkey] = hent;
406  	}
407  
408  	/* Remove from queue. hent is not first, vn_q_prev is not NULL */
409  	q_next = hent->vn_q_next;
410  	q_prev = hent->vn_q_prev;
411  	if (q_next == NULL)
412  		hp->vn_q_last = q_prev;
413  	else
414  		q_next->vn_q_prev = q_prev;
415  	q_prev->vn_q_next = q_next;
416  
417  	/* Add to the front of queue */
418  	hent->vn_q_prev = NULL;
419  	hent->vn_q_next = q_first;
420  	q_first->vn_q_prev = hent;
421  	hp->vn_q_first = hent;
422  
423  	/* Set v_flag in vnode pointer from hent */
424  	vp->v_flag = hent->vn_flag;
425  
426  	return (0);
427  }
428  
429  /* Summary statistics of pages */
430  typedef struct memstat {
431  	struct vnode    *ms_kvp;	/* Cached address of kernel vnode */
432  	struct vnode    *ms_unused_vp;	/* Unused pages vnode pointer	  */
433  	struct vnode    *ms_zvp;	/* Cached address of zio vnode    */
434  	uint64_t	ms_kmem;	/* Pages of kernel memory	  */
435  	uint64_t	ms_zfs_data;	/* Pages of zfs data		  */
436  	uint64_t	ms_anon;	/* Pages of anonymous memory	  */
437  	uint64_t	ms_vnode;	/* Pages of named (vnode) memory  */
438  	uint64_t	ms_exec;	/* Pages of exec/library memory	  */
439  	uint64_t	ms_cachelist;	/* Pages on the cachelist (free)  */
440  	uint64_t	ms_total;	/* Pages on page hash		  */
441  	vn_htable_t	*ms_vn_htable;	/* Pointer to hash table	  */
442  	struct vnode	ms_vn;		/* vnode buffer			  */
443  } memstat_t;
444  
445  #define	MS_PP_ISKAS(pp, stats)				\
446  	((pp)->p_vnode == (stats)->ms_kvp)
447  
448  #define	MS_PP_ISZFS_DATA(pp, stats)			\
449  	(((stats)->ms_zvp != NULL) && ((pp)->p_vnode == (stats)->ms_zvp))
450  
451  /*
452   * Summarize pages by type and update stat information
453   */
454  
455  /* ARGSUSED */
456  static int
457  memstat_callback(page_t *page, page_t *pp, memstat_t *stats)
458  {
459  	struct vnode *vp = &stats->ms_vn;
460  
461  	if (pp->p_vnode == NULL || pp->p_vnode == stats->ms_unused_vp)
462  		return (WALK_NEXT);
463  	else if (MS_PP_ISKAS(pp, stats))
464  		stats->ms_kmem++;
465  	else if (MS_PP_ISZFS_DATA(pp, stats))
466  		stats->ms_zfs_data++;
467  	else if (PP_ISFREE(pp))
468  		stats->ms_cachelist++;
469  	else if (vn_get(stats->ms_vn_htable, vp, (uintptr_t)pp->p_vnode))
470  		return (WALK_ERR);
471  	else if (IS_SWAPFSVP(vp))
472  		stats->ms_anon++;
473  	else if ((vp->v_flag & VVMEXEC) != 0)
474  		stats->ms_exec++;
475  	else
476  		stats->ms_vnode++;
477  
478  	stats->ms_total++;
479  
480  	return (WALK_NEXT);
481  }
482  
483  /* ARGSUSED */
484  int
485  memstat(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
486  {
487  	ulong_t pagesize;
488  	pgcnt_t total_pages, physmem;
489  	ulong_t freemem;
490  	memstat_t stats;
491  	GElf_Sym sym;
492  	vn_htable_t ht;
493  	uintptr_t vn_size = 0;
494  #if defined(__i386) || defined(__amd64)
495  	bln_stats_t bln_stats;
496  	ssize_t bln_size;
497  #endif
498  
499  	bzero(&stats, sizeof (memstat_t));
500  
501  	/*
502  	 * -s size, is an internal option. It specifies the size of vn_htable.
503  	 * Hash table size is set in the following order:
504  	 * If user has specified the size that is larger than VN_LARGE: try it,
505  	 * but if malloc failed default to VN_SMALL. Otherwise try VN_LARGE, if
506  	 * failed to allocate default to VN_SMALL.
507  	 * For a better efficiency of hash table it is highly recommended to
508  	 * set size to a prime number.
509  	 */
510  	if ((flags & DCMD_ADDRSPEC) || mdb_getopts(argc, argv,
511  	    's', MDB_OPT_UINTPTR, &vn_size, NULL) != argc)
512  		return (DCMD_USAGE);
513  
514  	/* Initialize vnode hash list and queue */
515  	vn_htable_init(&ht, vn_size);
516  	stats.ms_vn_htable = &ht;
517  
518  	/* Grab base page size */
519  	if (mdb_readvar(&pagesize, "_pagesize") == -1) {
520  		mdb_warn("unable to read _pagesize");
521  		return (DCMD_ERR);
522  	}
523  
524  	/* Total physical memory */
525  	if (mdb_readvar(&total_pages, "total_pages") == -1) {
526  		mdb_warn("unable to read total_pages");
527  		return (DCMD_ERR);
528  	}
529  
530  	/* Artificially limited memory */
531  	if (mdb_readvar(&physmem, "physmem") == -1) {
532  		mdb_warn("unable to read physmem");
533  		return (DCMD_ERR);
534  	}
535  
536  	/* read kernel vnode pointer */
537  	if (mdb_lookup_by_obj(MDB_OBJ_EXEC, "kvp",
538  	    (GElf_Sym *)&sym) == -1) {
539  		mdb_warn("unable to read kvp");
540  		return (DCMD_ERR);
541  	}
542  
543  	stats.ms_kvp = (struct vnode *)(uintptr_t)sym.st_value;
544  
545  	/*
546  	 * Read the zio vnode pointer.  It may not exist on all kernels, so it
547  	 * it isn't found, it's not a fatal error.
548  	 */
549  	if (mdb_lookup_by_obj(MDB_OBJ_EXEC, "zvp",
550  	    (GElf_Sym *)&sym) == -1) {
551  		stats.ms_zvp = NULL;
552  	} else {
553  		stats.ms_zvp = (struct vnode *)(uintptr_t)sym.st_value;
554  	}
555  
556  	/*
557  	 * If physmem != total_pages, then the administrator has limited the
558  	 * number of pages available in the system.  Excluded pages are
559  	 * associated with the unused pages vnode.  Read this vnode so the
560  	 * pages can be excluded in the page accounting.
561  	 */
562  	if (mdb_lookup_by_obj(MDB_OBJ_EXEC, "unused_pages_vp",
563  	    (GElf_Sym *)&sym) == -1) {
564  		mdb_warn("unable to read unused_pages_vp");
565  		return (DCMD_ERR);
566  	}
567  	stats.ms_unused_vp = (struct vnode *)(uintptr_t)sym.st_value;
568  
569  	/* walk all pages, collect statistics */
570  	if (mdb_walk("allpages", (mdb_walk_cb_t)memstat_callback,
571  	    &stats) == -1) {
572  		mdb_warn("can't walk memseg");
573  		return (DCMD_ERR);
574  	}
575  
576  #define	MS_PCT_TOTAL(x)	((ulong_t)((((5 * total_pages) + ((x) * 1000ull))) / \
577  		((physmem) * 10)))
578  
579  	mdb_printf("Page Summary                Pages                MB"
580  	    "  %%Tot\n");
581  	mdb_printf("------------     ----------------  ----------------"
582  	    "  ----\n");
583  	mdb_printf("Kernel           %16llu  %16llu  %3lu%%\n",
584  	    stats.ms_kmem,
585  	    (uint64_t)stats.ms_kmem * pagesize / (1024 * 1024),
586  	    MS_PCT_TOTAL(stats.ms_kmem));
587  
588  	if (stats.ms_zfs_data != 0)
589  		mdb_printf("ZFS File Data    %16llu  %16llu  %3lu%%\n",
590  		    stats.ms_zfs_data,
591  		    (uint64_t)stats.ms_zfs_data * pagesize / (1024 * 1024),
592  		    MS_PCT_TOTAL(stats.ms_zfs_data));
593  
594  	mdb_printf("Anon             %16llu  %16llu  %3lu%%\n",
595  	    stats.ms_anon,
596  	    (uint64_t)stats.ms_anon * pagesize / (1024 * 1024),
597  	    MS_PCT_TOTAL(stats.ms_anon));
598  	mdb_printf("Exec and libs    %16llu  %16llu  %3lu%%\n",
599  	    stats.ms_exec,
600  	    (uint64_t)stats.ms_exec * pagesize / (1024 * 1024),
601  	    MS_PCT_TOTAL(stats.ms_exec));
602  	mdb_printf("Page cache       %16llu  %16llu  %3lu%%\n",
603  	    stats.ms_vnode,
604  	    (uint64_t)stats.ms_vnode * pagesize / (1024 * 1024),
605  	    MS_PCT_TOTAL(stats.ms_vnode));
606  	mdb_printf("Free (cachelist) %16llu  %16llu  %3lu%%\n",
607  	    stats.ms_cachelist,
608  	    (uint64_t)stats.ms_cachelist * pagesize / (1024 * 1024),
609  	    MS_PCT_TOTAL(stats.ms_cachelist));
610  
611  	/*
612  	 * occasionally, we double count pages above.  To avoid printing
613  	 * absurdly large values for freemem, we clamp it at zero.
614  	 */
615  	if (physmem > stats.ms_total)
616  		freemem = physmem - stats.ms_total;
617  	else
618  		freemem = 0;
619  
620  #if defined(__i386) || defined(__amd64)
621  	/* Are we running under Xen?  If so, get balloon memory usage. */
622  	if ((bln_size = mdb_readvar(&bln_stats, "bln_stats")) != -1) {
623  		if (freemem > bln_stats.bln_hv_pages)
624  			freemem -= bln_stats.bln_hv_pages;
625  		else
626  			freemem = 0;
627  	}
628  #endif
629  
630  	mdb_printf("Free (freelist)  %16lu  %16llu  %3lu%%\n", freemem,
631  	    (uint64_t)freemem * pagesize / (1024 * 1024),
632  	    MS_PCT_TOTAL(freemem));
633  
634  #if defined(__i386) || defined(__amd64)
635  	if (bln_size != -1) {
636  		mdb_printf("Balloon          %16lu  %16llu  %3lu%%\n",
637  		    bln_stats.bln_hv_pages,
638  		    (uint64_t)bln_stats.bln_hv_pages * pagesize / (1024 * 1024),
639  		    MS_PCT_TOTAL(bln_stats.bln_hv_pages));
640  	}
641  #endif
642  
643  	mdb_printf("\nTotal            %16lu  %16lu\n",
644  	    physmem,
645  	    (uint64_t)physmem * pagesize / (1024 * 1024));
646  
647  	if (physmem != total_pages) {
648  		mdb_printf("Physical         %16lu  %16lu\n",
649  		    total_pages,
650  		    (uint64_t)total_pages * pagesize / (1024 * 1024));
651  	}
652  
653  #undef MS_PCT_TOTAL
654  
655  	return (DCMD_OK);
656  }
657  
658  int
659  page(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
660  {
661  	page_t	p;
662  
663  	if (!(flags & DCMD_ADDRSPEC)) {
664  		if (mdb_walk_dcmd("page", "page", argc, argv) == -1) {
665  			mdb_warn("can't walk pages");
666  			return (DCMD_ERR);
667  		}
668  		return (DCMD_OK);
669  	}
670  
671  	if (DCMD_HDRSPEC(flags)) {
672  		mdb_printf("%<u>%?s %?s %16s %8s %3s %3s %2s %2s %2s%</u>\n",
673  		    "PAGE", "VNODE", "OFFSET", "SELOCK",
674  		    "LCT", "COW", "IO", "FS", "ST");
675  	}
676  
677  	if (mdb_vread(&p, sizeof (page_t), addr) == -1) {
678  		mdb_warn("can't read page_t at %#lx", addr);
679  		return (DCMD_ERR);
680  	}
681  
682  	mdb_printf("%0?lx %?p %16llx %8x %3d %3d %2x %2x %2x\n",
683  	    addr, p.p_vnode, p.p_offset, p.p_selock, p.p_lckcnt, p.p_cowcnt,
684  	    p.p_iolock_state, p.p_fsdata, p.p_state);
685  
686  	return (DCMD_OK);
687  }
688  
689  int
690  swap_walk_init(mdb_walk_state_t *wsp)
691  {
692  	void	*ptr;
693  
694  	if ((mdb_readvar(&ptr, "swapinfo") == -1) || ptr == NULL) {
695  		mdb_warn("swapinfo not found or invalid");
696  		return (WALK_ERR);
697  	}
698  
699  	wsp->walk_addr = (uintptr_t)ptr;
700  
701  	return (WALK_NEXT);
702  }
703  
704  int
705  swap_walk_step(mdb_walk_state_t *wsp)
706  {
707  	uintptr_t	sip;
708  	struct swapinfo	si;
709  
710  	sip = wsp->walk_addr;
711  
712  	if (sip == NULL)
713  		return (WALK_DONE);
714  
715  	if (mdb_vread(&si, sizeof (struct swapinfo), sip) == -1) {
716  		mdb_warn("unable to read swapinfo at %#lx", sip);
717  		return (WALK_ERR);
718  	}
719  
720  	wsp->walk_addr = (uintptr_t)si.si_next;
721  
722  	return (wsp->walk_callback(sip, &si, wsp->walk_cbdata));
723  }
724  
725  int
726  swapinfof(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
727  {
728  	struct swapinfo	si;
729  	char		*name;
730  
731  	if (!(flags & DCMD_ADDRSPEC)) {
732  		if (mdb_walk_dcmd("swapinfo", "swapinfo", argc, argv) == -1) {
733  			mdb_warn("can't walk swapinfo");
734  			return (DCMD_ERR);
735  		}
736  		return (DCMD_OK);
737  	}
738  
739  	if (DCMD_HDRSPEC(flags)) {
740  		mdb_printf("%<u>%?s %?s %9s %9s %s%</u>\n",
741  		    "ADDR", "VNODE", "PAGES", "FREE", "NAME");
742  	}
743  
744  	if (mdb_vread(&si, sizeof (struct swapinfo), addr) == -1) {
745  		mdb_warn("can't read swapinfo at %#lx", addr);
746  		return (DCMD_ERR);
747  	}
748  
749  	name = mdb_alloc(si.si_pnamelen, UM_SLEEP | UM_GC);
750  	if (mdb_vread(name, si.si_pnamelen, (uintptr_t)si.si_pname) == -1)
751  		name = "*error*";
752  
753  	mdb_printf("%0?lx %?p %9d %9d %s\n",
754  	    addr, si.si_vp, si.si_npgs, si.si_nfpgs, name);
755  
756  	return (DCMD_OK);
757  }
758  
759  int
760  memlist_walk_step(mdb_walk_state_t *wsp)
761  {
762  	uintptr_t	mlp;
763  	struct memlist	ml;
764  
765  	mlp = wsp->walk_addr;
766  
767  	if (mlp == NULL)
768  		return (WALK_DONE);
769  
770  	if (mdb_vread(&ml, sizeof (struct memlist), mlp) == -1) {
771  		mdb_warn("unable to read memlist at %#lx", mlp);
772  		return (WALK_ERR);
773  	}
774  
775  	wsp->walk_addr = (uintptr_t)ml.next;
776  
777  	return (wsp->walk_callback(mlp, &ml, wsp->walk_cbdata));
778  }
779  
780  int
781  memlist(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
782  {
783  	struct memlist	ml;
784  
785  	if (!(flags & DCMD_ADDRSPEC)) {
786  		uintptr_t ptr;
787  		uint_t list = 0;
788  		int i;
789  		static const char *lists[] = {
790  			"phys_install",
791  			"phys_avail",
792  			"virt_avail"
793  		};
794  
795  		if (mdb_getopts(argc, argv,
796  		    'i', MDB_OPT_SETBITS, (1 << 0), &list,
797  		    'a', MDB_OPT_SETBITS, (1 << 1), &list,
798  		    'v', MDB_OPT_SETBITS, (1 << 2), &list, NULL) != argc)
799  			return (DCMD_USAGE);
800  
801  		if (!list)
802  			list = 1;
803  
804  		for (i = 0; list; i++, list >>= 1) {
805  			if (!(list & 1))
806  				continue;
807  			if ((mdb_readvar(&ptr, lists[i]) == -1) ||
808  			    (ptr == NULL)) {
809  				mdb_warn("%s not found or invalid", lists[i]);
810  				return (DCMD_ERR);
811  			}
812  
813  			mdb_printf("%s:\n", lists[i]);
814  			if (mdb_pwalk_dcmd("memlist", "memlist", 0, NULL,
815  			    ptr) == -1) {
816  				mdb_warn("can't walk memlist");
817  				return (DCMD_ERR);
818  			}
819  		}
820  		return (DCMD_OK);
821  	}
822  
823  	if (DCMD_HDRSPEC(flags))
824  		mdb_printf("%<u>%?s %16s %16s%</u>\n", "ADDR", "BASE", "SIZE");
825  
826  	if (mdb_vread(&ml, sizeof (struct memlist), addr) == -1) {
827  		mdb_warn("can't read memlist at %#lx", addr);
828  		return (DCMD_ERR);
829  	}
830  
831  	mdb_printf("%0?lx %16llx %16llx\n", addr, ml.address, ml.size);
832  
833  	return (DCMD_OK);
834  }
835