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