1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 * Copyright 2026 Oxide Computer Company 26 */ 27 28 /* 29 * vm_usage 30 * 31 * This file implements the getvmusage() private system call. 32 * getvmusage() counts the amount of resident memory pages and swap 33 * reserved by the specified process collective. A "process collective" is 34 * the set of processes owned by a particular, zone, project, task, or user. 35 * 36 * rss and swap are counted so that for a given process collective, a page is 37 * only counted once. For example, this means that if multiple processes in 38 * the same project map the same page, then the project will only be charged 39 * once for that page. On the other hand, if two processes in different 40 * projects map the same page, then both projects will be charged 41 * for the page. 42 * 43 * The vm_getusage() calculation is implemented so that the first thread 44 * performs the rss/swap counting. Other callers will wait for that thread to 45 * finish, copying the results. This enables multiple rcapds and prstats to 46 * consume data from the same calculation. The results are also cached so that 47 * a caller interested in recent results can just copy them instead of starting 48 * a new calculation. The caller passes the maximium age (in seconds) of the 49 * data. If the cached data is young enough, the cache is copied, otherwise, 50 * a new calculation is executed and the cache is replaced with the new 51 * data. 52 * 53 * The rss calculation for each process collective is as follows: 54 * 55 * - Inspect flags, determine if counting rss for zones, projects, tasks, 56 * and/or users. 57 * - For each proc: 58 * - Figure out proc's collectives (zone, project, task, and/or user). 59 * - For each seg in proc's address space: 60 * - If seg is private: 61 * - Lookup anons in the amp. 62 * - For incore pages not previously visited each of the 63 * proc's collectives, add incore pagesize to each. 64 * collective. 65 * Anon's with a refcnt of 1 can be assummed to be not 66 * previously visited. 67 * - For address ranges without anons in the amp: 68 * - Lookup pages in underlying vnode. 69 * - For incore pages not previously visiting for 70 * each of the proc's collectives, add incore 71 * pagesize to each collective. 72 * - If seg is shared: 73 * - Lookup pages in the shared amp or vnode. 74 * - For incore pages not previously visited for each of 75 * the proc's collectives, add incore pagesize to each 76 * collective. 77 * 78 * Swap is reserved by private segments, and shared anonymous segments. 79 * The only shared anon segments which do not reserve swap are ISM segments 80 * and schedctl segments, both of which can be identified by having 81 * amp->swresv == 0. 82 * 83 * The swap calculation for each collective is as follows: 84 * 85 * - Inspect flags, determine if counting rss for zones, projects, tasks, 86 * and/or users. 87 * - For each proc: 88 * - Figure out proc's collectives (zone, project, task, and/or user). 89 * - For each seg in proc's address space: 90 * - If seg is private: 91 * - Add svd->swresv pages to swap count for each of the 92 * proc's collectives. 93 * - If seg is anon, shared, and amp->swresv != 0 94 * - For address ranges in amp not previously visited for 95 * each of the proc's collectives, add size of address 96 * range to the swap count for each collective. 97 * 98 * These two calculations are done simultaneously, with most of the work 99 * being done in vmu_calculate_seg(). The results of the calculation are 100 * copied into "vmu_data.vmu_cache_results". 101 * 102 * To perform the calculation, various things are tracked and cached: 103 * 104 * - incore/not-incore page ranges for all vnodes. 105 * (vmu_data.vmu_all_vnodes_hash) 106 * This eliminates looking up the same page more than once. 107 * 108 * - incore/not-incore page ranges for all shared amps. 109 * (vmu_data.vmu_all_amps_hash) 110 * This eliminates looking up the same page more than once. 111 * 112 * - visited page ranges for each collective. 113 * - per vnode (entity->vme_vnode_hash) 114 * - per shared amp (entity->vme_amp_hash) 115 * For accurate counting of map-shared and COW-shared pages. 116 * 117 * - visited private anons (refcnt > 1) for each collective. 118 * (entity->vme_anon_hash) 119 * For accurate counting of COW-shared pages. 120 * 121 * The common accounting structure is the vmu_entity_t, which represents 122 * collectives: 123 * 124 * - A zone. 125 * - A project, task, or user within a zone. 126 * - The entire system (vmu_data.vmu_system). 127 * - Each collapsed (col) project and user. This means a given projid or 128 * uid, regardless of which zone the process is in. For instance, 129 * project 0 in the global zone and project 0 in a non global zone are 130 * the same collapsed project. 131 * 132 * Each entity structure tracks which pages have been already visited for 133 * that entity (via previously inspected processes) so that these pages are 134 * not double counted. 135 */ 136 137 #include <sys/errno.h> 138 #include <sys/types.h> 139 #include <sys/zone.h> 140 #include <sys/proc.h> 141 #include <sys/project.h> 142 #include <sys/task.h> 143 #include <sys/thread.h> 144 #include <sys/time.h> 145 #include <sys/mman.h> 146 #include <sys/modhash.h> 147 #include <sys/modhash_impl.h> 148 #include <sys/shm.h> 149 #include <sys/swap.h> 150 #include <sys/synch.h> 151 #include <sys/systm.h> 152 #include <sys/uio.h> 153 #include <sys/var.h> 154 #include <sys/vm_usage.h> 155 #include <sys/zone.h> 156 #include <sys/sunddi.h> 157 #include <sys/avl.h> 158 #include <vm/anon.h> 159 #include <vm/as.h> 160 #include <vm/seg_vn.h> 161 #include <vm/seg_spt.h> 162 163 #define VMUSAGE_HASH_SIZE 512 164 165 #define VMUSAGE_TYPE_VNODE 1 166 #define VMUSAGE_TYPE_AMP 2 167 #define VMUSAGE_TYPE_ANON 3 168 169 #define VMUSAGE_BOUND_UNKNOWN 0 170 #define VMUSAGE_BOUND_INCORE 1 171 #define VMUSAGE_BOUND_NOT_INCORE 2 172 173 #define ISWITHIN(node, addr) ((node)->vmb_start <= addr && \ 174 (node)->vmb_end >= addr ? 1 : 0) 175 176 /* 177 * bounds for vnodes and shared amps 178 * Each bound is either entirely incore, entirely not in core, or 179 * entirely unknown. bounds are stored in an avl tree sorted by start member 180 * when in use, otherwise (free or temporary lists) they're strung 181 * together off of vmb_next. 182 */ 183 typedef struct vmu_bound { 184 avl_node_t vmb_node; 185 struct vmu_bound *vmb_next; /* NULL in tree else on free or temp list */ 186 pgcnt_t vmb_start; /* page offset in vnode/amp on which bound starts */ 187 pgcnt_t vmb_end; /* page offset in vnode/amp on which bound ends */ 188 char vmb_type; /* One of VMUSAGE_BOUND_* */ 189 } vmu_bound_t; 190 191 /* 192 * hash of visited objects (vnodes or shared amps) 193 * key is address of vnode or amp. Bounds lists known incore/non-incore 194 * bounds for vnode/amp. 195 */ 196 typedef struct vmu_object { 197 struct vmu_object *vmo_next; /* free list */ 198 caddr_t vmo_key; 199 short vmo_type; 200 avl_tree_t vmo_bounds; 201 } vmu_object_t; 202 203 /* 204 * Entity by which to count results. 205 * 206 * The entity structure keeps the current rss/swap counts for each entity 207 * (zone, project, etc), and hashes of vm structures that have already 208 * been visited for the entity. 209 * 210 * vme_next: links the list of all entities currently being counted by 211 * vmu_calculate(). 212 * 213 * vme_next_calc: links the list of entities related to the current process 214 * being counted by vmu_calculate_proc(). 215 * 216 * vmu_calculate_proc() walks all processes. For each process, it makes a 217 * list of the entities related to that process using vme_next_calc. This 218 * list changes each time vmu_calculate_proc() is called. 219 * 220 */ 221 typedef struct vmu_entity { 222 struct vmu_entity *vme_next; 223 struct vmu_entity *vme_next_calc; 224 mod_hash_t *vme_vnode_hash; /* vnodes visited for entity */ 225 mod_hash_t *vme_amp_hash; /* shared amps visited for entity */ 226 mod_hash_t *vme_anon_hash; /* COW anons visited for entity */ 227 vmusage_t vme_result; /* identifies entity and results */ 228 } vmu_entity_t; 229 230 /* 231 * Hash of entities visited within a zone, and an entity for the zone 232 * itself. 233 */ 234 typedef struct vmu_zone { 235 struct vmu_zone *vmz_next; /* free list */ 236 id_t vmz_id; 237 vmu_entity_t *vmz_zone; 238 mod_hash_t *vmz_projects_hash; 239 mod_hash_t *vmz_tasks_hash; 240 mod_hash_t *vmz_rusers_hash; 241 mod_hash_t *vmz_eusers_hash; 242 } vmu_zone_t; 243 244 /* 245 * Cache of results from last calculation 246 */ 247 typedef struct vmu_cache { 248 vmusage_t *vmc_results; /* Results from last call to */ 249 /* vm_getusage(). */ 250 uint64_t vmc_nresults; /* Count of cached results */ 251 uint64_t vmc_refcnt; /* refcnt for free */ 252 uint_t vmc_flags; /* Flags for vm_getusage() */ 253 hrtime_t vmc_timestamp; /* when cache was created */ 254 } vmu_cache_t; 255 256 /* 257 * top level rss info for the system 258 */ 259 typedef struct vmu_data { 260 kmutex_t vmu_lock; /* Protects vmu_data */ 261 kcondvar_t vmu_cv; /* Used to signal threads */ 262 /* Waiting for */ 263 /* Rss_calc_thread to finish */ 264 vmu_entity_t *vmu_system; /* Entity for tracking */ 265 /* rss/swap for all processes */ 266 /* in all zones */ 267 mod_hash_t *vmu_zones_hash; /* Zones visited */ 268 mod_hash_t *vmu_projects_col_hash; /* These *_col_hash hashes */ 269 mod_hash_t *vmu_rusers_col_hash; /* keep track of entities, */ 270 mod_hash_t *vmu_eusers_col_hash; /* ignoring zoneid, in order */ 271 /* to implement VMUSAGE_COL_* */ 272 /* flags, which aggregate by */ 273 /* project or user regardless */ 274 /* of zoneid. */ 275 mod_hash_t *vmu_all_vnodes_hash; /* System wide visited vnodes */ 276 /* to track incore/not-incore */ 277 mod_hash_t *vmu_all_amps_hash; /* System wide visited shared */ 278 /* amps to track incore/not- */ 279 /* incore */ 280 vmu_entity_t *vmu_entities; /* Linked list of entities */ 281 size_t vmu_nentities; /* Count of entities in list */ 282 vmu_cache_t *vmu_cache; /* Cached results */ 283 kthread_t *vmu_calc_thread; /* NULL, or thread running */ 284 /* vmu_calculate() */ 285 uint_t vmu_calc_flags; /* Flags being using by */ 286 /* currently running calc */ 287 /* thread */ 288 uint_t vmu_pending_flags; /* Flags of vm_getusage() */ 289 /* threads waiting for */ 290 /* calc thread to finish */ 291 uint_t vmu_pending_waiters; /* Number of threads waiting */ 292 /* for calc thread */ 293 vmu_bound_t *vmu_free_bounds; 294 vmu_object_t *vmu_free_objects; 295 vmu_entity_t *vmu_free_entities; 296 vmu_zone_t *vmu_free_zones; 297 } vmu_data_t; 298 299 extern struct as kas; 300 extern proc_t *practive; 301 extern zone_t *global_zone; 302 extern struct seg_ops segvn_ops; 303 extern struct seg_ops segspt_shmops; 304 305 static vmu_data_t vmu_data; 306 static kmem_cache_t *vmu_bound_cache; 307 static kmem_cache_t *vmu_object_cache; 308 309 /* 310 * Comparison routine for AVL tree. We base our comparison on vmb_start. 311 */ 312 static int 313 bounds_cmp(const void *bnd1, const void *bnd2) 314 { 315 const vmu_bound_t *bound1 = bnd1; 316 const vmu_bound_t *bound2 = bnd2; 317 318 if (bound1->vmb_start == bound2->vmb_start) { 319 return (0); 320 } 321 if (bound1->vmb_start < bound2->vmb_start) { 322 return (-1); 323 } 324 325 return (1); 326 } 327 328 /* 329 * Save a bound on the free list. 330 */ 331 static void 332 vmu_free_bound(vmu_bound_t *bound) 333 { 334 bound->vmb_next = vmu_data.vmu_free_bounds; 335 bound->vmb_start = 0; 336 bound->vmb_end = 0; 337 bound->vmb_type = 0; 338 vmu_data.vmu_free_bounds = bound; 339 } 340 341 /* 342 * Free an object, and all visited bound info. 343 */ 344 static void 345 vmu_free_object(mod_hash_val_t val) 346 { 347 vmu_object_t *obj = (vmu_object_t *)val; 348 avl_tree_t *tree = &(obj->vmo_bounds); 349 vmu_bound_t *bound; 350 void *cookie = NULL; 351 352 while ((bound = avl_destroy_nodes(tree, &cookie)) != NULL) 353 vmu_free_bound(bound); 354 avl_destroy(tree); 355 356 obj->vmo_type = 0; 357 obj->vmo_next = vmu_data.vmu_free_objects; 358 vmu_data.vmu_free_objects = obj; 359 } 360 361 /* 362 * Free an entity, and hashes of visited objects for that entity. 363 */ 364 static void 365 vmu_free_entity(mod_hash_val_t val) 366 { 367 vmu_entity_t *entity = (vmu_entity_t *)val; 368 369 if (entity->vme_vnode_hash != NULL) 370 i_mod_hash_clear_nosync(entity->vme_vnode_hash); 371 if (entity->vme_amp_hash != NULL) 372 i_mod_hash_clear_nosync(entity->vme_amp_hash); 373 if (entity->vme_anon_hash != NULL) 374 i_mod_hash_clear_nosync(entity->vme_anon_hash); 375 376 entity->vme_next = vmu_data.vmu_free_entities; 377 vmu_data.vmu_free_entities = entity; 378 } 379 380 /* 381 * Free zone entity, and all hashes of entities inside that zone, 382 * which are projects, tasks, and users. 383 */ 384 static void 385 vmu_free_zone(mod_hash_val_t val) 386 { 387 vmu_zone_t *zone = (vmu_zone_t *)val; 388 389 if (zone->vmz_zone != NULL) { 390 vmu_free_entity((mod_hash_val_t)zone->vmz_zone); 391 zone->vmz_zone = NULL; 392 } 393 if (zone->vmz_projects_hash != NULL) 394 i_mod_hash_clear_nosync(zone->vmz_projects_hash); 395 if (zone->vmz_tasks_hash != NULL) 396 i_mod_hash_clear_nosync(zone->vmz_tasks_hash); 397 if (zone->vmz_rusers_hash != NULL) 398 i_mod_hash_clear_nosync(zone->vmz_rusers_hash); 399 if (zone->vmz_eusers_hash != NULL) 400 i_mod_hash_clear_nosync(zone->vmz_eusers_hash); 401 zone->vmz_next = vmu_data.vmu_free_zones; 402 vmu_data.vmu_free_zones = zone; 403 } 404 405 /* 406 * Initialize synchronization primitives and hashes for system-wide tracking 407 * of visited vnodes and shared amps. Initialize results cache. 408 */ 409 void 410 vm_usage_init() 411 { 412 mutex_init(&vmu_data.vmu_lock, NULL, MUTEX_DEFAULT, NULL); 413 cv_init(&vmu_data.vmu_cv, NULL, CV_DEFAULT, NULL); 414 415 vmu_data.vmu_system = NULL; 416 vmu_data.vmu_zones_hash = NULL; 417 vmu_data.vmu_projects_col_hash = NULL; 418 vmu_data.vmu_rusers_col_hash = NULL; 419 vmu_data.vmu_eusers_col_hash = NULL; 420 421 vmu_data.vmu_free_bounds = NULL; 422 vmu_data.vmu_free_objects = NULL; 423 vmu_data.vmu_free_entities = NULL; 424 vmu_data.vmu_free_zones = NULL; 425 426 vmu_data.vmu_all_vnodes_hash = mod_hash_create_ptrhash( 427 "vmusage vnode hash", VMUSAGE_HASH_SIZE, vmu_free_object, 428 sizeof (vnode_t)); 429 vmu_data.vmu_all_amps_hash = mod_hash_create_ptrhash( 430 "vmusage amp hash", VMUSAGE_HASH_SIZE, vmu_free_object, 431 sizeof (struct anon_map)); 432 vmu_data.vmu_projects_col_hash = mod_hash_create_idhash( 433 "vmusage collapsed project hash", VMUSAGE_HASH_SIZE, 434 vmu_free_entity); 435 vmu_data.vmu_rusers_col_hash = mod_hash_create_idhash( 436 "vmusage collapsed ruser hash", VMUSAGE_HASH_SIZE, 437 vmu_free_entity); 438 vmu_data.vmu_eusers_col_hash = mod_hash_create_idhash( 439 "vmusage collpased euser hash", VMUSAGE_HASH_SIZE, 440 vmu_free_entity); 441 vmu_data.vmu_zones_hash = mod_hash_create_idhash( 442 "vmusage zone hash", VMUSAGE_HASH_SIZE, vmu_free_zone); 443 444 vmu_bound_cache = kmem_cache_create("vmu_bound_cache", 445 sizeof (vmu_bound_t), 0, NULL, NULL, NULL, NULL, NULL, 0); 446 vmu_object_cache = kmem_cache_create("vmu_object_cache", 447 sizeof (vmu_object_t), 0, NULL, NULL, NULL, NULL, NULL, 0); 448 449 vmu_data.vmu_entities = NULL; 450 vmu_data.vmu_nentities = 0; 451 452 vmu_data.vmu_cache = NULL; 453 vmu_data.vmu_calc_thread = NULL; 454 vmu_data.vmu_calc_flags = 0; 455 vmu_data.vmu_pending_flags = 0; 456 vmu_data.vmu_pending_waiters = 0; 457 } 458 459 /* 460 * Allocate hashes for tracking vm objects visited for an entity. 461 * Update list of entities. 462 */ 463 static vmu_entity_t * 464 vmu_alloc_entity(id_t id, int type, id_t zoneid) 465 { 466 vmu_entity_t *entity; 467 468 if (vmu_data.vmu_free_entities != NULL) { 469 entity = vmu_data.vmu_free_entities; 470 vmu_data.vmu_free_entities = 471 vmu_data.vmu_free_entities->vme_next; 472 bzero(&entity->vme_result, sizeof (vmusage_t)); 473 } else { 474 entity = kmem_zalloc(sizeof (vmu_entity_t), KM_SLEEP); 475 } 476 entity->vme_result.vmu_id = id; 477 entity->vme_result.vmu_zoneid = zoneid; 478 entity->vme_result.vmu_type = type; 479 480 if (entity->vme_vnode_hash == NULL) 481 entity->vme_vnode_hash = mod_hash_create_ptrhash( 482 "vmusage vnode hash", VMUSAGE_HASH_SIZE, vmu_free_object, 483 sizeof (vnode_t)); 484 485 if (entity->vme_amp_hash == NULL) 486 entity->vme_amp_hash = mod_hash_create_ptrhash( 487 "vmusage amp hash", VMUSAGE_HASH_SIZE, vmu_free_object, 488 sizeof (struct anon_map)); 489 490 if (entity->vme_anon_hash == NULL) 491 entity->vme_anon_hash = mod_hash_create_ptrhash( 492 "vmusage anon hash", VMUSAGE_HASH_SIZE, 493 mod_hash_null_valdtor, sizeof (struct anon)); 494 495 entity->vme_next = vmu_data.vmu_entities; 496 vmu_data.vmu_entities = entity; 497 vmu_data.vmu_nentities++; 498 499 return (entity); 500 } 501 502 /* 503 * Allocate a zone entity, and hashes for tracking visited vm objects 504 * for projects, tasks, and users within that zone. 505 */ 506 static vmu_zone_t * 507 vmu_alloc_zone(id_t id) 508 { 509 vmu_zone_t *zone; 510 511 if (vmu_data.vmu_free_zones != NULL) { 512 zone = vmu_data.vmu_free_zones; 513 vmu_data.vmu_free_zones = 514 vmu_data.vmu_free_zones->vmz_next; 515 zone->vmz_next = NULL; 516 zone->vmz_zone = NULL; 517 } else { 518 zone = kmem_zalloc(sizeof (vmu_zone_t), KM_SLEEP); 519 } 520 521 zone->vmz_id = id; 522 523 if ((vmu_data.vmu_calc_flags & (VMUSAGE_ZONE | VMUSAGE_ALL_ZONES)) != 0) 524 zone->vmz_zone = vmu_alloc_entity(id, VMUSAGE_ZONE, id); 525 526 if ((vmu_data.vmu_calc_flags & (VMUSAGE_PROJECTS | 527 VMUSAGE_ALL_PROJECTS)) != 0 && zone->vmz_projects_hash == NULL) 528 zone->vmz_projects_hash = mod_hash_create_idhash( 529 "vmusage project hash", VMUSAGE_HASH_SIZE, vmu_free_entity); 530 531 if ((vmu_data.vmu_calc_flags & (VMUSAGE_TASKS | VMUSAGE_ALL_TASKS)) 532 != 0 && zone->vmz_tasks_hash == NULL) 533 zone->vmz_tasks_hash = mod_hash_create_idhash( 534 "vmusage task hash", VMUSAGE_HASH_SIZE, vmu_free_entity); 535 536 if ((vmu_data.vmu_calc_flags & (VMUSAGE_RUSERS | VMUSAGE_ALL_RUSERS)) 537 != 0 && zone->vmz_rusers_hash == NULL) 538 zone->vmz_rusers_hash = mod_hash_create_idhash( 539 "vmusage ruser hash", VMUSAGE_HASH_SIZE, vmu_free_entity); 540 541 if ((vmu_data.vmu_calc_flags & (VMUSAGE_EUSERS | VMUSAGE_ALL_EUSERS)) 542 != 0 && zone->vmz_eusers_hash == NULL) 543 zone->vmz_eusers_hash = mod_hash_create_idhash( 544 "vmusage euser hash", VMUSAGE_HASH_SIZE, vmu_free_entity); 545 546 return (zone); 547 } 548 549 /* 550 * Allocate a structure for tracking visited bounds for a vm object. 551 */ 552 static vmu_object_t * 553 vmu_alloc_object(caddr_t key, int type) 554 { 555 vmu_object_t *object; 556 557 if (vmu_data.vmu_free_objects != NULL) { 558 object = vmu_data.vmu_free_objects; 559 vmu_data.vmu_free_objects = 560 vmu_data.vmu_free_objects->vmo_next; 561 } else { 562 object = kmem_cache_alloc(vmu_object_cache, KM_SLEEP); 563 } 564 565 object->vmo_next = NULL; 566 object->vmo_key = key; 567 object->vmo_type = type; 568 avl_create(&(object->vmo_bounds), bounds_cmp, sizeof (vmu_bound_t), 0); 569 570 return (object); 571 } 572 573 /* 574 * Allocate and return a bound structure. 575 */ 576 static vmu_bound_t * 577 vmu_alloc_bound() 578 { 579 vmu_bound_t *bound; 580 581 if (vmu_data.vmu_free_bounds != NULL) { 582 bound = vmu_data.vmu_free_bounds; 583 vmu_data.vmu_free_bounds = 584 vmu_data.vmu_free_bounds->vmb_next; 585 } else { 586 bound = kmem_cache_alloc(vmu_bound_cache, KM_SLEEP); 587 } 588 589 bound->vmb_next = NULL; 590 bound->vmb_start = 0; 591 bound->vmb_end = 0; 592 bound->vmb_type = 0; 593 return (bound); 594 } 595 596 /* 597 * vmu_find_insert_* functions implement hash lookup or allocate and 598 * insert operations. 599 */ 600 static vmu_object_t * 601 vmu_find_insert_object(mod_hash_t *hash, caddr_t key, uint_t type) 602 { 603 int ret; 604 vmu_object_t *object; 605 606 ret = i_mod_hash_find_nosync(hash, (mod_hash_key_t)key, 607 (mod_hash_val_t *)&object); 608 if (ret != 0) { 609 object = vmu_alloc_object(key, type); 610 ret = i_mod_hash_insert_nosync(hash, (mod_hash_key_t)key, 611 (mod_hash_val_t)object, (mod_hash_hndl_t)0); 612 ASSERT(ret == 0); 613 } 614 return (object); 615 } 616 617 static int 618 vmu_find_insert_anon(mod_hash_t *hash, caddr_t key) 619 { 620 int ret; 621 caddr_t val; 622 623 ret = i_mod_hash_find_nosync(hash, (mod_hash_key_t)key, 624 (mod_hash_val_t *)&val); 625 626 if (ret == 0) 627 return (0); 628 629 ret = i_mod_hash_insert_nosync(hash, (mod_hash_key_t)key, 630 (mod_hash_val_t)key, (mod_hash_hndl_t)0); 631 632 ASSERT(ret == 0); 633 634 return (1); 635 } 636 637 static vmu_entity_t * 638 vmu_find_insert_entity(mod_hash_t *hash, id_t id, uint_t type, id_t zoneid) 639 { 640 int ret; 641 vmu_entity_t *entity; 642 643 ret = i_mod_hash_find_nosync(hash, (mod_hash_key_t)(uintptr_t)id, 644 (mod_hash_val_t *)&entity); 645 if (ret != 0) { 646 entity = vmu_alloc_entity(id, type, zoneid); 647 ret = i_mod_hash_insert_nosync(hash, 648 (mod_hash_key_t)(uintptr_t)id, (mod_hash_val_t)entity, 649 (mod_hash_hndl_t)0); 650 ASSERT(ret == 0); 651 } 652 return (entity); 653 } 654 655 656 657 658 /* 659 * Returns list of object bounds between start and end. New bounds inserted 660 * by this call are given type. 661 * 662 * Returns the number of pages covered if new bounds are created. Returns 0 663 * if region between start/end consists of all existing bounds. 664 */ 665 static pgcnt_t 666 vmu_insert_lookup_object_bounds(vmu_object_t *ro, pgcnt_t start, pgcnt_t 667 end, char type, vmu_bound_t **first, vmu_bound_t **last) 668 { 669 avl_tree_t *tree = &(ro->vmo_bounds); 670 avl_index_t where; 671 vmu_bound_t *walker, *tmp; 672 pgcnt_t ret = 0; 673 674 ASSERT(start <= end); 675 676 *first = *last = NULL; 677 678 tmp = vmu_alloc_bound(); 679 tmp->vmb_start = start; 680 tmp->vmb_type = type; 681 682 /* Hopelessly optimistic case. */ 683 if (walker = avl_find(tree, tmp, &where)) { 684 /* We got lucky. */ 685 vmu_free_bound(tmp); 686 *first = walker; 687 } 688 689 if (walker == NULL) { 690 /* Is start in the previous node? */ 691 walker = avl_nearest(tree, where, AVL_BEFORE); 692 if (walker != NULL) { 693 if (ISWITHIN(walker, start)) { 694 /* We found start. */ 695 vmu_free_bound(tmp); 696 *first = walker; 697 } 698 } 699 } 700 701 /* 702 * At this point, if *first is still NULL, then we 703 * didn't get a direct hit and start isn't covered 704 * by the previous node. We know that the next node 705 * must have a greater start value than we require 706 * because avl_find tells us where the AVL routines would 707 * insert our new node. We have some gap between the 708 * start we want and the next node. 709 */ 710 if (*first == NULL) { 711 walker = avl_nearest(tree, where, AVL_AFTER); 712 if (walker != NULL && walker->vmb_start <= end) { 713 /* Fill the gap. */ 714 tmp->vmb_end = walker->vmb_start - 1; 715 *first = tmp; 716 } else { 717 /* We have a gap over [start, end]. */ 718 tmp->vmb_end = end; 719 *first = *last = tmp; 720 } 721 ret += tmp->vmb_end - tmp->vmb_start + 1; 722 avl_insert(tree, tmp, where); 723 } 724 725 ASSERT(*first != NULL); 726 727 if (*last != NULL) { 728 /* We're done. */ 729 return (ret); 730 } 731 732 /* 733 * If we are here we still need to set *last and 734 * that may involve filling in some gaps. 735 */ 736 *last = *first; 737 for (;;) { 738 if (ISWITHIN(*last, end)) { 739 /* We're done. */ 740 break; 741 } 742 walker = AVL_NEXT(tree, *last); 743 if (walker == NULL || walker->vmb_start > end) { 744 /* Bottom or mid tree with gap. */ 745 tmp = vmu_alloc_bound(); 746 tmp->vmb_start = (*last)->vmb_end + 1; 747 tmp->vmb_end = end; 748 tmp->vmb_type = type; 749 ret += tmp->vmb_end - tmp->vmb_start + 1; 750 avl_insert_here(tree, tmp, *last, AVL_AFTER); 751 *last = tmp; 752 break; 753 } else { 754 if ((*last)->vmb_end + 1 != walker->vmb_start) { 755 /* Non-contiguous. */ 756 tmp = vmu_alloc_bound(); 757 tmp->vmb_start = (*last)->vmb_end + 1; 758 tmp->vmb_end = walker->vmb_start - 1; 759 tmp->vmb_type = type; 760 ret += tmp->vmb_end - tmp->vmb_start + 1; 761 avl_insert_here(tree, tmp, *last, AVL_AFTER); 762 *last = tmp; 763 } else { 764 *last = walker; 765 } 766 } 767 } 768 769 return (ret); 770 } 771 772 /* 773 * vmu_update_bounds() 774 * 775 * tree: avl_tree in which first and last hang. 776 * 777 * first, last: list of continuous bounds, of which zero or more are of 778 * type VMUSAGE_BOUND_UNKNOWN. 779 * 780 * new_tree: avl_tree in which new_first and new_last hang. 781 * 782 * new_first, new_last: list of continuous bounds, of which none are of 783 * type VMUSAGE_BOUND_UNKNOWN. These bounds are used to 784 * update the types of bounds in (first,last) with 785 * type VMUSAGE_BOUND_UNKNOWN. 786 * 787 * For the list of bounds (first,last), this function updates any bounds 788 * with type VMUSAGE_BOUND_UNKNOWN using the type of the corresponding bound in 789 * the list (new_first, new_last). 790 * 791 * If a bound of type VMUSAGE_BOUND_UNKNOWN spans multiple bounds in the list 792 * (new_first, new_last), it will be split into multiple bounds. 793 * 794 * Return value: 795 * The number of pages in the list of bounds (first,last) that were of 796 * type VMUSAGE_BOUND_UNKNOWN, which have been updated to be of type 797 * VMUSAGE_BOUND_INCORE. 798 * 799 */ 800 static pgcnt_t 801 vmu_update_bounds(avl_tree_t *tree, vmu_bound_t **first, vmu_bound_t **last, 802 avl_tree_t *new_tree, vmu_bound_t *new_first, vmu_bound_t *new_last) 803 { 804 vmu_bound_t *next, *new_next, *tmp; 805 pgcnt_t rss = 0; 806 807 next = *first; 808 new_next = new_first; 809 810 /* 811 * Verify first and last bound are covered by new bounds if they 812 * have unknown type. 813 */ 814 ASSERT((*first)->vmb_type != VMUSAGE_BOUND_UNKNOWN || 815 (*first)->vmb_start >= new_first->vmb_start); 816 ASSERT((*last)->vmb_type != VMUSAGE_BOUND_UNKNOWN || 817 (*last)->vmb_end <= new_last->vmb_end); 818 for (;;) { 819 /* If bound already has type, proceed to next bound. */ 820 if (next->vmb_type != VMUSAGE_BOUND_UNKNOWN) { 821 if (next == *last) 822 break; 823 next = AVL_NEXT(tree, next); 824 continue; 825 } 826 while (new_next->vmb_end < next->vmb_start) 827 new_next = AVL_NEXT(new_tree, new_next); 828 ASSERT(new_next->vmb_type != VMUSAGE_BOUND_UNKNOWN); 829 next->vmb_type = new_next->vmb_type; 830 if (new_next->vmb_end < next->vmb_end) { 831 /* need to split bound */ 832 tmp = vmu_alloc_bound(); 833 tmp->vmb_type = VMUSAGE_BOUND_UNKNOWN; 834 tmp->vmb_start = new_next->vmb_end + 1; 835 tmp->vmb_end = next->vmb_end; 836 avl_insert_here(tree, tmp, next, AVL_AFTER); 837 next->vmb_end = new_next->vmb_end; 838 if (*last == next) 839 *last = tmp; 840 if (next->vmb_type == VMUSAGE_BOUND_INCORE) 841 rss += next->vmb_end - next->vmb_start + 1; 842 next = tmp; 843 } else { 844 if (next->vmb_type == VMUSAGE_BOUND_INCORE) 845 rss += next->vmb_end - next->vmb_start + 1; 846 if (next == *last) 847 break; 848 next = AVL_NEXT(tree, next); 849 } 850 } 851 return (rss); 852 } 853 854 /* 855 * Merges adjacent bounds with same type between first and last bound. 856 * After merge, last pointer may point to a different bound, as (incoming) 857 * last bound may have been merged away. 858 */ 859 static void 860 vmu_merge_bounds(avl_tree_t *tree, vmu_bound_t **first, vmu_bound_t **last) 861 { 862 vmu_bound_t *current; 863 vmu_bound_t *next; 864 865 ASSERT(tree != NULL); 866 ASSERT(*first != NULL); 867 ASSERT(*last != NULL); 868 869 current = *first; 870 while (current != *last) { 871 next = AVL_NEXT(tree, current); 872 if ((current->vmb_end + 1) == next->vmb_start && 873 current->vmb_type == next->vmb_type) { 874 current->vmb_end = next->vmb_end; 875 avl_remove(tree, next); 876 vmu_free_bound(next); 877 if (next == *last) { 878 *last = current; 879 } 880 } else { 881 current = AVL_NEXT(tree, current); 882 } 883 } 884 } 885 886 /* 887 * Given an amp and a list of bounds, updates each bound's type with 888 * VMUSAGE_BOUND_INCORE or VMUSAGE_BOUND_NOT_INCORE. 889 * 890 * If a bound is partially incore, it will be split into two bounds. 891 * first and last may be modified, as bounds may be split into multiple 892 * bounds if they are partially incore/not-incore. 893 * 894 * Set incore to non-zero if bounds are already known to be incore. 895 * 896 */ 897 static void 898 vmu_amp_update_incore_bounds(avl_tree_t *tree, struct anon_map *amp, 899 vmu_bound_t **first, vmu_bound_t **last, boolean_t incore) 900 { 901 vmu_bound_t *next; 902 vmu_bound_t *tmp; 903 pgcnt_t index; 904 short bound_type; 905 short page_type; 906 vnode_t *vn; 907 anoff_t off; 908 struct anon *ap; 909 910 next = *first; 911 /* Shared anon slots don't change once set. */ 912 ANON_LOCK_ENTER(&->a_rwlock, RW_READER); 913 for (;;) { 914 if (incore == B_TRUE) 915 next->vmb_type = VMUSAGE_BOUND_INCORE; 916 917 if (next->vmb_type != VMUSAGE_BOUND_UNKNOWN) { 918 if (next == *last) 919 break; 920 next = AVL_NEXT(tree, next); 921 continue; 922 } 923 bound_type = next->vmb_type; 924 index = next->vmb_start; 925 while (index <= next->vmb_end) { 926 927 /* 928 * These are used to determine how much to increment 929 * index when a large page is found. 930 */ 931 page_t *page; 932 pgcnt_t pgcnt = 1; 933 uint_t pgshft; 934 pgcnt_t pgmsk; 935 936 ap = anon_get_ptr(amp->ahp, index); 937 if (ap != NULL) 938 swap_xlate(ap, &vn, &off); 939 940 if (ap != NULL && vn != NULL && vn->v_pages != NULL && 941 (page = page_exists(vn, off)) != NULL) { 942 page_type = VMUSAGE_BOUND_INCORE; 943 if (page->p_szc > 0) { 944 pgcnt = page_get_pagecnt(page->p_szc); 945 pgshft = page_get_shift(page->p_szc); 946 pgmsk = (0x1 << (pgshft - PAGESHIFT)) 947 - 1; 948 } 949 } else { 950 page_type = VMUSAGE_BOUND_NOT_INCORE; 951 } 952 if (bound_type == VMUSAGE_BOUND_UNKNOWN) { 953 next->vmb_type = page_type; 954 } else if (next->vmb_type != page_type) { 955 /* 956 * If current bound type does not match page 957 * type, need to split off new bound. 958 */ 959 tmp = vmu_alloc_bound(); 960 tmp->vmb_type = page_type; 961 tmp->vmb_start = index; 962 tmp->vmb_end = next->vmb_end; 963 avl_insert_here(tree, tmp, next, AVL_AFTER); 964 next->vmb_end = index - 1; 965 if (*last == next) 966 *last = tmp; 967 next = tmp; 968 } 969 if (pgcnt > 1) { 970 /* 971 * If inside large page, jump to next large 972 * page 973 */ 974 index = (index & ~pgmsk) + pgcnt; 975 } else { 976 index++; 977 } 978 } 979 if (next == *last) { 980 ASSERT(next->vmb_type != VMUSAGE_BOUND_UNKNOWN); 981 break; 982 } else 983 next = AVL_NEXT(tree, next); 984 } 985 ANON_LOCK_EXIT(&->a_rwlock); 986 } 987 988 /* 989 * Same as vmu_amp_update_incore_bounds(), except for tracking 990 * incore-/not-incore for vnodes. 991 */ 992 static void 993 vmu_vnode_update_incore_bounds(avl_tree_t *tree, vnode_t *vnode, 994 vmu_bound_t **first, vmu_bound_t **last) 995 { 996 vmu_bound_t *next; 997 vmu_bound_t *tmp; 998 pgcnt_t index; 999 short bound_type; 1000 short page_type; 1001 1002 next = *first; 1003 for (;;) { 1004 if (vnode->v_pages == NULL) 1005 next->vmb_type = VMUSAGE_BOUND_NOT_INCORE; 1006 1007 if (next->vmb_type != VMUSAGE_BOUND_UNKNOWN) { 1008 if (next == *last) 1009 break; 1010 next = AVL_NEXT(tree, next); 1011 continue; 1012 } 1013 1014 bound_type = next->vmb_type; 1015 index = next->vmb_start; 1016 while (index <= next->vmb_end) { 1017 1018 /* 1019 * These are used to determine how much to increment 1020 * index when a large page is found. 1021 */ 1022 page_t *page; 1023 pgcnt_t pgcnt = 1; 1024 uint_t pgshft; 1025 pgcnt_t pgmsk; 1026 1027 if (vnode->v_pages != NULL && 1028 (page = page_exists(vnode, ptob(index))) != NULL) { 1029 page_type = VMUSAGE_BOUND_INCORE; 1030 if (page->p_szc > 0) { 1031 pgcnt = page_get_pagecnt(page->p_szc); 1032 pgshft = page_get_shift(page->p_szc); 1033 pgmsk = (0x1 << (pgshft - PAGESHIFT)) 1034 - 1; 1035 } 1036 } else { 1037 page_type = VMUSAGE_BOUND_NOT_INCORE; 1038 } 1039 if (bound_type == VMUSAGE_BOUND_UNKNOWN) { 1040 next->vmb_type = page_type; 1041 } else if (next->vmb_type != page_type) { 1042 /* 1043 * If current bound type does not match page 1044 * type, need to split off new bound. 1045 */ 1046 tmp = vmu_alloc_bound(); 1047 tmp->vmb_type = page_type; 1048 tmp->vmb_start = index; 1049 tmp->vmb_end = next->vmb_end; 1050 avl_insert_here(tree, tmp, next, AVL_AFTER); 1051 next->vmb_end = index - 1; 1052 if (*last == next) 1053 *last = tmp; 1054 next = tmp; 1055 } 1056 if (pgcnt > 1) { 1057 /* 1058 * If inside large page, jump to next large 1059 * page 1060 */ 1061 index = (index & ~pgmsk) + pgcnt; 1062 } else { 1063 index++; 1064 } 1065 } 1066 if (next == *last) { 1067 ASSERT(next->vmb_type != VMUSAGE_BOUND_UNKNOWN); 1068 break; 1069 } else 1070 next = AVL_NEXT(tree, next); 1071 } 1072 } 1073 1074 /* 1075 * Calculate the rss and swap consumed by a segment. vmu_entities is the 1076 * list of entities to visit. For shared segments, the vnode or amp 1077 * is looked up in each entity to see if it has been already counted. Private 1078 * anon pages are checked per entity to ensure that COW pages are not 1079 * double counted. 1080 * 1081 * For private mapped files, first the amp is checked for private pages. 1082 * Bounds not backed by the amp are looked up in the vnode for each entity 1083 * to avoid double counting of private COW vnode pages. 1084 */ 1085 static void 1086 vmu_calculate_seg(vmu_entity_t *vmu_entities, struct seg *seg) 1087 { 1088 struct segvn_data *svd; 1089 struct shm_data *shmd; 1090 struct spt_data *sptd; 1091 vmu_object_t *shared_object = NULL; 1092 vmu_object_t *entity_object = NULL; 1093 vmu_entity_t *entity; 1094 vmusage_t *result; 1095 vmu_bound_t *first = NULL; 1096 vmu_bound_t *last = NULL; 1097 vmu_bound_t *cur = NULL; 1098 vmu_bound_t *e_first = NULL; 1099 vmu_bound_t *e_last = NULL; 1100 vmu_bound_t *tmp; 1101 pgcnt_t p_index, s_index, p_start, p_end, s_start, s_end, rss, virt; 1102 struct anon_map *private_amp = NULL; 1103 boolean_t incore = B_FALSE; 1104 boolean_t shared = B_FALSE; 1105 int file = 0; 1106 pgcnt_t swresv = 0; 1107 pgcnt_t panon = 0; 1108 1109 s_start = 0; 1110 p_end = 0; 1111 /* Can zero-length segments exist? Not sure, so paranoia. */ 1112 if (seg->s_size <= 0) 1113 return; 1114 1115 /* 1116 * Figure out if there is a shared object (such as a named vnode or 1117 * a shared amp, then figure out if there is a private amp, which 1118 * identifies private pages. 1119 */ 1120 if (seg->s_ops == &segvn_ops) { 1121 svd = (struct segvn_data *)seg->s_data; 1122 if (svd->type == MAP_SHARED) { 1123 shared = B_TRUE; 1124 } else { 1125 swresv = svd->swresv; 1126 1127 if (SEGVN_LOCK_TRYENTER(seg->s_as, &svd->lock, 1128 RW_READER) != 0) { 1129 /* 1130 * Text replication anon maps can be shared 1131 * across all zones. Space used for text 1132 * replication is typically capped as a small % 1133 * of memory. To keep it simple for now we 1134 * don't account for swap and memory space used 1135 * for text replication. 1136 */ 1137 if (svd->tr_state == SEGVN_TR_OFF && 1138 svd->amp != NULL) { 1139 private_amp = svd->amp; 1140 p_start = svd->anon_index; 1141 p_end = svd->anon_index + 1142 btop(seg->s_size) - 1; 1143 } 1144 SEGVN_LOCK_EXIT(seg->s_as, &svd->lock); 1145 } 1146 } 1147 if (svd->vp != NULL) { 1148 file = 1; 1149 shared_object = vmu_find_insert_object( 1150 vmu_data.vmu_all_vnodes_hash, (caddr_t)svd->vp, 1151 VMUSAGE_TYPE_VNODE); 1152 s_start = btop(svd->offset); 1153 s_end = btop(svd->offset + seg->s_size) - 1; 1154 } 1155 if (svd->amp != NULL && svd->type == MAP_SHARED) { 1156 ASSERT(shared_object == NULL); 1157 shared_object = vmu_find_insert_object( 1158 vmu_data.vmu_all_amps_hash, (caddr_t)svd->amp, 1159 VMUSAGE_TYPE_AMP); 1160 s_start = svd->anon_index; 1161 s_end = svd->anon_index + btop(seg->s_size) - 1; 1162 /* schedctl mappings are always in core */ 1163 if (svd->amp->swresv == 0) 1164 incore = B_TRUE; 1165 } 1166 } else if (seg->s_ops == &segspt_shmops) { 1167 shared = B_TRUE; 1168 shmd = (struct shm_data *)seg->s_data; 1169 shared_object = vmu_find_insert_object( 1170 vmu_data.vmu_all_amps_hash, (caddr_t)shmd->shm_amp, 1171 VMUSAGE_TYPE_AMP); 1172 s_start = 0; 1173 s_end = btop(seg->s_size) - 1; 1174 sptd = shmd->shm_sptseg->s_data; 1175 1176 /* ism segments are always incore and do not reserve swap */ 1177 if (sptd->spt_flags & SHM_SHARE_MMU) 1178 incore = B_TRUE; 1179 1180 } else { 1181 return; 1182 } 1183 1184 /* 1185 * If there is a private amp, count anon pages that exist. If an 1186 * anon has a refcnt > 1 (COW sharing), then save the anon in a 1187 * hash so that it is not double counted. 1188 * 1189 * If there is also a shared object, then figure out the bounds 1190 * which are not mapped by the private amp. 1191 */ 1192 if (private_amp != NULL) { 1193 1194 /* Enter as writer to prevent COW anons from being freed */ 1195 ANON_LOCK_ENTER(&private_amp->a_rwlock, RW_WRITER); 1196 1197 p_index = p_start; 1198 s_index = s_start; 1199 1200 while (p_index <= p_end) { 1201 1202 pgcnt_t p_index_next; 1203 pgcnt_t p_bound_size; 1204 int cnt; 1205 anoff_t off; 1206 struct vnode *vn; 1207 struct anon *ap; 1208 page_t *page; /* For handling of large */ 1209 pgcnt_t pgcnt = 1; /* pages */ 1210 pgcnt_t pgstart; 1211 pgcnt_t pgend; 1212 uint_t pgshft; 1213 pgcnt_t pgmsk; 1214 1215 p_index_next = p_index; 1216 ap = anon_get_next_ptr(private_amp->ahp, 1217 &p_index_next); 1218 1219 /* 1220 * If next anon is past end of mapping, simulate 1221 * end of anon so loop terminates. 1222 */ 1223 if (p_index_next > p_end) { 1224 p_index_next = p_end + 1; 1225 ap = NULL; 1226 } 1227 /* 1228 * For COW segments, keep track of bounds not 1229 * backed by private amp so they can be looked 1230 * up in the backing vnode 1231 */ 1232 if (p_index_next != p_index) { 1233 1234 /* 1235 * Compute index difference between anon and 1236 * previous anon. 1237 */ 1238 p_bound_size = p_index_next - p_index - 1; 1239 1240 if (shared_object != NULL) { 1241 cur = vmu_alloc_bound(); 1242 cur->vmb_start = s_index; 1243 cur->vmb_end = s_index + p_bound_size; 1244 cur->vmb_type = VMUSAGE_BOUND_UNKNOWN; 1245 if (first == NULL) { 1246 first = cur; 1247 last = cur; 1248 } else { 1249 last->vmb_next = cur; 1250 last = cur; 1251 } 1252 } 1253 p_index = p_index + p_bound_size + 1; 1254 s_index = s_index + p_bound_size + 1; 1255 } 1256 1257 /* Detect end of anons in amp */ 1258 if (ap == NULL) 1259 break; 1260 1261 cnt = ap->an_refcnt; 1262 swap_xlate(ap, &vn, &off); 1263 1264 if (vn == NULL || vn->v_pages == NULL || 1265 (page = page_exists(vn, off)) == NULL) { 1266 p_index++; 1267 s_index++; 1268 continue; 1269 } 1270 1271 /* 1272 * If large page is found, compute portion of large 1273 * page in mapping, and increment indicies to the next 1274 * large page. 1275 */ 1276 if (page->p_szc > 0) { 1277 1278 pgcnt = page_get_pagecnt(page->p_szc); 1279 pgshft = page_get_shift(page->p_szc); 1280 pgmsk = (0x1 << (pgshft - PAGESHIFT)) - 1; 1281 1282 /* First page in large page */ 1283 pgstart = p_index & ~pgmsk; 1284 /* Last page in large page */ 1285 pgend = pgstart + pgcnt - 1; 1286 /* 1287 * Artifically end page if page extends past 1288 * end of mapping. 1289 */ 1290 if (pgend > p_end) 1291 pgend = p_end; 1292 1293 /* 1294 * Compute number of pages from large page 1295 * which are mapped. 1296 */ 1297 pgcnt = pgend - p_index + 1; 1298 1299 /* 1300 * Point indicies at page after large page, 1301 * or at page after end of mapping. 1302 */ 1303 p_index += pgcnt; 1304 s_index += pgcnt; 1305 } else { 1306 p_index++; 1307 s_index++; 1308 } 1309 1310 /* 1311 * Assume anon structs with a refcnt 1312 * of 1 are not COW shared, so there 1313 * is no reason to track them per entity. 1314 */ 1315 if (cnt == 1) { 1316 panon += pgcnt; 1317 continue; 1318 } 1319 for (entity = vmu_entities; entity != NULL; 1320 entity = entity->vme_next_calc) { 1321 1322 result = &entity->vme_result; 1323 /* 1324 * Track COW anons per entity so 1325 * they are not double counted. 1326 */ 1327 if (vmu_find_insert_anon(entity->vme_anon_hash, 1328 (caddr_t)ap) == 0) 1329 continue; 1330 1331 result->vmu_rss_all += (pgcnt << PAGESHIFT); 1332 result->vmu_rss_private += 1333 (pgcnt << PAGESHIFT); 1334 } 1335 } 1336 ANON_LOCK_EXIT(&private_amp->a_rwlock); 1337 } 1338 1339 /* Add up resident anon and swap reserved for private mappings */ 1340 if (swresv > 0 || panon > 0) { 1341 for (entity = vmu_entities; entity != NULL; 1342 entity = entity->vme_next_calc) { 1343 result = &entity->vme_result; 1344 result->vmu_swap_all += swresv; 1345 result->vmu_swap_private += swresv; 1346 result->vmu_rss_all += (panon << PAGESHIFT); 1347 result->vmu_rss_private += (panon << PAGESHIFT); 1348 } 1349 } 1350 1351 /* Compute resident pages backing shared amp or named vnode */ 1352 if (shared_object != NULL) { 1353 avl_tree_t *tree = &(shared_object->vmo_bounds); 1354 1355 if (first == NULL) { 1356 /* 1357 * No private amp, or private amp has no anon 1358 * structs. This means entire segment is backed by 1359 * the shared object. 1360 */ 1361 first = vmu_alloc_bound(); 1362 first->vmb_start = s_start; 1363 first->vmb_end = s_end; 1364 first->vmb_type = VMUSAGE_BOUND_UNKNOWN; 1365 } 1366 /* 1367 * Iterate bounds not backed by private amp, and compute 1368 * resident pages. 1369 */ 1370 cur = first; 1371 while (cur != NULL) { 1372 1373 if (vmu_insert_lookup_object_bounds(shared_object, 1374 cur->vmb_start, cur->vmb_end, VMUSAGE_BOUND_UNKNOWN, 1375 &first, &last) > 0) { 1376 /* new bounds, find incore/not-incore */ 1377 if (shared_object->vmo_type == 1378 VMUSAGE_TYPE_VNODE) { 1379 vmu_vnode_update_incore_bounds( 1380 tree, 1381 (vnode_t *) 1382 shared_object->vmo_key, &first, 1383 &last); 1384 } else { 1385 vmu_amp_update_incore_bounds( 1386 tree, 1387 (struct anon_map *) 1388 shared_object->vmo_key, &first, 1389 &last, incore); 1390 } 1391 vmu_merge_bounds(tree, &first, &last); 1392 } 1393 for (entity = vmu_entities; entity != NULL; 1394 entity = entity->vme_next_calc) { 1395 avl_tree_t *e_tree; 1396 1397 result = &entity->vme_result; 1398 1399 entity_object = vmu_find_insert_object( 1400 shared_object->vmo_type == 1401 VMUSAGE_TYPE_VNODE ? entity->vme_vnode_hash: 1402 entity->vme_amp_hash, 1403 shared_object->vmo_key, 1404 shared_object->vmo_type); 1405 1406 virt = vmu_insert_lookup_object_bounds( 1407 entity_object, cur->vmb_start, cur->vmb_end, 1408 VMUSAGE_BOUND_UNKNOWN, &e_first, &e_last); 1409 1410 if (virt == 0) 1411 continue; 1412 /* 1413 * Range visited for this entity 1414 */ 1415 e_tree = &(entity_object->vmo_bounds); 1416 rss = vmu_update_bounds(e_tree, &e_first, 1417 &e_last, tree, first, last); 1418 result->vmu_rss_all += (rss << PAGESHIFT); 1419 if (shared == B_TRUE && file == B_FALSE) { 1420 /* shared anon mapping */ 1421 result->vmu_swap_all += 1422 (virt << PAGESHIFT); 1423 result->vmu_swap_shared += 1424 (virt << PAGESHIFT); 1425 result->vmu_rss_shared += 1426 (rss << PAGESHIFT); 1427 } else if (shared == B_TRUE && file == B_TRUE) { 1428 /* shared file mapping */ 1429 result->vmu_rss_shared += 1430 (rss << PAGESHIFT); 1431 } else if (shared == B_FALSE && 1432 file == B_TRUE) { 1433 /* private file mapping */ 1434 result->vmu_rss_private += 1435 (rss << PAGESHIFT); 1436 } 1437 vmu_merge_bounds(e_tree, &e_first, &e_last); 1438 } 1439 tmp = cur; 1440 cur = cur->vmb_next; 1441 vmu_free_bound(tmp); 1442 } 1443 } 1444 } 1445 1446 /* 1447 * Based on the current calculation flags, find the relevant entities 1448 * which are relative to the process. Then calculate each segment 1449 * in the process'es address space for each relevant entity. 1450 */ 1451 static void 1452 vmu_calculate_proc(proc_t *p) 1453 { 1454 vmu_entity_t *entities = NULL; 1455 vmu_zone_t *zone; 1456 vmu_entity_t *tmp; 1457 struct as *as; 1458 struct seg *seg; 1459 int ret; 1460 1461 /* Figure out which entities are being computed */ 1462 if ((vmu_data.vmu_system) != NULL) { 1463 tmp = vmu_data.vmu_system; 1464 tmp->vme_next_calc = entities; 1465 entities = tmp; 1466 } 1467 if (vmu_data.vmu_calc_flags & 1468 (VMUSAGE_ZONE | VMUSAGE_ALL_ZONES | VMUSAGE_PROJECTS | 1469 VMUSAGE_ALL_PROJECTS | VMUSAGE_TASKS | VMUSAGE_ALL_TASKS | 1470 VMUSAGE_RUSERS | VMUSAGE_ALL_RUSERS | VMUSAGE_EUSERS | 1471 VMUSAGE_ALL_EUSERS)) { 1472 ret = i_mod_hash_find_nosync(vmu_data.vmu_zones_hash, 1473 (mod_hash_key_t)(uintptr_t)p->p_zone->zone_id, 1474 (mod_hash_val_t *)&zone); 1475 if (ret != 0) { 1476 zone = vmu_alloc_zone(p->p_zone->zone_id); 1477 ret = i_mod_hash_insert_nosync(vmu_data.vmu_zones_hash, 1478 (mod_hash_key_t)(uintptr_t)p->p_zone->zone_id, 1479 (mod_hash_val_t)zone, (mod_hash_hndl_t)0); 1480 ASSERT(ret == 0); 1481 } 1482 if (zone->vmz_zone != NULL) { 1483 tmp = zone->vmz_zone; 1484 tmp->vme_next_calc = entities; 1485 entities = tmp; 1486 } 1487 if (vmu_data.vmu_calc_flags & 1488 (VMUSAGE_PROJECTS | VMUSAGE_ALL_PROJECTS)) { 1489 tmp = vmu_find_insert_entity(zone->vmz_projects_hash, 1490 p->p_task->tk_proj->kpj_id, VMUSAGE_PROJECTS, 1491 zone->vmz_id); 1492 tmp->vme_next_calc = entities; 1493 entities = tmp; 1494 } 1495 if (vmu_data.vmu_calc_flags & 1496 (VMUSAGE_TASKS | VMUSAGE_ALL_TASKS)) { 1497 tmp = vmu_find_insert_entity(zone->vmz_tasks_hash, 1498 p->p_task->tk_tkid, VMUSAGE_TASKS, zone->vmz_id); 1499 tmp->vme_next_calc = entities; 1500 entities = tmp; 1501 } 1502 if (vmu_data.vmu_calc_flags & 1503 (VMUSAGE_RUSERS | VMUSAGE_ALL_RUSERS)) { 1504 tmp = vmu_find_insert_entity(zone->vmz_rusers_hash, 1505 crgetruid(p->p_cred), VMUSAGE_RUSERS, zone->vmz_id); 1506 tmp->vme_next_calc = entities; 1507 entities = tmp; 1508 } 1509 if (vmu_data.vmu_calc_flags & 1510 (VMUSAGE_EUSERS | VMUSAGE_ALL_EUSERS)) { 1511 tmp = vmu_find_insert_entity(zone->vmz_eusers_hash, 1512 crgetuid(p->p_cred), VMUSAGE_EUSERS, zone->vmz_id); 1513 tmp->vme_next_calc = entities; 1514 entities = tmp; 1515 } 1516 } 1517 /* Entities which collapse projects and users for all zones */ 1518 if (vmu_data.vmu_calc_flags & VMUSAGE_COL_PROJECTS) { 1519 tmp = vmu_find_insert_entity(vmu_data.vmu_projects_col_hash, 1520 p->p_task->tk_proj->kpj_id, VMUSAGE_PROJECTS, ALL_ZONES); 1521 tmp->vme_next_calc = entities; 1522 entities = tmp; 1523 } 1524 if (vmu_data.vmu_calc_flags & VMUSAGE_COL_RUSERS) { 1525 tmp = vmu_find_insert_entity(vmu_data.vmu_rusers_col_hash, 1526 crgetruid(p->p_cred), VMUSAGE_RUSERS, ALL_ZONES); 1527 tmp->vme_next_calc = entities; 1528 entities = tmp; 1529 } 1530 if (vmu_data.vmu_calc_flags & VMUSAGE_COL_EUSERS) { 1531 tmp = vmu_find_insert_entity(vmu_data.vmu_eusers_col_hash, 1532 crgetuid(p->p_cred), VMUSAGE_EUSERS, ALL_ZONES); 1533 tmp->vme_next_calc = entities; 1534 entities = tmp; 1535 } 1536 1537 ASSERT(entities != NULL); 1538 /* process all segs in process's address space */ 1539 as = p->p_as; 1540 AS_LOCK_ENTER(as, RW_READER); 1541 for (seg = AS_SEGFIRST(as); seg != NULL; 1542 seg = AS_SEGNEXT(as, seg)) { 1543 vmu_calculate_seg(entities, seg); 1544 } 1545 AS_LOCK_EXIT(as); 1546 } 1547 1548 /* 1549 * Free data created by previous call to vmu_calculate(). 1550 */ 1551 static void 1552 vmu_clear_calc() 1553 { 1554 if (vmu_data.vmu_system != NULL) { 1555 vmu_free_entity(vmu_data.vmu_system); 1556 vmu_data.vmu_system = NULL; 1557 } 1558 if (vmu_data.vmu_zones_hash != NULL) 1559 i_mod_hash_clear_nosync(vmu_data.vmu_zones_hash); 1560 if (vmu_data.vmu_projects_col_hash != NULL) 1561 i_mod_hash_clear_nosync(vmu_data.vmu_projects_col_hash); 1562 if (vmu_data.vmu_rusers_col_hash != NULL) 1563 i_mod_hash_clear_nosync(vmu_data.vmu_rusers_col_hash); 1564 if (vmu_data.vmu_eusers_col_hash != NULL) 1565 i_mod_hash_clear_nosync(vmu_data.vmu_eusers_col_hash); 1566 1567 i_mod_hash_clear_nosync(vmu_data.vmu_all_vnodes_hash); 1568 i_mod_hash_clear_nosync(vmu_data.vmu_all_amps_hash); 1569 } 1570 1571 /* 1572 * Free unused data structures. These can result if the system workload 1573 * decreases between calculations. 1574 */ 1575 static void 1576 vmu_free_extra() 1577 { 1578 vmu_bound_t *tb; 1579 vmu_object_t *to; 1580 vmu_entity_t *te; 1581 vmu_zone_t *tz; 1582 1583 while (vmu_data.vmu_free_bounds != NULL) { 1584 tb = vmu_data.vmu_free_bounds; 1585 vmu_data.vmu_free_bounds = vmu_data.vmu_free_bounds->vmb_next; 1586 kmem_cache_free(vmu_bound_cache, tb); 1587 } 1588 while (vmu_data.vmu_free_objects != NULL) { 1589 to = vmu_data.vmu_free_objects; 1590 vmu_data.vmu_free_objects = 1591 vmu_data.vmu_free_objects->vmo_next; 1592 kmem_cache_free(vmu_object_cache, to); 1593 } 1594 while (vmu_data.vmu_free_entities != NULL) { 1595 te = vmu_data.vmu_free_entities; 1596 vmu_data.vmu_free_entities = 1597 vmu_data.vmu_free_entities->vme_next; 1598 if (te->vme_vnode_hash != NULL) 1599 mod_hash_destroy_hash(te->vme_vnode_hash); 1600 if (te->vme_amp_hash != NULL) 1601 mod_hash_destroy_hash(te->vme_amp_hash); 1602 if (te->vme_anon_hash != NULL) 1603 mod_hash_destroy_hash(te->vme_anon_hash); 1604 kmem_free(te, sizeof (vmu_entity_t)); 1605 } 1606 while (vmu_data.vmu_free_zones != NULL) { 1607 tz = vmu_data.vmu_free_zones; 1608 vmu_data.vmu_free_zones = 1609 vmu_data.vmu_free_zones->vmz_next; 1610 if (tz->vmz_projects_hash != NULL) 1611 mod_hash_destroy_hash(tz->vmz_projects_hash); 1612 if (tz->vmz_tasks_hash != NULL) 1613 mod_hash_destroy_hash(tz->vmz_tasks_hash); 1614 if (tz->vmz_rusers_hash != NULL) 1615 mod_hash_destroy_hash(tz->vmz_rusers_hash); 1616 if (tz->vmz_eusers_hash != NULL) 1617 mod_hash_destroy_hash(tz->vmz_eusers_hash); 1618 kmem_free(tz, sizeof (vmu_zone_t)); 1619 } 1620 } 1621 1622 extern kcondvar_t *pr_pid_cv; 1623 1624 /* 1625 * Determine which entity types are relevant and allocate the hashes to 1626 * track them. Then walk the process table and count rss and swap 1627 * for each process'es address space. Address space object such as 1628 * vnodes, amps and anons are tracked per entity, so that they are 1629 * not double counted in the results. 1630 * 1631 */ 1632 static void 1633 vmu_calculate() 1634 { 1635 int i = 0; 1636 int ret; 1637 proc_t *p; 1638 1639 vmu_clear_calc(); 1640 1641 if (vmu_data.vmu_calc_flags & VMUSAGE_SYSTEM) 1642 vmu_data.vmu_system = vmu_alloc_entity(0, VMUSAGE_SYSTEM, 1643 ALL_ZONES); 1644 1645 /* 1646 * Walk process table and calculate rss of each proc. 1647 * 1648 * Pidlock and p_lock cannot be held while doing the rss calculation. 1649 * This is because: 1650 * 1. The calculation allocates using KM_SLEEP. 1651 * 2. The calculation grabs a_lock, which cannot be grabbed 1652 * after p_lock. 1653 * 1654 * Since pidlock must be dropped, we cannot simply just walk the 1655 * practive list. Instead, we walk the process table, and sprlock 1656 * each process to ensure that it does not exit during the 1657 * calculation. 1658 */ 1659 1660 mutex_enter(&pidlock); 1661 for (i = 0; i < v.v_proc; i++) { 1662 again: 1663 p = pid_entry(i); 1664 if (p == NULL) 1665 continue; 1666 1667 mutex_enter(&p->p_lock); 1668 mutex_exit(&pidlock); 1669 1670 if (panicstr) { 1671 mutex_exit(&p->p_lock); 1672 return; 1673 } 1674 1675 /* Try to set P_PR_LOCK */ 1676 ret = sprtrylock_proc(p); 1677 if (ret == -1) { 1678 /* Process in invalid state */ 1679 mutex_exit(&p->p_lock); 1680 mutex_enter(&pidlock); 1681 continue; 1682 } else if (ret == 1) { 1683 /* 1684 * P_PR_LOCK is already set. Wait and try again. 1685 * This also drops p_lock. 1686 */ 1687 sprwaitlock_proc(p); 1688 mutex_enter(&pidlock); 1689 goto again; 1690 } 1691 mutex_exit(&p->p_lock); 1692 1693 vmu_calculate_proc(p); 1694 1695 mutex_enter(&p->p_lock); 1696 sprunlock(p); 1697 mutex_enter(&pidlock); 1698 } 1699 mutex_exit(&pidlock); 1700 1701 vmu_free_extra(); 1702 } 1703 1704 /* 1705 * allocate a new cache for N results satisfying flags 1706 */ 1707 vmu_cache_t * 1708 vmu_cache_alloc(size_t nres, uint_t flags) 1709 { 1710 vmu_cache_t *cache; 1711 1712 cache = kmem_zalloc(sizeof (vmu_cache_t), KM_SLEEP); 1713 cache->vmc_results = kmem_zalloc(sizeof (vmusage_t) * nres, KM_SLEEP); 1714 cache->vmc_nresults = nres; 1715 cache->vmc_flags = flags; 1716 cache->vmc_refcnt = 1; 1717 return (cache); 1718 } 1719 1720 /* 1721 * Make sure cached results are not freed 1722 */ 1723 static void 1724 vmu_cache_hold(vmu_cache_t *cache) 1725 { 1726 ASSERT(MUTEX_HELD(&vmu_data.vmu_lock)); 1727 cache->vmc_refcnt++; 1728 } 1729 1730 /* 1731 * free cache data 1732 */ 1733 static void 1734 vmu_cache_rele(vmu_cache_t *cache) 1735 { 1736 ASSERT(MUTEX_HELD(&vmu_data.vmu_lock)); 1737 ASSERT(cache->vmc_refcnt > 0); 1738 cache->vmc_refcnt--; 1739 if (cache->vmc_refcnt == 0) { 1740 kmem_free(cache->vmc_results, sizeof (vmusage_t) * 1741 cache->vmc_nresults); 1742 kmem_free(cache, sizeof (vmu_cache_t)); 1743 } 1744 } 1745 1746 /* 1747 * Copy out the cached results to a caller. Inspect the callers flags 1748 * and zone to determine which cached results should be copied. 1749 */ 1750 static int 1751 vmu_copyout_results(vmu_cache_t *cache, vmusage_t *buf, size_t *nres, 1752 uint_t flags, uio_seg_t seg) 1753 { 1754 vmusage_t *result, *out_result; 1755 vmusage_t dummy; 1756 size_t i, count = 0; 1757 size_t bufsize; 1758 int ret = 0; 1759 uint_t types = 0; 1760 1761 if (nres != NULL) { 1762 if (uio_copyin(nres, &bufsize, sizeof (size_t), seg) != 0) 1763 return (set_errno(EFAULT)); 1764 } else { 1765 bufsize = 0; 1766 } 1767 1768 /* figure out what results the caller is interested in. */ 1769 if ((flags & VMUSAGE_SYSTEM) && curproc->p_zone == global_zone) 1770 types |= VMUSAGE_SYSTEM; 1771 if (flags & (VMUSAGE_ZONE | VMUSAGE_ALL_ZONES)) 1772 types |= VMUSAGE_ZONE; 1773 if (flags & (VMUSAGE_PROJECTS | VMUSAGE_ALL_PROJECTS | 1774 VMUSAGE_COL_PROJECTS)) 1775 types |= VMUSAGE_PROJECTS; 1776 if (flags & (VMUSAGE_TASKS | VMUSAGE_ALL_TASKS)) 1777 types |= VMUSAGE_TASKS; 1778 if (flags & (VMUSAGE_RUSERS | VMUSAGE_ALL_RUSERS | VMUSAGE_COL_RUSERS)) 1779 types |= VMUSAGE_RUSERS; 1780 if (flags & (VMUSAGE_EUSERS | VMUSAGE_ALL_EUSERS | VMUSAGE_COL_EUSERS)) 1781 types |= VMUSAGE_EUSERS; 1782 1783 /* count results for current zone */ 1784 out_result = buf; 1785 for (result = cache->vmc_results, i = 0; 1786 i < cache->vmc_nresults; result++, i++) { 1787 1788 /* Do not return "other-zone" results to non-global zones */ 1789 if (curproc->p_zone != global_zone && 1790 curproc->p_zone->zone_id != result->vmu_zoneid) 1791 continue; 1792 1793 /* 1794 * If non-global zone requests VMUSAGE_SYSTEM, fake 1795 * up VMUSAGE_ZONE result as VMUSAGE_SYSTEM result. 1796 */ 1797 if (curproc->p_zone != global_zone && 1798 (flags & VMUSAGE_SYSTEM) != 0 && 1799 result->vmu_type == VMUSAGE_ZONE) { 1800 count++; 1801 if (out_result != NULL) { 1802 if (bufsize < count) { 1803 ret = set_errno(EOVERFLOW); 1804 } else { 1805 dummy = *result; 1806 dummy.vmu_zoneid = ALL_ZONES; 1807 dummy.vmu_id = 0; 1808 dummy.vmu_type = VMUSAGE_SYSTEM; 1809 if (uio_copyout(&dummy, out_result, 1810 sizeof (vmusage_t), seg) != 0) 1811 return (set_errno(EFAULT)); 1812 out_result++; 1813 } 1814 } 1815 } 1816 1817 /* Skip results that do not match requested type */ 1818 if ((result->vmu_type & types) == 0) 1819 continue; 1820 1821 /* Skip collated results if not requested */ 1822 if (result->vmu_zoneid == ALL_ZONES) { 1823 if (result->vmu_type == VMUSAGE_PROJECTS && 1824 (flags & VMUSAGE_COL_PROJECTS) == 0) 1825 continue; 1826 if (result->vmu_type == VMUSAGE_EUSERS && 1827 (flags & VMUSAGE_COL_EUSERS) == 0) 1828 continue; 1829 if (result->vmu_type == VMUSAGE_RUSERS && 1830 (flags & VMUSAGE_COL_RUSERS) == 0) 1831 continue; 1832 } 1833 1834 /* Skip "other zone" results if not requested */ 1835 if (result->vmu_zoneid != curproc->p_zone->zone_id) { 1836 if (result->vmu_type == VMUSAGE_ZONE && 1837 (flags & VMUSAGE_ALL_ZONES) == 0) 1838 continue; 1839 if (result->vmu_type == VMUSAGE_PROJECTS && 1840 (flags & (VMUSAGE_ALL_PROJECTS | 1841 VMUSAGE_COL_PROJECTS)) == 0) 1842 continue; 1843 if (result->vmu_type == VMUSAGE_TASKS && 1844 (flags & VMUSAGE_ALL_TASKS) == 0) 1845 continue; 1846 if (result->vmu_type == VMUSAGE_RUSERS && 1847 (flags & (VMUSAGE_ALL_RUSERS | 1848 VMUSAGE_COL_RUSERS)) == 0) 1849 continue; 1850 if (result->vmu_type == VMUSAGE_EUSERS && 1851 (flags & (VMUSAGE_ALL_EUSERS | 1852 VMUSAGE_COL_EUSERS)) == 0) 1853 continue; 1854 } 1855 count++; 1856 if (out_result != NULL) { 1857 if (bufsize < count) { 1858 ret = set_errno(EOVERFLOW); 1859 } else { 1860 if (uio_copyout(result, out_result, 1861 sizeof (vmusage_t), seg) != 0) 1862 return (set_errno(EFAULT)); 1863 out_result++; 1864 } 1865 } 1866 } 1867 if (nres != NULL) 1868 if (uio_copyout(&count, nres, sizeof (size_t), seg) != 0) 1869 return (set_errno(EFAULT)); 1870 1871 return (ret); 1872 } 1873 1874 /* 1875 * vm_getusage() 1876 * 1877 * Counts rss and swap by zone, project, task, and/or user. The flags argument 1878 * determines the type of results structures returned. Flags requesting 1879 * results from more than one zone are "flattened" to the local zone if the 1880 * caller is not the global zone. 1881 * 1882 * args: 1883 * flags: bitmap consisting of one or more of VMUSAGE_*. 1884 * age: maximum allowable age (time since counting was done) in 1885 * seconds of the results. Results from previous callers are 1886 * cached in kernel. 1887 * buf: pointer to buffer array of vmusage_t. If NULL, then only nres 1888 * set on success. 1889 * nres: Set to number of vmusage_t structures pointed to by buf 1890 * before calling vm_getusage(). 1891 * On return 0 (success) or ENOSPC, is set to the number of result 1892 * structures returned or attempted to return. 1893 * seg: address space in which buf and nres reside, either 1894 * UIO_USERSPACE or UIO_SYSSPACE. 1895 * 1896 * returns 0 on success, -1 on failure: 1897 * EINTR (interrupted) 1898 * ENOSPC (nres to small for results, nres set to needed value for success) 1899 * EINVAL (flags invalid) 1900 * EFAULT (bad address for buf or nres) 1901 */ 1902 int 1903 vm_getusage(uint_t flags, time_t age, vmusage_t *buf, size_t *nres, 1904 uio_seg_t seg) 1905 { 1906 vmu_entity_t *entity; 1907 vmusage_t *result; 1908 int ret = 0; 1909 int cacherecent = 0; 1910 hrtime_t now; 1911 uint_t flags_orig; 1912 1913 /* 1914 * Non-global zones cannot request system wide and/or collated 1915 * results, or the system result, so munge the flags accordingly. 1916 */ 1917 flags_orig = flags; 1918 if (curproc->p_zone != global_zone) { 1919 if (flags & (VMUSAGE_ALL_PROJECTS | VMUSAGE_COL_PROJECTS)) { 1920 flags &= ~(VMUSAGE_ALL_PROJECTS | VMUSAGE_COL_PROJECTS); 1921 flags |= VMUSAGE_PROJECTS; 1922 } 1923 if (flags & (VMUSAGE_ALL_RUSERS | VMUSAGE_COL_RUSERS)) { 1924 flags &= ~(VMUSAGE_ALL_RUSERS | VMUSAGE_COL_RUSERS); 1925 flags |= VMUSAGE_RUSERS; 1926 } 1927 if (flags & (VMUSAGE_ALL_EUSERS | VMUSAGE_COL_EUSERS)) { 1928 flags &= ~(VMUSAGE_ALL_EUSERS | VMUSAGE_COL_EUSERS); 1929 flags |= VMUSAGE_EUSERS; 1930 } 1931 if (flags & VMUSAGE_SYSTEM) { 1932 flags &= ~VMUSAGE_SYSTEM; 1933 flags |= VMUSAGE_ZONE; 1934 } 1935 } 1936 1937 /* Check for unknown flags */ 1938 if ((flags & (~VMUSAGE_MASK)) != 0) 1939 return (set_errno(EINVAL)); 1940 1941 /* Check for no flags */ 1942 if ((flags & VMUSAGE_MASK) == 0) 1943 return (set_errno(EINVAL)); 1944 1945 mutex_enter(&vmu_data.vmu_lock); 1946 now = gethrtime(); 1947 1948 start: 1949 if (vmu_data.vmu_cache != NULL) { 1950 1951 vmu_cache_t *cache; 1952 1953 if ((vmu_data.vmu_cache->vmc_timestamp + 1954 ((hrtime_t)age * NANOSEC)) > now) 1955 cacherecent = 1; 1956 1957 if ((vmu_data.vmu_cache->vmc_flags & flags) == flags && 1958 cacherecent == 1) { 1959 cache = vmu_data.vmu_cache; 1960 vmu_cache_hold(cache); 1961 mutex_exit(&vmu_data.vmu_lock); 1962 1963 ret = vmu_copyout_results(cache, buf, nres, flags_orig, 1964 seg); 1965 mutex_enter(&vmu_data.vmu_lock); 1966 vmu_cache_rele(cache); 1967 if (vmu_data.vmu_pending_waiters > 0) 1968 cv_broadcast(&vmu_data.vmu_cv); 1969 mutex_exit(&vmu_data.vmu_lock); 1970 return (ret); 1971 } 1972 /* 1973 * If the cache is recent, it is likely that there are other 1974 * consumers of vm_getusage running, so add their flags to the 1975 * desired flags for the calculation. 1976 */ 1977 if (cacherecent == 1) 1978 flags = vmu_data.vmu_cache->vmc_flags | flags; 1979 } 1980 if (vmu_data.vmu_calc_thread == NULL) { 1981 1982 vmu_cache_t *cache; 1983 1984 vmu_data.vmu_calc_thread = curthread; 1985 vmu_data.vmu_calc_flags = flags; 1986 vmu_data.vmu_entities = NULL; 1987 vmu_data.vmu_nentities = 0; 1988 if (vmu_data.vmu_pending_waiters > 0) 1989 vmu_data.vmu_calc_flags |= 1990 vmu_data.vmu_pending_flags; 1991 1992 vmu_data.vmu_pending_flags = 0; 1993 mutex_exit(&vmu_data.vmu_lock); 1994 vmu_calculate(); 1995 mutex_enter(&vmu_data.vmu_lock); 1996 /* copy results to cache */ 1997 if (vmu_data.vmu_cache != NULL) 1998 vmu_cache_rele(vmu_data.vmu_cache); 1999 cache = vmu_data.vmu_cache = 2000 vmu_cache_alloc(vmu_data.vmu_nentities, 2001 vmu_data.vmu_calc_flags); 2002 2003 result = cache->vmc_results; 2004 for (entity = vmu_data.vmu_entities; entity != NULL; 2005 entity = entity->vme_next) { 2006 *result = entity->vme_result; 2007 result++; 2008 } 2009 cache->vmc_timestamp = gethrtime(); 2010 vmu_cache_hold(cache); 2011 2012 vmu_data.vmu_calc_flags = 0; 2013 vmu_data.vmu_calc_thread = NULL; 2014 2015 if (vmu_data.vmu_pending_waiters > 0) 2016 cv_broadcast(&vmu_data.vmu_cv); 2017 2018 mutex_exit(&vmu_data.vmu_lock); 2019 2020 /* copy cache */ 2021 ret = vmu_copyout_results(cache, buf, nres, flags_orig, seg); 2022 mutex_enter(&vmu_data.vmu_lock); 2023 vmu_cache_rele(cache); 2024 mutex_exit(&vmu_data.vmu_lock); 2025 2026 return (ret); 2027 } 2028 vmu_data.vmu_pending_flags |= flags; 2029 vmu_data.vmu_pending_waiters++; 2030 while (vmu_data.vmu_calc_thread != NULL) { 2031 if (cv_wait_sig(&vmu_data.vmu_cv, 2032 &vmu_data.vmu_lock) == 0) { 2033 vmu_data.vmu_pending_waiters--; 2034 mutex_exit(&vmu_data.vmu_lock); 2035 return (set_errno(EINTR)); 2036 } 2037 } 2038 vmu_data.vmu_pending_waiters--; 2039 goto start; 2040 } 2041