13247Sgjelinek /* 23247Sgjelinek * CDDL HEADER START 33247Sgjelinek * 43247Sgjelinek * The contents of this file are subject to the terms of the 53247Sgjelinek * Common Development and Distribution License (the "License"). 63247Sgjelinek * You may not use this file except in compliance with the License. 73247Sgjelinek * 83247Sgjelinek * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 93247Sgjelinek * or http://www.opensolaris.org/os/licensing. 103247Sgjelinek * See the License for the specific language governing permissions 113247Sgjelinek * and limitations under the License. 123247Sgjelinek * 133247Sgjelinek * When distributing Covered Code, include this CDDL HEADER in each 143247Sgjelinek * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 153247Sgjelinek * If applicable, add the following below this CDDL HEADER, with the 163247Sgjelinek * fields enclosed by brackets "[]" replaced with your own identifying 173247Sgjelinek * information: Portions Copyright [yyyy] [name of copyright owner] 183247Sgjelinek * 193247Sgjelinek * CDDL HEADER END 203247Sgjelinek */ 213247Sgjelinek 223247Sgjelinek /* 233671Ssl108498 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 243247Sgjelinek * Use is subject to license terms. 253247Sgjelinek */ 263247Sgjelinek 273247Sgjelinek #pragma ident "%Z%%M% %I% %E% SMI" 283247Sgjelinek 293247Sgjelinek /* 303247Sgjelinek * vm_usage 313247Sgjelinek * 323247Sgjelinek * This file implements the getvmusage() private system call. 333247Sgjelinek * getvmusage() counts the amount of resident memory pages and swap 343247Sgjelinek * reserved by the specified process collective. A "process collective" is 353247Sgjelinek * the set of processes owned by a particular, zone, project, task, or user. 363247Sgjelinek * 373247Sgjelinek * rss and swap are counted so that for a given process collective, a page is 383247Sgjelinek * only counted once. For example, this means that if multiple processes in 393247Sgjelinek * the same project map the same page, then the project will only be charged 403247Sgjelinek * once for that page. On the other hand, if two processes in different 413247Sgjelinek * projects map the same page, then both projects will be charged 423247Sgjelinek * for the page. 433247Sgjelinek * 443247Sgjelinek * The vm_getusage() calculation is implemented so that the first thread 453247Sgjelinek * performs the rss/swap counting. Other callers will wait for that thread to 463247Sgjelinek * finish, copying the results. This enables multiple rcapds and prstats to 473247Sgjelinek * consume data from the same calculation. The results are also cached so that 483247Sgjelinek * a caller interested in recent results can just copy them instead of starting 493247Sgjelinek * a new calculation. The caller passes the maximium age (in seconds) of the 503247Sgjelinek * data. If the cached data is young enough, the cache is copied, otherwise, 513247Sgjelinek * a new calculation is executed and the cache is replaced with the new 523247Sgjelinek * data. 533247Sgjelinek * 543247Sgjelinek * The rss calculation for each process collective is as follows: 553247Sgjelinek * 563247Sgjelinek * - Inspect flags, determine if counting rss for zones, projects, tasks, 573247Sgjelinek * and/or users. 583247Sgjelinek * - For each proc: 593247Sgjelinek * - Figure out proc's collectives (zone, project, task, and/or user). 603247Sgjelinek * - For each seg in proc's address space: 613247Sgjelinek * - If seg is private: 623247Sgjelinek * - Lookup anons in the amp. 633247Sgjelinek * - For incore pages not previously visited each of the 643247Sgjelinek * proc's collectives, add incore pagesize to each. 653247Sgjelinek * collective. 663247Sgjelinek * Anon's with a refcnt of 1 can be assummed to be not 673247Sgjelinek * previously visited. 683247Sgjelinek * - For address ranges without anons in the amp: 693247Sgjelinek * - Lookup pages in underlying vnode. 703247Sgjelinek * - For incore pages not previously visiting for 713247Sgjelinek * each of the proc's collectives, add incore 723247Sgjelinek * pagesize to each collective. 733247Sgjelinek * - If seg is shared: 743247Sgjelinek * - Lookup pages in the shared amp or vnode. 753247Sgjelinek * - For incore pages not previously visited for each of 763247Sgjelinek * the proc's collectives, add incore pagesize to each 773247Sgjelinek * collective. 783247Sgjelinek * 793247Sgjelinek * Swap is reserved by private segments, and shared anonymous segments. 803247Sgjelinek * The only shared anon segments which do not reserve swap are ISM segments 813247Sgjelinek * and schedctl segments, both of which can be identified by having 823247Sgjelinek * amp->swresv == 0. 833247Sgjelinek * 843247Sgjelinek * The swap calculation for each collective is as follows: 853247Sgjelinek * 863247Sgjelinek * - Inspect flags, determine if counting rss for zones, projects, tasks, 873247Sgjelinek * and/or users. 883247Sgjelinek * - For each proc: 893247Sgjelinek * - Figure out proc's collectives (zone, project, task, and/or user). 903247Sgjelinek * - For each seg in proc's address space: 913247Sgjelinek * - If seg is private: 923247Sgjelinek * - Add svd->swresv pages to swap count for each of the 933247Sgjelinek * proc's collectives. 943247Sgjelinek * - If seg is anon, shared, and amp->swresv != 0 953247Sgjelinek * - For address ranges in amp not previously visited for 963247Sgjelinek * each of the proc's collectives, add size of address 973247Sgjelinek * range to the swap count for each collective. 983247Sgjelinek * 993247Sgjelinek * These two calculations are done simultaneously, with most of the work 1003247Sgjelinek * being done in vmu_calculate_seg(). The results of the calculation are 1013247Sgjelinek * copied into "vmu_data.vmu_cache_results". 1023247Sgjelinek * 1033247Sgjelinek * To perform the calculation, various things are tracked and cached: 1043247Sgjelinek * 1053247Sgjelinek * - incore/not-incore page ranges for all vnodes. 1063247Sgjelinek * (vmu_data.vmu_all_vnodes_hash) 1073247Sgjelinek * This eliminates looking up the same page more than once. 1083247Sgjelinek * 1093247Sgjelinek * - incore/not-incore page ranges for all shared amps. 1103247Sgjelinek * (vmu_data.vmu_all_amps_hash) 1113247Sgjelinek * This eliminates looking up the same page more than once. 1123247Sgjelinek * 1133247Sgjelinek * - visited page ranges for each collective. 1143247Sgjelinek * - per vnode (entity->vme_vnode_hash) 1153247Sgjelinek * - per shared amp (entity->vme_amp_hash) 1163247Sgjelinek * For accurate counting of map-shared and cow-shared pages. 1173247Sgjelinek * 1183247Sgjelinek * - visited private anons (refcnt > 1) for each collective. 1193247Sgjelinek * (entity->vme_anon_hash) 1203247Sgjelinek * For accurate counting of cow-shared pages. 1213247Sgjelinek * 1223247Sgjelinek * The common accounting structure is the vmu_entity_t, which represents 1233247Sgjelinek * collectives: 1243247Sgjelinek * 1253247Sgjelinek * - A zone. 1263247Sgjelinek * - A project, task, or user within a zone. 1273247Sgjelinek * - The entire system (vmu_data.vmu_system). 1283247Sgjelinek * - Each collapsed (col) project and user. This means a given projid or 1293247Sgjelinek * uid, regardless of which zone the process is in. For instance, 1303247Sgjelinek * project 0 in the global zone and project 0 in a non global zone are 1313247Sgjelinek * the same collapsed project. 1323247Sgjelinek * 1333247Sgjelinek * Each entity structure tracks which pages have been already visited for 1343247Sgjelinek * that entity (via previously inspected processes) so that these pages are 1353247Sgjelinek * not double counted. 1363247Sgjelinek */ 1373247Sgjelinek 1383247Sgjelinek #include <sys/errno.h> 1393247Sgjelinek #include <sys/types.h> 1403247Sgjelinek #include <sys/zone.h> 1413247Sgjelinek #include <sys/proc.h> 1423247Sgjelinek #include <sys/project.h> 1433247Sgjelinek #include <sys/task.h> 1443247Sgjelinek #include <sys/thread.h> 1453247Sgjelinek #include <sys/time.h> 1463247Sgjelinek #include <sys/mman.h> 1473247Sgjelinek #include <sys/modhash.h> 1483247Sgjelinek #include <sys/modhash_impl.h> 1493247Sgjelinek #include <sys/shm.h> 1503247Sgjelinek #include <sys/swap.h> 1513247Sgjelinek #include <sys/synch.h> 1523247Sgjelinek #include <sys/systm.h> 1533247Sgjelinek #include <sys/var.h> 1543247Sgjelinek #include <sys/vm_usage.h> 1553247Sgjelinek #include <sys/zone.h> 1563247Sgjelinek #include <vm/anon.h> 1573247Sgjelinek #include <vm/as.h> 1583247Sgjelinek #include <vm/seg_vn.h> 1593247Sgjelinek #include <vm/seg_spt.h> 1603247Sgjelinek 1613247Sgjelinek #define VMUSAGE_HASH_SIZE 512 1623247Sgjelinek 1633247Sgjelinek #define VMUSAGE_TYPE_VNODE 1 1643247Sgjelinek #define VMUSAGE_TYPE_AMP 2 1653247Sgjelinek #define VMUSAGE_TYPE_ANON 3 1663247Sgjelinek 1673247Sgjelinek #define VMUSAGE_BOUND_UNKNOWN 0 1683247Sgjelinek #define VMUSAGE_BOUND_INCORE 1 1693247Sgjelinek #define VMUSAGE_BOUND_NOT_INCORE 2 1703247Sgjelinek 1713247Sgjelinek /* 1723247Sgjelinek * bounds for vnodes and shared amps 1733247Sgjelinek * Each bound is either entirely incore, entirely not in core, or 1743247Sgjelinek * entirely unknown. bounds are stored in order by offset. 1753247Sgjelinek */ 1763247Sgjelinek typedef struct vmu_bound { 1773247Sgjelinek struct vmu_bound *vmb_next; 1783247Sgjelinek pgcnt_t vmb_start; /* page offset in vnode/amp on which bound starts */ 1793247Sgjelinek pgcnt_t vmb_end; /* page offset in vnode/amp on which bound ends */ 1803247Sgjelinek char vmb_type; /* One of VMUSAGE_BOUND_* */ 1813247Sgjelinek } vmu_bound_t; 1823247Sgjelinek 1833247Sgjelinek /* 1843247Sgjelinek * hash of visited objects (vnodes or shared amps) 1853247Sgjelinek * key is address of vnode or amp. Bounds lists known incore/non-incore 1863247Sgjelinek * bounds for vnode/amp. 1873247Sgjelinek */ 1883247Sgjelinek typedef struct vmu_object { 1893247Sgjelinek struct vmu_object *vmo_next; /* free list */ 1903247Sgjelinek caddr_t vmo_key; 1913247Sgjelinek short vmo_type; 1923247Sgjelinek vmu_bound_t *vmo_bounds; 1933247Sgjelinek } vmu_object_t; 1943247Sgjelinek 1953247Sgjelinek /* 1963247Sgjelinek * Entity by which to count results. 1973247Sgjelinek * 1983247Sgjelinek * The entity structure keeps the current rss/swap counts for each entity 1993247Sgjelinek * (zone, project, etc), and hashes of vm structures that have already 2003247Sgjelinek * been visited for the entity. 2013247Sgjelinek * 2023247Sgjelinek * vme_next: links the list of all entities currently being counted by 2033247Sgjelinek * vmu_calculate(). 2043247Sgjelinek * 2053247Sgjelinek * vme_next_calc: links the list of entities related to the current process 2063247Sgjelinek * being counted by vmu_calculate_proc(). 2073247Sgjelinek * 2083247Sgjelinek * vmu_calculate_proc() walks all processes. For each process, it makes a 2093247Sgjelinek * list of the entities related to that process using vme_next_calc. This 2103247Sgjelinek * list changes each time vmu_calculate_proc() is called. 2113247Sgjelinek * 2123247Sgjelinek */ 2133247Sgjelinek typedef struct vmu_entity { 2143247Sgjelinek struct vmu_entity *vme_next; 2153247Sgjelinek struct vmu_entity *vme_next_calc; 2163247Sgjelinek mod_hash_t *vme_vnode_hash; /* vnodes visited for entity */ 2173247Sgjelinek mod_hash_t *vme_amp_hash; /* shared amps visited for entity */ 2183247Sgjelinek mod_hash_t *vme_anon_hash; /* cow anons visited for entity */ 2193247Sgjelinek vmusage_t vme_result; /* identifies entity and results */ 2203247Sgjelinek } vmu_entity_t; 2213247Sgjelinek 2223247Sgjelinek /* 2233247Sgjelinek * Hash of entities visited within a zone, and an entity for the zone 2243247Sgjelinek * itself. 2253247Sgjelinek */ 2263247Sgjelinek typedef struct vmu_zone { 2273247Sgjelinek struct vmu_zone *vmz_next; /* free list */ 2283247Sgjelinek id_t vmz_id; 2293247Sgjelinek vmu_entity_t *vmz_zone; 2303247Sgjelinek mod_hash_t *vmz_projects_hash; 2313247Sgjelinek mod_hash_t *vmz_tasks_hash; 2323247Sgjelinek mod_hash_t *vmz_rusers_hash; 2333247Sgjelinek mod_hash_t *vmz_eusers_hash; 2343247Sgjelinek } vmu_zone_t; 2353247Sgjelinek 2363247Sgjelinek /* 2373247Sgjelinek * Cache of results from last calculation 2383247Sgjelinek */ 2393247Sgjelinek typedef struct vmu_cache { 2403247Sgjelinek vmusage_t *vmc_results; /* Results from last call to */ 2413247Sgjelinek /* vm_getusage(). */ 2423247Sgjelinek uint64_t vmc_nresults; /* Count of cached results */ 2433247Sgjelinek uint64_t vmc_refcnt; /* refcnt for free */ 2443247Sgjelinek uint_t vmc_flags; /* Flags for vm_getusage() */ 2453247Sgjelinek hrtime_t vmc_timestamp; /* when cache was created */ 2463247Sgjelinek } vmu_cache_t; 2473247Sgjelinek 2483247Sgjelinek /* 2493247Sgjelinek * top level rss info for the system 2503247Sgjelinek */ 2513247Sgjelinek typedef struct vmu_data { 2523247Sgjelinek kmutex_t vmu_lock; /* Protects vmu_data */ 2533247Sgjelinek kcondvar_t vmu_cv; /* Used to signal threads */ 2543247Sgjelinek /* Waiting for */ 2553247Sgjelinek /* Rss_calc_thread to finish */ 2563247Sgjelinek vmu_entity_t *vmu_system; /* Entity for tracking */ 2573247Sgjelinek /* rss/swap for all processes */ 2583247Sgjelinek /* in all zones */ 2593247Sgjelinek mod_hash_t *vmu_zones_hash; /* Zones visited */ 2603247Sgjelinek mod_hash_t *vmu_projects_col_hash; /* These *_col_hash hashes */ 2613247Sgjelinek mod_hash_t *vmu_rusers_col_hash; /* keep track of entities, */ 2623247Sgjelinek mod_hash_t *vmu_eusers_col_hash; /* ignoring zoneid, in order */ 2633247Sgjelinek /* to implement VMUSAGE_COL_* */ 2643247Sgjelinek /* flags, which aggregate by */ 2653247Sgjelinek /* project or user regardless */ 2663247Sgjelinek /* of zoneid. */ 2673247Sgjelinek mod_hash_t *vmu_all_vnodes_hash; /* System wide visited vnodes */ 2683247Sgjelinek /* to track incore/not-incore */ 2693247Sgjelinek mod_hash_t *vmu_all_amps_hash; /* System wide visited shared */ 2703247Sgjelinek /* amps to track incore/not- */ 2713247Sgjelinek /* incore */ 2723247Sgjelinek vmu_entity_t *vmu_entities; /* Linked list of entities */ 2733247Sgjelinek size_t vmu_nentities; /* Count of entities in list */ 2743247Sgjelinek vmu_cache_t *vmu_cache; /* Cached results */ 2753247Sgjelinek kthread_t *vmu_calc_thread; /* NULL, or thread running */ 2763247Sgjelinek /* vmu_calculate() */ 2773247Sgjelinek uint_t vmu_calc_flags; /* Flags being using by */ 2783247Sgjelinek /* currently running calc */ 2793247Sgjelinek /* thread */ 2803247Sgjelinek uint_t vmu_pending_flags; /* Flags of vm_getusage() */ 2813247Sgjelinek /* threads waiting for */ 2823247Sgjelinek /* calc thread to finish */ 2833247Sgjelinek uint_t vmu_pending_waiters; /* Number of threads waiting */ 2843247Sgjelinek /* for calc thread */ 2853247Sgjelinek vmu_bound_t *vmu_free_bounds; 2863247Sgjelinek vmu_object_t *vmu_free_objects; 2873247Sgjelinek vmu_entity_t *vmu_free_entities; 2883247Sgjelinek vmu_zone_t *vmu_free_zones; 2893247Sgjelinek } vmu_data_t; 2903247Sgjelinek 2913247Sgjelinek extern struct as kas; 2923247Sgjelinek extern proc_t *practive; 2933247Sgjelinek extern zone_t *global_zone; 2943247Sgjelinek extern struct seg_ops segvn_ops; 2953247Sgjelinek extern struct seg_ops segspt_shmops; 2963247Sgjelinek 2973247Sgjelinek static vmu_data_t vmu_data; 2983247Sgjelinek static kmem_cache_t *vmu_bound_cache; 2993247Sgjelinek static kmem_cache_t *vmu_object_cache; 3003247Sgjelinek 3013247Sgjelinek /* 3023247Sgjelinek * Save a bound on the free list 3033247Sgjelinek */ 3043247Sgjelinek static void 3053247Sgjelinek vmu_free_bound(vmu_bound_t *bound) 3063247Sgjelinek { 3073247Sgjelinek bound->vmb_next = vmu_data.vmu_free_bounds; 3083247Sgjelinek vmu_data.vmu_free_bounds = bound; 3093247Sgjelinek } 3103247Sgjelinek 3113247Sgjelinek /* 3123247Sgjelinek * Free an object, and all visited bound info. 3133247Sgjelinek */ 3143247Sgjelinek static void 3153247Sgjelinek vmu_free_object(mod_hash_val_t val) 3163247Sgjelinek { 3173247Sgjelinek vmu_object_t *obj = (vmu_object_t *)val; 3183247Sgjelinek vmu_bound_t *bound = obj->vmo_bounds; 3193247Sgjelinek vmu_bound_t *tmp; 3203247Sgjelinek 3213247Sgjelinek while (bound != NULL) { 3223247Sgjelinek tmp = bound; 3233247Sgjelinek bound = bound->vmb_next; 3243247Sgjelinek vmu_free_bound(tmp); 3253247Sgjelinek } 3263247Sgjelinek obj->vmo_next = vmu_data.vmu_free_objects; 3273247Sgjelinek vmu_data.vmu_free_objects = obj; 3283247Sgjelinek } 3293247Sgjelinek 3303247Sgjelinek /* 3313247Sgjelinek * Free an entity, and hashes of visited objects for that entity. 3323247Sgjelinek */ 3333247Sgjelinek static void 3343247Sgjelinek vmu_free_entity(mod_hash_val_t val) 3353247Sgjelinek { 3363247Sgjelinek vmu_entity_t *entity = (vmu_entity_t *)val; 3373247Sgjelinek 3383247Sgjelinek if (entity->vme_vnode_hash != NULL) 3393247Sgjelinek i_mod_hash_clear_nosync(entity->vme_vnode_hash); 3403247Sgjelinek if (entity->vme_amp_hash != NULL) 3413247Sgjelinek i_mod_hash_clear_nosync(entity->vme_amp_hash); 3423247Sgjelinek if (entity->vme_anon_hash != NULL) 3433247Sgjelinek i_mod_hash_clear_nosync(entity->vme_anon_hash); 3443247Sgjelinek 3453247Sgjelinek entity->vme_next = vmu_data.vmu_free_entities; 3463247Sgjelinek vmu_data.vmu_free_entities = entity; 3473247Sgjelinek } 3483247Sgjelinek 3493247Sgjelinek /* 3503247Sgjelinek * Free zone entity, and all hashes of entities inside that zone, 3513247Sgjelinek * which are projects, tasks, and users. 3523247Sgjelinek */ 3533247Sgjelinek static void 3543247Sgjelinek vmu_free_zone(mod_hash_val_t val) 3553247Sgjelinek { 3563247Sgjelinek vmu_zone_t *zone = (vmu_zone_t *)val; 3573247Sgjelinek 3583247Sgjelinek if (zone->vmz_zone != NULL) { 3593247Sgjelinek vmu_free_entity((mod_hash_val_t)zone->vmz_zone); 3603247Sgjelinek zone->vmz_zone = NULL; 3613247Sgjelinek } 3623247Sgjelinek if (zone->vmz_projects_hash != NULL) 3633247Sgjelinek i_mod_hash_clear_nosync(zone->vmz_projects_hash); 3643247Sgjelinek if (zone->vmz_tasks_hash != NULL) 3653247Sgjelinek i_mod_hash_clear_nosync(zone->vmz_tasks_hash); 3663247Sgjelinek if (zone->vmz_rusers_hash != NULL) 3673247Sgjelinek i_mod_hash_clear_nosync(zone->vmz_rusers_hash); 3683247Sgjelinek if (zone->vmz_eusers_hash != NULL) 3693247Sgjelinek i_mod_hash_clear_nosync(zone->vmz_eusers_hash); 3703247Sgjelinek zone->vmz_next = vmu_data.vmu_free_zones; 3713247Sgjelinek vmu_data.vmu_free_zones = zone; 3723247Sgjelinek } 3733247Sgjelinek 3743247Sgjelinek /* 3753247Sgjelinek * Initialize synchronization primitives and hashes for system-wide tracking 3763247Sgjelinek * of visited vnodes and shared amps. Initialize results cache. 3773247Sgjelinek */ 3783247Sgjelinek void 3793247Sgjelinek vm_usage_init() 3803247Sgjelinek { 3813247Sgjelinek mutex_init(&vmu_data.vmu_lock, NULL, MUTEX_DEFAULT, NULL); 3823247Sgjelinek cv_init(&vmu_data.vmu_cv, NULL, CV_DEFAULT, NULL); 3833247Sgjelinek 3843247Sgjelinek vmu_data.vmu_system = NULL; 3853247Sgjelinek vmu_data.vmu_zones_hash = NULL; 3863247Sgjelinek vmu_data.vmu_projects_col_hash = NULL; 3873247Sgjelinek vmu_data.vmu_rusers_col_hash = NULL; 3883247Sgjelinek vmu_data.vmu_eusers_col_hash = NULL; 3893247Sgjelinek 3903247Sgjelinek vmu_data.vmu_free_bounds = NULL; 3913247Sgjelinek vmu_data.vmu_free_objects = NULL; 3923247Sgjelinek vmu_data.vmu_free_entities = NULL; 3933247Sgjelinek vmu_data.vmu_free_zones = NULL; 3943247Sgjelinek 3953247Sgjelinek vmu_data.vmu_all_vnodes_hash = mod_hash_create_ptrhash( 3963247Sgjelinek "vmusage vnode hash", VMUSAGE_HASH_SIZE, vmu_free_object, 3973247Sgjelinek sizeof (vnode_t)); 3983247Sgjelinek vmu_data.vmu_all_amps_hash = mod_hash_create_ptrhash( 3993247Sgjelinek "vmusage amp hash", VMUSAGE_HASH_SIZE, vmu_free_object, 4003247Sgjelinek sizeof (struct anon_map)); 4013247Sgjelinek vmu_data.vmu_projects_col_hash = mod_hash_create_idhash( 4023247Sgjelinek "vmusage collapsed project hash", VMUSAGE_HASH_SIZE, 4033247Sgjelinek vmu_free_entity); 4043247Sgjelinek vmu_data.vmu_rusers_col_hash = mod_hash_create_idhash( 4053247Sgjelinek "vmusage collapsed ruser hash", VMUSAGE_HASH_SIZE, 4063247Sgjelinek vmu_free_entity); 4073247Sgjelinek vmu_data.vmu_eusers_col_hash = mod_hash_create_idhash( 4083247Sgjelinek "vmusage collpased euser hash", VMUSAGE_HASH_SIZE, 4093247Sgjelinek vmu_free_entity); 4103247Sgjelinek vmu_data.vmu_zones_hash = mod_hash_create_idhash( 4113247Sgjelinek "vmusage zone hash", VMUSAGE_HASH_SIZE, vmu_free_zone); 4123247Sgjelinek 4133247Sgjelinek vmu_bound_cache = kmem_cache_create("vmu_bound_cache", 4143247Sgjelinek sizeof (vmu_bound_t), 0, NULL, NULL, NULL, NULL, NULL, 0); 4153247Sgjelinek vmu_object_cache = kmem_cache_create("vmu_object_cache", 4163247Sgjelinek sizeof (vmu_object_t), 0, NULL, NULL, NULL, NULL, NULL, 0); 4173247Sgjelinek 4183247Sgjelinek vmu_data.vmu_entities = NULL; 4193247Sgjelinek vmu_data.vmu_nentities = 0; 4203247Sgjelinek 4213247Sgjelinek vmu_data.vmu_cache = NULL; 4223247Sgjelinek vmu_data.vmu_calc_thread = NULL; 4233247Sgjelinek vmu_data.vmu_calc_flags = 0; 4243247Sgjelinek vmu_data.vmu_pending_flags = 0; 4253247Sgjelinek vmu_data.vmu_pending_waiters = 0; 4263247Sgjelinek } 4273247Sgjelinek 4283247Sgjelinek /* 4293247Sgjelinek * Allocate hashes for tracking vm objects visited for an entity. 4303247Sgjelinek * Update list of entities. 4313247Sgjelinek */ 4323247Sgjelinek static vmu_entity_t * 4333247Sgjelinek vmu_alloc_entity(id_t id, int type, id_t zoneid) 4343247Sgjelinek { 4353247Sgjelinek vmu_entity_t *entity; 4363247Sgjelinek 4373247Sgjelinek if (vmu_data.vmu_free_entities != NULL) { 4383247Sgjelinek entity = vmu_data.vmu_free_entities; 4393247Sgjelinek vmu_data.vmu_free_entities = 4403247Sgjelinek vmu_data.vmu_free_entities->vme_next; 4413247Sgjelinek bzero(&entity->vme_result, sizeof (vmusage_t)); 4423247Sgjelinek } else { 4433247Sgjelinek entity = kmem_zalloc(sizeof (vmu_entity_t), KM_SLEEP); 4443247Sgjelinek } 4453247Sgjelinek entity->vme_result.vmu_id = id; 4463247Sgjelinek entity->vme_result.vmu_zoneid = zoneid; 4473247Sgjelinek entity->vme_result.vmu_type = type; 4483247Sgjelinek 4493247Sgjelinek if (entity->vme_vnode_hash == NULL) 4503247Sgjelinek entity->vme_vnode_hash = mod_hash_create_ptrhash( 4513247Sgjelinek "vmusage vnode hash", VMUSAGE_HASH_SIZE, vmu_free_object, 4523247Sgjelinek sizeof (vnode_t)); 4533247Sgjelinek 4543247Sgjelinek if (entity->vme_amp_hash == NULL) 4553247Sgjelinek entity->vme_amp_hash = mod_hash_create_ptrhash( 4563247Sgjelinek "vmusage amp hash", VMUSAGE_HASH_SIZE, vmu_free_object, 4573247Sgjelinek sizeof (struct anon_map)); 4583247Sgjelinek 4593247Sgjelinek if (entity->vme_anon_hash == NULL) 4603247Sgjelinek entity->vme_anon_hash = mod_hash_create_ptrhash( 4613247Sgjelinek "vmusage anon hash", VMUSAGE_HASH_SIZE, 4623247Sgjelinek mod_hash_null_valdtor, sizeof (struct anon)); 4633247Sgjelinek 4643247Sgjelinek entity->vme_next = vmu_data.vmu_entities; 4653247Sgjelinek vmu_data.vmu_entities = entity; 4663247Sgjelinek vmu_data.vmu_nentities++; 4673247Sgjelinek 4683247Sgjelinek return (entity); 4693247Sgjelinek } 4703247Sgjelinek 4713247Sgjelinek /* 4723247Sgjelinek * Allocate a zone entity, and hashes for tracking visited vm objects 4733247Sgjelinek * for projects, tasks, and users within that zone. 4743247Sgjelinek */ 4753247Sgjelinek static vmu_zone_t * 4763247Sgjelinek vmu_alloc_zone(id_t id) 4773247Sgjelinek { 4783247Sgjelinek vmu_zone_t *zone; 4793247Sgjelinek 4803247Sgjelinek if (vmu_data.vmu_free_zones != NULL) { 4813247Sgjelinek zone = vmu_data.vmu_free_zones; 4823247Sgjelinek vmu_data.vmu_free_zones = 4833247Sgjelinek vmu_data.vmu_free_zones->vmz_next; 4843247Sgjelinek zone->vmz_next = NULL; 4853247Sgjelinek zone->vmz_zone = NULL; 4863247Sgjelinek } else { 4873247Sgjelinek zone = kmem_zalloc(sizeof (vmu_zone_t), KM_SLEEP); 4883247Sgjelinek } 4893247Sgjelinek 4903247Sgjelinek zone->vmz_id = id; 4913247Sgjelinek 4923247Sgjelinek if ((vmu_data.vmu_calc_flags & (VMUSAGE_ZONE | VMUSAGE_ALL_ZONES)) != 0) 4933247Sgjelinek zone->vmz_zone = vmu_alloc_entity(id, VMUSAGE_ZONE, id); 4943247Sgjelinek 4953247Sgjelinek if ((vmu_data.vmu_calc_flags & (VMUSAGE_PROJECTS | 4963247Sgjelinek VMUSAGE_ALL_PROJECTS)) != 0 && zone->vmz_projects_hash == NULL) 4973247Sgjelinek zone->vmz_projects_hash = mod_hash_create_idhash( 4983247Sgjelinek "vmusage project hash", VMUSAGE_HASH_SIZE, vmu_free_entity); 4993247Sgjelinek 5003247Sgjelinek if ((vmu_data.vmu_calc_flags & (VMUSAGE_TASKS | VMUSAGE_ALL_TASKS)) 5013247Sgjelinek != 0 && zone->vmz_tasks_hash == NULL) 5023247Sgjelinek zone->vmz_tasks_hash = mod_hash_create_idhash( 5033247Sgjelinek "vmusage task hash", VMUSAGE_HASH_SIZE, vmu_free_entity); 5043247Sgjelinek 5053247Sgjelinek if ((vmu_data.vmu_calc_flags & (VMUSAGE_RUSERS | VMUSAGE_ALL_RUSERS)) 5063247Sgjelinek != 0 && zone->vmz_rusers_hash == NULL) 5073247Sgjelinek zone->vmz_rusers_hash = mod_hash_create_idhash( 5083247Sgjelinek "vmusage ruser hash", VMUSAGE_HASH_SIZE, vmu_free_entity); 5093247Sgjelinek 5103247Sgjelinek if ((vmu_data.vmu_calc_flags & (VMUSAGE_EUSERS | VMUSAGE_ALL_EUSERS)) 5113247Sgjelinek != 0 && zone->vmz_eusers_hash == NULL) 5123247Sgjelinek zone->vmz_eusers_hash = mod_hash_create_idhash( 5133247Sgjelinek "vmusage euser hash", VMUSAGE_HASH_SIZE, vmu_free_entity); 5143247Sgjelinek 5153247Sgjelinek return (zone); 5163247Sgjelinek } 5173247Sgjelinek 5183247Sgjelinek /* 5193247Sgjelinek * Allocate a structure for tracking visited bounds for a vm object. 5203247Sgjelinek */ 5213247Sgjelinek static vmu_object_t * 5223247Sgjelinek vmu_alloc_object(caddr_t key, int type) 5233247Sgjelinek { 5243247Sgjelinek vmu_object_t *object; 5253247Sgjelinek 5263247Sgjelinek if (vmu_data.vmu_free_objects != NULL) { 5273247Sgjelinek object = vmu_data.vmu_free_objects; 5283247Sgjelinek vmu_data.vmu_free_objects = 5293247Sgjelinek vmu_data.vmu_free_objects->vmo_next; 5303247Sgjelinek } else { 5313247Sgjelinek object = kmem_cache_alloc(vmu_object_cache, KM_SLEEP); 5323247Sgjelinek } 5333247Sgjelinek 5343247Sgjelinek object->vmo_key = key; 5353247Sgjelinek object->vmo_type = type; 5363247Sgjelinek object->vmo_bounds = NULL; 5373247Sgjelinek 5383247Sgjelinek return (object); 5393247Sgjelinek } 5403247Sgjelinek 5413247Sgjelinek /* 5423247Sgjelinek * Allocate and return a bound structure. 5433247Sgjelinek */ 5443247Sgjelinek static vmu_bound_t * 5453247Sgjelinek vmu_alloc_bound() 5463247Sgjelinek { 5473247Sgjelinek vmu_bound_t *bound; 5483247Sgjelinek 5493247Sgjelinek if (vmu_data.vmu_free_bounds != NULL) { 5503247Sgjelinek bound = vmu_data.vmu_free_bounds; 5513247Sgjelinek vmu_data.vmu_free_bounds = 5523247Sgjelinek vmu_data.vmu_free_bounds->vmb_next; 5533247Sgjelinek bzero(bound, sizeof (vmu_bound_t)); 5543247Sgjelinek } else { 5553247Sgjelinek bound = kmem_cache_alloc(vmu_bound_cache, KM_SLEEP); 5563247Sgjelinek bzero(bound, sizeof (vmu_bound_t)); 5573247Sgjelinek } 5583247Sgjelinek return (bound); 5593247Sgjelinek } 5603247Sgjelinek 5613247Sgjelinek /* 5623247Sgjelinek * vmu_find_insert_* functions implement hash lookup or allocate and 5633247Sgjelinek * insert operations. 5643247Sgjelinek */ 5653247Sgjelinek static vmu_object_t * 5663247Sgjelinek vmu_find_insert_object(mod_hash_t *hash, caddr_t key, uint_t type) 5673247Sgjelinek { 5683247Sgjelinek int ret; 5693247Sgjelinek vmu_object_t *object; 5703247Sgjelinek 5713247Sgjelinek ret = i_mod_hash_find_nosync(hash, (mod_hash_key_t)key, 5723247Sgjelinek (mod_hash_val_t *)&object); 5733247Sgjelinek if (ret != 0) { 5743247Sgjelinek object = vmu_alloc_object(key, type); 5753247Sgjelinek ret = i_mod_hash_insert_nosync(hash, (mod_hash_key_t)key, 5763247Sgjelinek (mod_hash_val_t)object, (mod_hash_hndl_t)0); 5773247Sgjelinek ASSERT(ret == 0); 5783247Sgjelinek } 5793247Sgjelinek return (object); 5803247Sgjelinek } 5813247Sgjelinek 5823247Sgjelinek static int 5833247Sgjelinek vmu_find_insert_anon(mod_hash_t *hash, caddr_t key) 5843247Sgjelinek { 5853247Sgjelinek int ret; 5863247Sgjelinek caddr_t val; 5873247Sgjelinek 5883247Sgjelinek ret = i_mod_hash_find_nosync(hash, (mod_hash_key_t)key, 5893247Sgjelinek (mod_hash_val_t *)&val); 5903247Sgjelinek 5913247Sgjelinek if (ret == 0) 5923247Sgjelinek return (0); 5933247Sgjelinek 5943247Sgjelinek ret = i_mod_hash_insert_nosync(hash, (mod_hash_key_t)key, 5953247Sgjelinek (mod_hash_val_t)key, (mod_hash_hndl_t)0); 5963247Sgjelinek 5973247Sgjelinek ASSERT(ret == 0); 5983247Sgjelinek 5993247Sgjelinek return (1); 6003247Sgjelinek } 6013247Sgjelinek 6023247Sgjelinek static vmu_entity_t * 6033247Sgjelinek vmu_find_insert_entity(mod_hash_t *hash, id_t id, uint_t type, id_t zoneid) 6043247Sgjelinek { 6053247Sgjelinek int ret; 6063247Sgjelinek vmu_entity_t *entity; 6073247Sgjelinek 6083247Sgjelinek ret = i_mod_hash_find_nosync(hash, (mod_hash_key_t)(uintptr_t)id, 6093247Sgjelinek (mod_hash_val_t *)&entity); 6103247Sgjelinek if (ret != 0) { 6113247Sgjelinek entity = vmu_alloc_entity(id, type, zoneid); 6123247Sgjelinek ret = i_mod_hash_insert_nosync(hash, 6133247Sgjelinek (mod_hash_key_t)(uintptr_t)id, (mod_hash_val_t)entity, 6143247Sgjelinek (mod_hash_hndl_t)0); 6153247Sgjelinek ASSERT(ret == 0); 6163247Sgjelinek } 6173247Sgjelinek return (entity); 6183247Sgjelinek } 6193247Sgjelinek 6203247Sgjelinek 6213247Sgjelinek 6223247Sgjelinek 6233247Sgjelinek /* 6243247Sgjelinek * Returns list of object bounds between start and end. New bounds inserted 6253247Sgjelinek * by this call are given type. 6263247Sgjelinek * 6273247Sgjelinek * Returns the number of pages covered if new bounds are created. Returns 0 6283247Sgjelinek * if region between start/end consists of all existing bounds. 6293247Sgjelinek */ 6303247Sgjelinek static pgcnt_t 6313247Sgjelinek vmu_insert_lookup_object_bounds(vmu_object_t *ro, pgcnt_t start, pgcnt_t 6323247Sgjelinek end, char type, vmu_bound_t **first, vmu_bound_t **last) 6333247Sgjelinek { 6343247Sgjelinek vmu_bound_t *next; 6353247Sgjelinek vmu_bound_t *prev = NULL; 6363247Sgjelinek vmu_bound_t *tmp = NULL; 6373247Sgjelinek pgcnt_t ret = 0; 6383247Sgjelinek 6393247Sgjelinek *first = *last = NULL; 6403247Sgjelinek 6413247Sgjelinek for (next = ro->vmo_bounds; next != NULL; next = next->vmb_next) { 6423247Sgjelinek /* 6433247Sgjelinek * Find bounds overlapping or overlapped by range [start,end]. 6443247Sgjelinek */ 6453247Sgjelinek if (start > next->vmb_end) { 6463247Sgjelinek /* bound is before new bound */ 6473247Sgjelinek prev = next; 6483247Sgjelinek continue; 6493247Sgjelinek } 6503247Sgjelinek if (next->vmb_start > end) { 6513247Sgjelinek /* bound is after new bound */ 6523247Sgjelinek break; 6533247Sgjelinek } 6543247Sgjelinek if (*first == NULL) 6553247Sgjelinek *first = next; 6563247Sgjelinek *last = next; 6573247Sgjelinek } 6583247Sgjelinek 6593247Sgjelinek if (*first == NULL) { 6603247Sgjelinek ASSERT(*last == NULL); 6613247Sgjelinek /* 6623247Sgjelinek * No bounds overlapping range [start,end], so create new 6633247Sgjelinek * bound 6643247Sgjelinek */ 6653247Sgjelinek tmp = vmu_alloc_bound(); 6663247Sgjelinek tmp->vmb_start = start; 6673247Sgjelinek tmp->vmb_end = end; 6683247Sgjelinek tmp->vmb_type = type; 6693247Sgjelinek if (prev == NULL) { 6703247Sgjelinek tmp->vmb_next = ro->vmo_bounds; 6713247Sgjelinek ro->vmo_bounds = tmp; 6723247Sgjelinek } else { 6733247Sgjelinek tmp->vmb_next = prev->vmb_next; 6743247Sgjelinek prev->vmb_next = tmp; 6753247Sgjelinek } 6763247Sgjelinek *first = tmp; 6773247Sgjelinek *last = tmp; 6783247Sgjelinek ASSERT(tmp->vmb_end >= tmp->vmb_start); 6793247Sgjelinek ret = tmp->vmb_end - tmp->vmb_start + 1; 6803247Sgjelinek return (ret); 6813247Sgjelinek } 6823247Sgjelinek 6833247Sgjelinek /* Check to see if start is before first known bound */ 6843247Sgjelinek ASSERT(first != NULL && last != NULL); 6853247Sgjelinek next = (*first); 6863247Sgjelinek if (start < (*first)->vmb_start) { 6873247Sgjelinek /* Create new bound before first bound */ 6883247Sgjelinek tmp = vmu_alloc_bound(); 6893247Sgjelinek tmp->vmb_start = start; 6903247Sgjelinek tmp->vmb_end = (*first)->vmb_start - 1; 6913247Sgjelinek tmp->vmb_type = type; 6923247Sgjelinek tmp->vmb_next = *first; 6933247Sgjelinek if (*first == ro->vmo_bounds) 6943247Sgjelinek ro->vmo_bounds = tmp; 6953247Sgjelinek if (prev != NULL) 6963247Sgjelinek prev->vmb_next = tmp; 6973247Sgjelinek ASSERT(tmp->vmb_end >= tmp->vmb_start); 6983247Sgjelinek ret += tmp->vmb_end - tmp->vmb_start + 1; 6993247Sgjelinek *first = tmp; 7003247Sgjelinek } 7013247Sgjelinek /* 7023247Sgjelinek * Between start and end, search for gaps between and after existing 7033247Sgjelinek * bounds. Create new bounds to fill gaps if they exist. 7043247Sgjelinek */ 7053247Sgjelinek while (end > next->vmb_end) { 7063247Sgjelinek /* 7073247Sgjelinek * Check for gap between bound and next bound. if no gap, 7083247Sgjelinek * continue. 7093247Sgjelinek */ 7103247Sgjelinek if ((next != *last) && 7113247Sgjelinek ((next->vmb_end + 1) == next->vmb_next->vmb_start)) { 7123247Sgjelinek next = next->vmb_next; 7133247Sgjelinek continue; 7143247Sgjelinek } 7153247Sgjelinek /* 7163247Sgjelinek * Insert new bound in gap after bound, and before next 7173247Sgjelinek * bound if next bound exists. 7183247Sgjelinek */ 7193247Sgjelinek tmp = vmu_alloc_bound(); 7203247Sgjelinek tmp->vmb_type = type; 7213247Sgjelinek tmp->vmb_next = next->vmb_next; 7223247Sgjelinek tmp->vmb_start = next->vmb_end + 1; 7233247Sgjelinek 7243247Sgjelinek if (next != *last) { 7253247Sgjelinek tmp->vmb_end = next->vmb_next->vmb_start - 1; 7263247Sgjelinek ASSERT(tmp->vmb_end >= tmp->vmb_start); 7273247Sgjelinek ret += tmp->vmb_end - tmp->vmb_start + 1; 7283247Sgjelinek next->vmb_next = tmp; 7293247Sgjelinek next = tmp->vmb_next; 7303247Sgjelinek } else { 7313247Sgjelinek tmp->vmb_end = end; 7323247Sgjelinek ASSERT(tmp->vmb_end >= tmp->vmb_start); 7333247Sgjelinek ret += tmp->vmb_end - tmp->vmb_start + 1; 7343247Sgjelinek next->vmb_next = tmp; 7353247Sgjelinek *last = tmp; 7363247Sgjelinek break; 7373247Sgjelinek } 7383247Sgjelinek } 7393247Sgjelinek return (ret); 7403247Sgjelinek } 7413247Sgjelinek 7423247Sgjelinek /* 7433247Sgjelinek * vmu_update_bounds() 7443247Sgjelinek * 7453247Sgjelinek * first, last: list of continuous bounds, of which zero or more are of 7463247Sgjelinek * type VMUSAGE_BOUND_UNKNOWN. 7473247Sgjelinek * 7483247Sgjelinek * new_first, new_last: list of continuous bounds, of which none are of 7493247Sgjelinek * type VMUSAGE_BOUND_UNKNOWN. These bounds are used to 7503247Sgjelinek * update the types of bounds in (first,last) with 7513247Sgjelinek * type VMUSAGE_BOUND_UNKNOWN. 7523247Sgjelinek * 7533247Sgjelinek * For the list of bounds (first,last), this function updates any bounds 7543247Sgjelinek * with type VMUSAGE_BOUND_UNKNOWN using the type of the corresponding bound in 7553247Sgjelinek * the list (new_first, new_last). 7563247Sgjelinek * 7573247Sgjelinek * If a bound of type VMUSAGE_BOUND_UNKNOWN spans multiple bounds in the list 7583247Sgjelinek * (new_first, new_last), it will be split into multiple bounds. 7593247Sgjelinek * 7603247Sgjelinek * Return value: 7613247Sgjelinek * The number of pages in the list of bounds (first,last) that were of 7623247Sgjelinek * type VMUSAGE_BOUND_UNKNOWN, which have been updated to be of type 7633247Sgjelinek * VMUSAGE_BOUND_INCORE. 7643247Sgjelinek * 7653247Sgjelinek */ 7663247Sgjelinek static pgcnt_t 7673247Sgjelinek vmu_update_bounds(vmu_bound_t **first, vmu_bound_t **last, 7683247Sgjelinek vmu_bound_t *new_first, vmu_bound_t *new_last) 7693247Sgjelinek { 7703247Sgjelinek vmu_bound_t *next, *new_next, *tmp; 7713247Sgjelinek pgcnt_t rss = 0; 7723247Sgjelinek 7733247Sgjelinek next = *first; 7743247Sgjelinek new_next = new_first; 7753247Sgjelinek 7763671Ssl108498 /* 7773671Ssl108498 * Verify first and last bound are covered by new bounds if they 7783671Ssl108498 * have unknown type. 7793671Ssl108498 */ 7803671Ssl108498 ASSERT((*first)->vmb_type != VMUSAGE_BOUND_UNKNOWN || 7813671Ssl108498 (*first)->vmb_start >= new_next->vmb_start); 7823671Ssl108498 ASSERT((*last)->vmb_type != VMUSAGE_BOUND_UNKNOWN || 7833671Ssl108498 (*last)->vmb_end <= new_last->vmb_end); 7843247Sgjelinek for (;;) { 7853247Sgjelinek /* If bound already has type, proceed to next bound */ 7863247Sgjelinek if (next->vmb_type != VMUSAGE_BOUND_UNKNOWN) { 7873247Sgjelinek if (next == *last) 7883247Sgjelinek break; 7893247Sgjelinek next = next->vmb_next; 7903247Sgjelinek continue; 7913247Sgjelinek } 7923247Sgjelinek while (new_next->vmb_end < next->vmb_start) 7933247Sgjelinek new_next = new_next->vmb_next; 7943247Sgjelinek ASSERT(new_next->vmb_type != VMUSAGE_BOUND_UNKNOWN); 7953247Sgjelinek next->vmb_type = new_next->vmb_type; 7963247Sgjelinek if (new_next->vmb_end < next->vmb_end) { 7973247Sgjelinek /* need to split bound */ 7983247Sgjelinek tmp = vmu_alloc_bound(); 7993247Sgjelinek tmp->vmb_type = VMUSAGE_BOUND_UNKNOWN; 8003247Sgjelinek tmp->vmb_start = new_next->vmb_end + 1; 8013247Sgjelinek tmp->vmb_end = next->vmb_end; 8023247Sgjelinek tmp->vmb_next = next->vmb_next; 8033247Sgjelinek next->vmb_end = new_next->vmb_end; 8043247Sgjelinek next->vmb_next = tmp; 8053247Sgjelinek if (*last == next) 8063247Sgjelinek *last = tmp; 8073247Sgjelinek if (next->vmb_type == VMUSAGE_BOUND_INCORE) 8083247Sgjelinek rss += next->vmb_end - next->vmb_start + 1; 8093247Sgjelinek next = tmp; 8103247Sgjelinek } else { 8113247Sgjelinek if (next->vmb_type == VMUSAGE_BOUND_INCORE) 8123247Sgjelinek rss += next->vmb_end - next->vmb_start + 1; 8133247Sgjelinek if (next == *last) 8143247Sgjelinek break; 8153247Sgjelinek next = next->vmb_next; 8163247Sgjelinek } 8173247Sgjelinek } 8183247Sgjelinek return (rss); 8193247Sgjelinek } 8203247Sgjelinek 8213247Sgjelinek /* 8223247Sgjelinek * merges adjacent bounds with same type between first and last bound. 8233247Sgjelinek * After merge, last pointer is no longer valid, as last bound may be 8243247Sgjelinek * merged away. 8253247Sgjelinek */ 8263247Sgjelinek static void 8273247Sgjelinek vmu_merge_bounds(vmu_bound_t **first, vmu_bound_t **last) 8283247Sgjelinek { 8293247Sgjelinek vmu_bound_t *next; 8303247Sgjelinek vmu_bound_t *tmp; 8313247Sgjelinek 8323247Sgjelinek ASSERT(*first != NULL); 8333247Sgjelinek ASSERT(*last != NULL); 8343247Sgjelinek 8353247Sgjelinek next = *first; 8363247Sgjelinek while (next != *last) { 8373247Sgjelinek 8383247Sgjelinek /* If bounds are adjacent and have same type, merge them */ 8393247Sgjelinek if (((next->vmb_end + 1) == next->vmb_next->vmb_start) && 8403247Sgjelinek (next->vmb_type == next->vmb_next->vmb_type)) { 8413247Sgjelinek tmp = next->vmb_next; 8423247Sgjelinek next->vmb_end = tmp->vmb_end; 8433247Sgjelinek next->vmb_next = tmp->vmb_next; 8443247Sgjelinek vmu_free_bound(tmp); 8453247Sgjelinek if (tmp == *last) 8463247Sgjelinek *last = next; 8473247Sgjelinek } else { 8483247Sgjelinek next = next->vmb_next; 8493247Sgjelinek } 8503247Sgjelinek } 8513247Sgjelinek } 8523247Sgjelinek 8533247Sgjelinek /* 8543247Sgjelinek * Given an amp and a list of bounds, updates each bound's type with 8553247Sgjelinek * VMUSAGE_BOUND_INCORE or VMUSAGE_BOUND_NOT_INCORE. 8563247Sgjelinek * 8573247Sgjelinek * If a bound is partially incore, it will be split into two bounds. 8583247Sgjelinek * first and last may be modified, as bounds may be split into multiple 8593247Sgjelinek * bounds if the are partially incore/not-incore. 8603247Sgjelinek * 8613247Sgjelinek * Set incore to non-zero if bounds are already known to be incore 8623247Sgjelinek * 8633247Sgjelinek */ 8643247Sgjelinek static void 8653247Sgjelinek vmu_amp_update_incore_bounds(struct anon_map *amp, vmu_bound_t **first, 8663247Sgjelinek vmu_bound_t **last, boolean_t incore) 8673247Sgjelinek { 8683247Sgjelinek vmu_bound_t *next; 8693247Sgjelinek vmu_bound_t *tmp; 8703247Sgjelinek pgcnt_t index; 8713247Sgjelinek short bound_type; 8723247Sgjelinek short page_type; 8733247Sgjelinek vnode_t *vn; 8743247Sgjelinek anoff_t off; 8753247Sgjelinek struct anon *ap; 8763247Sgjelinek 8773247Sgjelinek next = *first; 8783247Sgjelinek /* Shared anon slots don't change once set */ 8793247Sgjelinek ANON_LOCK_ENTER(&->a_rwlock, RW_READER); 8803247Sgjelinek for (;;) { 8813247Sgjelinek if (incore == B_TRUE) 8823247Sgjelinek next->vmb_type = VMUSAGE_BOUND_INCORE; 8833247Sgjelinek 8843247Sgjelinek if (next->vmb_type != VMUSAGE_BOUND_UNKNOWN) { 8853247Sgjelinek if (next == *last) 8863247Sgjelinek break; 8873247Sgjelinek next = next->vmb_next; 8883247Sgjelinek continue; 8893247Sgjelinek } 8903247Sgjelinek bound_type = next->vmb_type; 8913247Sgjelinek index = next->vmb_start; 8923247Sgjelinek while (index <= next->vmb_end) { 8933247Sgjelinek 8943247Sgjelinek /* 8953247Sgjelinek * These are used to determine how much to increment 8963247Sgjelinek * index when a large page is found. 8973247Sgjelinek */ 8983247Sgjelinek page_t *page; 8993247Sgjelinek pgcnt_t pgcnt = 1; 9003247Sgjelinek uint_t pgshft; 9013247Sgjelinek pgcnt_t pgmsk; 9023247Sgjelinek 9033247Sgjelinek ap = anon_get_ptr(amp->ahp, index); 9043247Sgjelinek if (ap != NULL) 9053247Sgjelinek swap_xlate(ap, &vn, &off); 9063247Sgjelinek 9073247Sgjelinek if (ap != NULL && vn != NULL && vn->v_pages != NULL && 9083247Sgjelinek (page = page_exists(vn, off)) != NULL) { 9093247Sgjelinek page_type = VMUSAGE_BOUND_INCORE; 9103247Sgjelinek if (page->p_szc > 0) { 9113247Sgjelinek pgcnt = page_get_pagecnt(page->p_szc); 9123247Sgjelinek pgshft = page_get_shift(page->p_szc); 9133247Sgjelinek pgmsk = (0x1 << (pgshft - PAGESHIFT)) 9143247Sgjelinek - 1; 9153247Sgjelinek } 9163247Sgjelinek } else { 9173247Sgjelinek page_type = VMUSAGE_BOUND_NOT_INCORE; 9183247Sgjelinek } 9193247Sgjelinek if (bound_type == VMUSAGE_BOUND_UNKNOWN) { 9203247Sgjelinek next->vmb_type = page_type; 9213247Sgjelinek } else if (next->vmb_type != page_type) { 9223247Sgjelinek /* 9233247Sgjelinek * if current bound type does not match page 9243247Sgjelinek * type, need to split off new bound. 9253247Sgjelinek */ 9263247Sgjelinek tmp = vmu_alloc_bound(); 9273247Sgjelinek tmp->vmb_type = page_type; 9283247Sgjelinek tmp->vmb_start = index; 9293247Sgjelinek tmp->vmb_end = next->vmb_end; 9303247Sgjelinek tmp->vmb_next = next->vmb_next; 9313247Sgjelinek next->vmb_end = index - 1; 9323247Sgjelinek next->vmb_next = tmp; 9333247Sgjelinek if (*last == next) 9343247Sgjelinek *last = tmp; 9353247Sgjelinek next = tmp; 9363247Sgjelinek } 9373247Sgjelinek if (pgcnt > 1) { 9383247Sgjelinek /* 9393247Sgjelinek * If inside large page, jump to next large 9403247Sgjelinek * page 9413247Sgjelinek */ 9423247Sgjelinek index = (index & ~pgmsk) + pgcnt; 9433247Sgjelinek } else { 9443247Sgjelinek index++; 9453247Sgjelinek } 9463247Sgjelinek } 9473247Sgjelinek if (next == *last) { 9483247Sgjelinek ASSERT(next->vmb_type != VMUSAGE_BOUND_UNKNOWN); 9493247Sgjelinek break; 9503247Sgjelinek } else 9513247Sgjelinek next = next->vmb_next; 9523247Sgjelinek } 9533247Sgjelinek ANON_LOCK_EXIT(&->a_rwlock); 9543247Sgjelinek } 9553247Sgjelinek 9563247Sgjelinek /* 9573247Sgjelinek * Same as vmu_amp_update_incore_bounds(), except for tracking 9583247Sgjelinek * incore-/not-incore for vnodes. 9593247Sgjelinek */ 9603247Sgjelinek static void 9613247Sgjelinek vmu_vnode_update_incore_bounds(vnode_t *vnode, vmu_bound_t **first, 9623247Sgjelinek vmu_bound_t **last) 9633247Sgjelinek { 9643247Sgjelinek vmu_bound_t *next; 9653247Sgjelinek vmu_bound_t *tmp; 9663247Sgjelinek pgcnt_t index; 9673247Sgjelinek short bound_type; 9683247Sgjelinek short page_type; 9693247Sgjelinek 9703247Sgjelinek next = *first; 9713247Sgjelinek for (;;) { 9723247Sgjelinek if (vnode->v_pages == NULL) 9733247Sgjelinek next->vmb_type = VMUSAGE_BOUND_NOT_INCORE; 9743247Sgjelinek 9753247Sgjelinek if (next->vmb_type != VMUSAGE_BOUND_UNKNOWN) { 9763247Sgjelinek if (next == *last) 9773247Sgjelinek break; 9783247Sgjelinek next = next->vmb_next; 9793247Sgjelinek continue; 9803247Sgjelinek } 9813247Sgjelinek 9823247Sgjelinek bound_type = next->vmb_type; 9833247Sgjelinek index = next->vmb_start; 9843247Sgjelinek while (index <= next->vmb_end) { 9853247Sgjelinek 9863247Sgjelinek /* 9873247Sgjelinek * These are used to determine how much to increment 9883247Sgjelinek * index when a large page is found. 9893247Sgjelinek */ 9903247Sgjelinek page_t *page; 9913247Sgjelinek pgcnt_t pgcnt = 1; 9923247Sgjelinek uint_t pgshft; 9933247Sgjelinek pgcnt_t pgmsk; 9943247Sgjelinek 9953247Sgjelinek if (vnode->v_pages != NULL && 9963247Sgjelinek (page = page_exists(vnode, ptob(index))) != NULL) { 9973247Sgjelinek page_type = VMUSAGE_BOUND_INCORE; 9983247Sgjelinek if (page->p_szc > 0) { 9993247Sgjelinek pgcnt = page_get_pagecnt(page->p_szc); 10003247Sgjelinek pgshft = page_get_shift(page->p_szc); 10013247Sgjelinek pgmsk = (0x1 << (pgshft - PAGESHIFT)) 10023247Sgjelinek - 1; 10033247Sgjelinek } 10043247Sgjelinek } else { 10053247Sgjelinek page_type = VMUSAGE_BOUND_NOT_INCORE; 10063247Sgjelinek } 10073247Sgjelinek if (bound_type == VMUSAGE_BOUND_UNKNOWN) { 10083247Sgjelinek next->vmb_type = page_type; 10093247Sgjelinek } else if (next->vmb_type != page_type) { 10103247Sgjelinek /* 10113247Sgjelinek * if current bound type does not match page 10123247Sgjelinek * type, need to split off new bound. 10133247Sgjelinek */ 10143247Sgjelinek tmp = vmu_alloc_bound(); 10153247Sgjelinek tmp->vmb_type = page_type; 10163247Sgjelinek tmp->vmb_start = index; 10173247Sgjelinek tmp->vmb_end = next->vmb_end; 10183247Sgjelinek tmp->vmb_next = next->vmb_next; 10193247Sgjelinek next->vmb_end = index - 1; 10203247Sgjelinek next->vmb_next = tmp; 10213247Sgjelinek if (*last == next) 10223247Sgjelinek *last = tmp; 10233247Sgjelinek next = tmp; 10243247Sgjelinek } 10253247Sgjelinek if (pgcnt > 1) { 10263247Sgjelinek /* 10273247Sgjelinek * If inside large page, jump to next large 10283247Sgjelinek * page 10293247Sgjelinek */ 10303247Sgjelinek index = (index & ~pgmsk) + pgcnt; 10313247Sgjelinek } else { 10323247Sgjelinek index++; 10333247Sgjelinek } 10343247Sgjelinek } 10353247Sgjelinek if (next == *last) { 10363247Sgjelinek ASSERT(next->vmb_type != VMUSAGE_BOUND_UNKNOWN); 10373247Sgjelinek break; 10383247Sgjelinek } else 10393247Sgjelinek next = next->vmb_next; 10403247Sgjelinek } 10413247Sgjelinek } 10423247Sgjelinek 10433247Sgjelinek /* 10443247Sgjelinek * Calculate the rss and swap consumed by a segment. vmu_entities is the 10453247Sgjelinek * list of entities to visit. For shared segments, the vnode or amp 10463247Sgjelinek * is looked up in each entity to see if has been already counted. Private 10473247Sgjelinek * anon pages are checked per entity to ensure that cow pages are not 10483247Sgjelinek * double counted. 10493247Sgjelinek * 10503247Sgjelinek * For private mapped files, first the amp is checked for private pages. 10513247Sgjelinek * Bounds not backed by the amp are looked up in the vnode for each entity 10523247Sgjelinek * to avoid double counting of private COW vnode pages. 10533247Sgjelinek */ 10543247Sgjelinek static void 10553247Sgjelinek vmu_calculate_seg(vmu_entity_t *vmu_entities, struct seg *seg) 10563247Sgjelinek { 10573247Sgjelinek struct segvn_data *svd; 10583247Sgjelinek struct shm_data *shmd; 10593247Sgjelinek struct spt_data *sptd; 10603247Sgjelinek vmu_object_t *shared_object = NULL; 10613247Sgjelinek vmu_object_t *entity_object = NULL; 10623247Sgjelinek vmu_entity_t *entity; 10633247Sgjelinek vmusage_t *result; 10643247Sgjelinek vmu_bound_t *first = NULL; 10653247Sgjelinek vmu_bound_t *last = NULL; 10663247Sgjelinek vmu_bound_t *cur = NULL; 10673247Sgjelinek vmu_bound_t *e_first = NULL; 10683247Sgjelinek vmu_bound_t *e_last = NULL; 10693247Sgjelinek vmu_bound_t *tmp; 10703247Sgjelinek pgcnt_t p_index, s_index, p_start, p_end, s_start, s_end, rss, virt; 10713247Sgjelinek struct anon_map *private_amp = NULL; 10723247Sgjelinek boolean_t incore = B_FALSE; 10733247Sgjelinek boolean_t shared = B_FALSE; 10743247Sgjelinek int file = 0; 10753247Sgjelinek pgcnt_t swresv = 0; 10763247Sgjelinek pgcnt_t panon = 0; 10773247Sgjelinek 10783247Sgjelinek /* Can zero-length segments exist? Not sure, so parenoia */ 10793247Sgjelinek if (seg->s_size <= 0) 10803247Sgjelinek return; 10813247Sgjelinek 10823247Sgjelinek /* 10833247Sgjelinek * Figure out if there is a shared object (such as a named vnode or 10843247Sgjelinek * a shared amp, then figure out if there is a private amp, which 10853247Sgjelinek * identifies private pages. 10863247Sgjelinek */ 10873247Sgjelinek if (seg->s_ops == &segvn_ops) { 10883247Sgjelinek svd = (struct segvn_data *)seg->s_data; 10893247Sgjelinek if (svd->type == MAP_SHARED) 10903247Sgjelinek shared = B_TRUE; 10913247Sgjelinek else 10923247Sgjelinek swresv = svd->swresv; 10933247Sgjelinek 10943247Sgjelinek if (svd->vp != NULL) { 10953247Sgjelinek file = 1; 10963247Sgjelinek shared_object = vmu_find_insert_object( 10973247Sgjelinek vmu_data.vmu_all_vnodes_hash, (caddr_t)svd->vp, 10983247Sgjelinek VMUSAGE_TYPE_VNODE); 10993247Sgjelinek s_start = btop(svd->offset); 11003247Sgjelinek s_end = btop(svd->offset + seg->s_size) - 1; 11013247Sgjelinek } 11023247Sgjelinek if (svd->amp != NULL && svd->type == MAP_SHARED) { 11033247Sgjelinek ASSERT(shared_object == NULL); 11043247Sgjelinek shared_object = vmu_find_insert_object( 11053247Sgjelinek vmu_data.vmu_all_amps_hash, (caddr_t)svd->amp, 11063247Sgjelinek VMUSAGE_TYPE_AMP); 11073247Sgjelinek s_start = svd->anon_index; 11083247Sgjelinek s_end = svd->anon_index + btop(seg->s_size) - 1; 11093247Sgjelinek /* schedctl mappings are always in core */ 11103247Sgjelinek if (svd->amp->swresv == 0) 11113247Sgjelinek incore = B_TRUE; 11123247Sgjelinek } 1113*4426Saguzovsk SEGVN_LOCK_ENTER(seg->s_as, &svd->lock, RW_READER); 1114*4426Saguzovsk /* 1115*4426Saguzovsk * Text replication anon maps can be shared across all zones. 1116*4426Saguzovsk * Space used for text replication is typically capped as 1117*4426Saguzovsk * small % of memory. To keep it simple for now we don't 1118*4426Saguzovsk * account for swap and memory space used for text replication. 1119*4426Saguzovsk */ 1120*4426Saguzovsk if (svd->tr_state == SEGVN_TR_OFF && svd->amp != NULL && 1121*4426Saguzovsk svd->type == MAP_PRIVATE) { 11223247Sgjelinek private_amp = svd->amp; 11233247Sgjelinek p_start = svd->anon_index; 11243247Sgjelinek p_end = svd->anon_index + btop(seg->s_size) - 1; 11253247Sgjelinek } 1126*4426Saguzovsk SEGVN_LOCK_EXIT(seg->s_as, &svd->lock); 11273247Sgjelinek } else if (seg->s_ops == &segspt_shmops) { 11283247Sgjelinek shared = B_TRUE; 11293247Sgjelinek shmd = (struct shm_data *)seg->s_data; 11303247Sgjelinek shared_object = vmu_find_insert_object( 11313247Sgjelinek vmu_data.vmu_all_amps_hash, (caddr_t)shmd->shm_amp, 11323247Sgjelinek VMUSAGE_TYPE_AMP); 11333247Sgjelinek s_start = 0; 11343247Sgjelinek s_end = btop(seg->s_size) - 1; 11353247Sgjelinek sptd = shmd->shm_sptseg->s_data; 11363247Sgjelinek 11373247Sgjelinek /* ism segments are always incore and do not reserve swap */ 11383247Sgjelinek if (sptd->spt_flags & SHM_SHARE_MMU) 11393247Sgjelinek incore = B_TRUE; 11403247Sgjelinek 11413247Sgjelinek } else { 11423247Sgjelinek return; 11433247Sgjelinek } 11443247Sgjelinek 11453247Sgjelinek /* 11463247Sgjelinek * If there is a private amp, count anon pages that exist. If an 11473247Sgjelinek * anon has a refcnt > 1 (cow sharing), then save the anon in a 11483247Sgjelinek * hash so that it is not double counted. 11493247Sgjelinek * 11503247Sgjelinek * If there is also a shared object, they figure out the bounds 11513247Sgjelinek * which are not mapped by the private amp. 11523247Sgjelinek */ 11533247Sgjelinek if (private_amp != NULL) { 11543247Sgjelinek 11553247Sgjelinek /* Enter as writer to prevent cow anons from being freed */ 11563247Sgjelinek ANON_LOCK_ENTER(&private_amp->a_rwlock, RW_WRITER); 11573247Sgjelinek 11583247Sgjelinek p_index = p_start; 11593247Sgjelinek s_index = s_start; 11603247Sgjelinek 11613247Sgjelinek while (p_index <= p_end) { 11623247Sgjelinek 11633247Sgjelinek pgcnt_t p_index_next; 11643247Sgjelinek pgcnt_t p_bound_size; 11653247Sgjelinek int cnt; 11663247Sgjelinek anoff_t off; 11673247Sgjelinek struct vnode *vn; 11683247Sgjelinek struct anon *ap; 11693247Sgjelinek page_t *page; /* For handling of large */ 11703247Sgjelinek pgcnt_t pgcnt = 1; /* pages */ 11713247Sgjelinek pgcnt_t pgstart; 11723247Sgjelinek pgcnt_t pgend; 11733247Sgjelinek uint_t pgshft; 11743247Sgjelinek pgcnt_t pgmsk; 11753247Sgjelinek 11763247Sgjelinek p_index_next = p_index; 11773247Sgjelinek ap = anon_get_next_ptr(private_amp->ahp, 11783247Sgjelinek &p_index_next); 11793247Sgjelinek 11803247Sgjelinek /* 11813247Sgjelinek * If next anon is past end of mapping, simulate 11823247Sgjelinek * end of anon so loop terminates. 11833247Sgjelinek */ 11843247Sgjelinek if (p_index_next > p_end) { 11853247Sgjelinek p_index_next = p_end + 1; 11863247Sgjelinek ap = NULL; 11873247Sgjelinek } 11883247Sgjelinek /* 11893247Sgjelinek * For cow segments, keep track of bounds not 11903247Sgjelinek * backed by private amp so they can be looked 11913247Sgjelinek * up in the backing vnode 11923247Sgjelinek */ 11933247Sgjelinek if (p_index_next != p_index) { 11943247Sgjelinek 11953247Sgjelinek /* 11963247Sgjelinek * Compute index difference between anon and 11973247Sgjelinek * previous anon. 11983247Sgjelinek */ 11993247Sgjelinek p_bound_size = p_index_next - p_index - 1; 12003247Sgjelinek 12013247Sgjelinek if (shared_object != NULL) { 12023247Sgjelinek cur = vmu_alloc_bound(); 12033247Sgjelinek cur->vmb_next = NULL; 12043247Sgjelinek cur->vmb_start = s_index; 12053247Sgjelinek cur->vmb_end = s_index + p_bound_size; 12063247Sgjelinek cur->vmb_type = VMUSAGE_BOUND_UNKNOWN; 12073247Sgjelinek if (first == NULL) { 12083247Sgjelinek first = cur; 12093247Sgjelinek last = cur; 12103247Sgjelinek } else { 12113247Sgjelinek last->vmb_next = cur; 12123247Sgjelinek last = cur; 12133247Sgjelinek } 12143247Sgjelinek } 12153247Sgjelinek p_index = p_index + p_bound_size + 1; 12163247Sgjelinek s_index = s_index + p_bound_size + 1; 12173247Sgjelinek } 12183247Sgjelinek 12193247Sgjelinek /* Detect end of anons in amp */ 12203247Sgjelinek if (ap == NULL) 12213247Sgjelinek break; 12223247Sgjelinek 12233247Sgjelinek cnt = ap->an_refcnt; 12243247Sgjelinek swap_xlate(ap, &vn, &off); 12253247Sgjelinek 12263247Sgjelinek if (vn == NULL || vn->v_pages == NULL || 12273247Sgjelinek (page = page_exists(vn, off)) == NULL) { 12283247Sgjelinek p_index++; 12293247Sgjelinek s_index++; 12303247Sgjelinek continue; 12313247Sgjelinek } 12323247Sgjelinek 12333247Sgjelinek /* 12343247Sgjelinek * If large page is found, compute portion of large 12353247Sgjelinek * page in mapping, and increment indicies to the next 12363247Sgjelinek * large page. 12373247Sgjelinek */ 12383247Sgjelinek if (page->p_szc > 0) { 12393247Sgjelinek 12403247Sgjelinek pgcnt = page_get_pagecnt(page->p_szc); 12413247Sgjelinek pgshft = page_get_shift(page->p_szc); 12423247Sgjelinek pgmsk = (0x1 << (pgshft - PAGESHIFT)) - 1; 12433247Sgjelinek 12443247Sgjelinek /* First page in large page */ 12453247Sgjelinek pgstart = p_index & ~pgmsk; 12463247Sgjelinek /* Last page in large page */ 12473247Sgjelinek pgend = pgstart + pgcnt - 1; 12483247Sgjelinek /* 12493247Sgjelinek * Artifically end page if page extends past 12503247Sgjelinek * end of mapping. 12513247Sgjelinek */ 12523247Sgjelinek if (pgend > p_end) 12533247Sgjelinek pgend = p_end; 12543247Sgjelinek 12553247Sgjelinek /* 12563247Sgjelinek * Compute number of pages from large page 12573247Sgjelinek * which are mapped. 12583247Sgjelinek */ 12593247Sgjelinek pgcnt = pgend - p_index + 1; 12603247Sgjelinek 12613247Sgjelinek /* 12623247Sgjelinek * Point indicies at page after large page, 12633247Sgjelinek * or at page after end of mapping. 12643247Sgjelinek */ 12653247Sgjelinek p_index += pgcnt; 12663247Sgjelinek s_index += pgcnt; 12673247Sgjelinek } else { 12683247Sgjelinek p_index++; 12693247Sgjelinek s_index++; 12703247Sgjelinek } 12713247Sgjelinek 12723247Sgjelinek /* 12733247Sgjelinek * Assume anon structs with a refcnt 12743247Sgjelinek * of 1 are not cow shared, so there 12753247Sgjelinek * is no reason to track them per entity. 12763247Sgjelinek */ 12773247Sgjelinek if (cnt == 1) { 12783247Sgjelinek panon += pgcnt; 12793247Sgjelinek continue; 12803247Sgjelinek } 12813247Sgjelinek for (entity = vmu_entities; entity != NULL; 12823247Sgjelinek entity = entity->vme_next_calc) { 12833247Sgjelinek 12843247Sgjelinek result = &entity->vme_result; 12853247Sgjelinek /* 12863247Sgjelinek * Track cow anons per entity so 12873247Sgjelinek * they are not double counted. 12883247Sgjelinek */ 12893247Sgjelinek if (vmu_find_insert_anon(entity->vme_anon_hash, 12903247Sgjelinek (caddr_t)ap) == 0) 12913247Sgjelinek continue; 12923247Sgjelinek 12933247Sgjelinek result->vmu_rss_all += (pgcnt << PAGESHIFT); 12943247Sgjelinek result->vmu_rss_private += 12953247Sgjelinek (pgcnt << PAGESHIFT); 12963247Sgjelinek } 12973247Sgjelinek } 12983247Sgjelinek ANON_LOCK_EXIT(&private_amp->a_rwlock); 12993247Sgjelinek } 13003247Sgjelinek 13013247Sgjelinek /* Add up resident anon and swap reserved for private mappings */ 13023247Sgjelinek if (swresv > 0 || panon > 0) { 13033247Sgjelinek for (entity = vmu_entities; entity != NULL; 13043247Sgjelinek entity = entity->vme_next_calc) { 13053247Sgjelinek result = &entity->vme_result; 13063247Sgjelinek result->vmu_swap_all += swresv; 13073247Sgjelinek result->vmu_swap_private += swresv; 13083247Sgjelinek result->vmu_rss_all += (panon << PAGESHIFT); 13093247Sgjelinek result->vmu_rss_private += (panon << PAGESHIFT); 13103247Sgjelinek } 13113247Sgjelinek } 13123247Sgjelinek 13133247Sgjelinek /* Compute resident pages backing shared amp or named vnode */ 13143247Sgjelinek if (shared_object != NULL) { 13153247Sgjelinek if (first == NULL) { 13163247Sgjelinek /* 13173247Sgjelinek * No private amp, or private amp has no anon 13183247Sgjelinek * structs. This means entire segment is backed by 13193247Sgjelinek * the shared object. 13203247Sgjelinek */ 13213247Sgjelinek first = vmu_alloc_bound(); 13223247Sgjelinek first->vmb_next = NULL; 13233247Sgjelinek first->vmb_start = s_start; 13243247Sgjelinek first->vmb_end = s_end; 13253247Sgjelinek first->vmb_type = VMUSAGE_BOUND_UNKNOWN; 13263247Sgjelinek } 13273247Sgjelinek /* 13283247Sgjelinek * Iterate bounds not backed by private amp, and compute 13293247Sgjelinek * resident pages. 13303247Sgjelinek */ 13313247Sgjelinek cur = first; 13323247Sgjelinek while (cur != NULL) { 13333247Sgjelinek 13343247Sgjelinek if (vmu_insert_lookup_object_bounds(shared_object, 13353247Sgjelinek cur->vmb_start, cur->vmb_end, VMUSAGE_BOUND_UNKNOWN, 13363247Sgjelinek &first, &last) > 0) { 13373247Sgjelinek /* new bounds, find incore/not-incore */ 13383247Sgjelinek if (shared_object->vmo_type == 13393247Sgjelinek VMUSAGE_TYPE_VNODE) 13403247Sgjelinek vmu_vnode_update_incore_bounds( 13413247Sgjelinek (vnode_t *) 13423247Sgjelinek shared_object->vmo_key, &first, 13433247Sgjelinek &last); 13443247Sgjelinek else 13453247Sgjelinek vmu_amp_update_incore_bounds( 13463247Sgjelinek (struct anon_map *) 13473247Sgjelinek shared_object->vmo_key, &first, 13483247Sgjelinek &last, incore); 13493247Sgjelinek vmu_merge_bounds(&first, &last); 13503247Sgjelinek } 13513247Sgjelinek for (entity = vmu_entities; entity != NULL; 13523247Sgjelinek entity = entity->vme_next_calc) { 13533247Sgjelinek 13543247Sgjelinek result = &entity->vme_result; 13553247Sgjelinek 13563247Sgjelinek entity_object = vmu_find_insert_object( 13573247Sgjelinek shared_object->vmo_type == 13583247Sgjelinek VMUSAGE_TYPE_VNODE ? entity->vme_vnode_hash: 13593247Sgjelinek entity->vme_amp_hash, 13603247Sgjelinek shared_object->vmo_key, 13613247Sgjelinek shared_object->vmo_type); 13623247Sgjelinek 13633247Sgjelinek virt = vmu_insert_lookup_object_bounds( 13643247Sgjelinek entity_object, cur->vmb_start, cur->vmb_end, 13653247Sgjelinek VMUSAGE_BOUND_UNKNOWN, &e_first, &e_last); 13663247Sgjelinek 13673247Sgjelinek if (virt == 0) 13683247Sgjelinek continue; 13693247Sgjelinek /* 13703247Sgjelinek * Range visited for this entity 13713247Sgjelinek */ 13723247Sgjelinek rss = vmu_update_bounds(&e_first, 13733247Sgjelinek &e_last, first, last); 13743247Sgjelinek result->vmu_rss_all += (rss << PAGESHIFT); 13753247Sgjelinek if (shared == B_TRUE && file == B_FALSE) { 13763247Sgjelinek /* shared anon mapping */ 13773247Sgjelinek result->vmu_swap_all += 13783247Sgjelinek (virt << PAGESHIFT); 13793247Sgjelinek result->vmu_swap_shared += 13803247Sgjelinek (virt << PAGESHIFT); 13813247Sgjelinek result->vmu_rss_shared += 13823247Sgjelinek (rss << PAGESHIFT); 13833247Sgjelinek } else if (shared == B_TRUE && file == B_TRUE) { 13843247Sgjelinek /* shared file mapping */ 13853247Sgjelinek result->vmu_rss_shared += 13863247Sgjelinek (rss << PAGESHIFT); 13873247Sgjelinek } else if (shared == B_FALSE && 13883247Sgjelinek file == B_TRUE) { 13893247Sgjelinek /* private file mapping */ 13903247Sgjelinek result->vmu_rss_private += 13913247Sgjelinek (rss << PAGESHIFT); 13923247Sgjelinek } 13933247Sgjelinek vmu_merge_bounds(&e_first, &e_last); 13943247Sgjelinek } 13953247Sgjelinek tmp = cur; 13963247Sgjelinek cur = cur->vmb_next; 13973247Sgjelinek vmu_free_bound(tmp); 13983247Sgjelinek } 13993247Sgjelinek } 14003247Sgjelinek } 14013247Sgjelinek 14023247Sgjelinek /* 14033247Sgjelinek * Based on the current calculation flags, find the relevant entities 14043247Sgjelinek * which are relative to the process. Then calculate each segment 14053247Sgjelinek * in the process'es address space for each relevant entity. 14063247Sgjelinek */ 14073247Sgjelinek static void 14083247Sgjelinek vmu_calculate_proc(proc_t *p) 14093247Sgjelinek { 14103247Sgjelinek vmu_entity_t *entities = NULL; 14113247Sgjelinek vmu_zone_t *zone; 14123247Sgjelinek vmu_entity_t *tmp; 14133247Sgjelinek struct as *as; 14143247Sgjelinek struct seg *seg; 14153247Sgjelinek int ret; 14163247Sgjelinek 14173247Sgjelinek /* Figure out which entities are being computed */ 14183247Sgjelinek if ((vmu_data.vmu_system) != NULL) { 14193247Sgjelinek tmp = vmu_data.vmu_system; 14203247Sgjelinek tmp->vme_next_calc = entities; 14213247Sgjelinek entities = tmp; 14223247Sgjelinek } 14233247Sgjelinek if (vmu_data.vmu_calc_flags & 14243247Sgjelinek (VMUSAGE_ZONE | VMUSAGE_ALL_ZONES | VMUSAGE_PROJECTS | 14253247Sgjelinek VMUSAGE_ALL_PROJECTS | VMUSAGE_TASKS | VMUSAGE_ALL_TASKS | 14263247Sgjelinek VMUSAGE_RUSERS | VMUSAGE_ALL_RUSERS | VMUSAGE_EUSERS | 14273247Sgjelinek VMUSAGE_ALL_EUSERS)) { 14283247Sgjelinek ret = i_mod_hash_find_nosync(vmu_data.vmu_zones_hash, 14293247Sgjelinek (mod_hash_key_t)(uintptr_t)p->p_zone->zone_id, 14303247Sgjelinek (mod_hash_val_t *)&zone); 14313247Sgjelinek if (ret != 0) { 14323247Sgjelinek zone = vmu_alloc_zone(p->p_zone->zone_id); 14333247Sgjelinek ret = i_mod_hash_insert_nosync(vmu_data.vmu_zones_hash, 14343247Sgjelinek (mod_hash_key_t)(uintptr_t)p->p_zone->zone_id, 14353247Sgjelinek (mod_hash_val_t)zone, (mod_hash_hndl_t)0); 14363247Sgjelinek ASSERT(ret == 0); 14373247Sgjelinek } 14383247Sgjelinek if (zone->vmz_zone != NULL) { 14393247Sgjelinek tmp = zone->vmz_zone; 14403247Sgjelinek tmp->vme_next_calc = entities; 14413247Sgjelinek entities = tmp; 14423247Sgjelinek } 14433247Sgjelinek if (vmu_data.vmu_calc_flags & 14443247Sgjelinek (VMUSAGE_PROJECTS | VMUSAGE_ALL_PROJECTS)) { 14453247Sgjelinek tmp = vmu_find_insert_entity(zone->vmz_projects_hash, 14463247Sgjelinek p->p_task->tk_proj->kpj_id, VMUSAGE_PROJECTS, 14473247Sgjelinek zone->vmz_id); 14483247Sgjelinek tmp->vme_next_calc = entities; 14493247Sgjelinek entities = tmp; 14503247Sgjelinek } 14513247Sgjelinek if (vmu_data.vmu_calc_flags & 14523247Sgjelinek (VMUSAGE_TASKS | VMUSAGE_ALL_TASKS)) { 14533247Sgjelinek tmp = vmu_find_insert_entity(zone->vmz_tasks_hash, 14543247Sgjelinek p->p_task->tk_tkid, VMUSAGE_TASKS, zone->vmz_id); 14553247Sgjelinek tmp->vme_next_calc = entities; 14563247Sgjelinek entities = tmp; 14573247Sgjelinek } 14583247Sgjelinek if (vmu_data.vmu_calc_flags & 14593247Sgjelinek (VMUSAGE_RUSERS | VMUSAGE_ALL_RUSERS)) { 14603247Sgjelinek tmp = vmu_find_insert_entity(zone->vmz_rusers_hash, 14613247Sgjelinek crgetruid(p->p_cred), VMUSAGE_RUSERS, zone->vmz_id); 14623247Sgjelinek tmp->vme_next_calc = entities; 14633247Sgjelinek entities = tmp; 14643247Sgjelinek } 14653247Sgjelinek if (vmu_data.vmu_calc_flags & 14663247Sgjelinek (VMUSAGE_EUSERS | VMUSAGE_ALL_EUSERS)) { 14673247Sgjelinek tmp = vmu_find_insert_entity(zone->vmz_eusers_hash, 14683247Sgjelinek crgetuid(p->p_cred), VMUSAGE_EUSERS, zone->vmz_id); 14693247Sgjelinek tmp->vme_next_calc = entities; 14703247Sgjelinek entities = tmp; 14713247Sgjelinek } 14723247Sgjelinek } 14733247Sgjelinek /* Entities which collapse projects and users for all zones */ 14743247Sgjelinek if (vmu_data.vmu_calc_flags & VMUSAGE_COL_PROJECTS) { 14753247Sgjelinek tmp = vmu_find_insert_entity(vmu_data.vmu_projects_col_hash, 14763247Sgjelinek p->p_task->tk_proj->kpj_id, VMUSAGE_PROJECTS, ALL_ZONES); 14773247Sgjelinek tmp->vme_next_calc = entities; 14783247Sgjelinek entities = tmp; 14793247Sgjelinek } 14803247Sgjelinek if (vmu_data.vmu_calc_flags & VMUSAGE_COL_RUSERS) { 14813247Sgjelinek tmp = vmu_find_insert_entity(vmu_data.vmu_rusers_col_hash, 14823247Sgjelinek crgetruid(p->p_cred), VMUSAGE_RUSERS, ALL_ZONES); 14833247Sgjelinek tmp->vme_next_calc = entities; 14843247Sgjelinek entities = tmp; 14853247Sgjelinek } 14863247Sgjelinek if (vmu_data.vmu_calc_flags & VMUSAGE_COL_EUSERS) { 14873247Sgjelinek tmp = vmu_find_insert_entity(vmu_data.vmu_eusers_col_hash, 14883247Sgjelinek crgetuid(p->p_cred), VMUSAGE_EUSERS, ALL_ZONES); 14893247Sgjelinek tmp->vme_next_calc = entities; 14903247Sgjelinek entities = tmp; 14913247Sgjelinek } 14923247Sgjelinek 14933247Sgjelinek ASSERT(entities != NULL); 14943247Sgjelinek /* process all segs in process's address space */ 14953247Sgjelinek as = p->p_as; 14963247Sgjelinek AS_LOCK_ENTER(as, &as->a_lock, RW_READER); 14973247Sgjelinek for (seg = AS_SEGFIRST(as); seg != NULL; 14983247Sgjelinek seg = AS_SEGNEXT(as, seg)) { 14993247Sgjelinek vmu_calculate_seg(entities, seg); 15003247Sgjelinek } 15013247Sgjelinek AS_LOCK_EXIT(as, &as->a_lock); 15023247Sgjelinek } 15033247Sgjelinek 15043247Sgjelinek /* 15053247Sgjelinek * Free data created by previous call to vmu_calculate(). 15063247Sgjelinek */ 15073247Sgjelinek static void 15083247Sgjelinek vmu_clear_calc() 15093247Sgjelinek { 15103247Sgjelinek if (vmu_data.vmu_system != NULL) 15113247Sgjelinek vmu_free_entity(vmu_data.vmu_system); 15123247Sgjelinek vmu_data.vmu_system = NULL; 15133247Sgjelinek if (vmu_data.vmu_zones_hash != NULL) 15143247Sgjelinek i_mod_hash_clear_nosync(vmu_data.vmu_zones_hash); 15153247Sgjelinek if (vmu_data.vmu_projects_col_hash != NULL) 15163247Sgjelinek i_mod_hash_clear_nosync(vmu_data.vmu_projects_col_hash); 15173247Sgjelinek if (vmu_data.vmu_rusers_col_hash != NULL) 15183247Sgjelinek i_mod_hash_clear_nosync(vmu_data.vmu_rusers_col_hash); 15193247Sgjelinek if (vmu_data.vmu_eusers_col_hash != NULL) 15203247Sgjelinek i_mod_hash_clear_nosync(vmu_data.vmu_eusers_col_hash); 15213247Sgjelinek 15223247Sgjelinek i_mod_hash_clear_nosync(vmu_data.vmu_all_vnodes_hash); 15233247Sgjelinek i_mod_hash_clear_nosync(vmu_data.vmu_all_amps_hash); 15243247Sgjelinek } 15253247Sgjelinek 15263247Sgjelinek /* 15273247Sgjelinek * Free unused data structures. These can result if the system workload 15283247Sgjelinek * decreases between calculations. 15293247Sgjelinek */ 15303247Sgjelinek static void 15313247Sgjelinek vmu_free_extra() 15323247Sgjelinek { 15333247Sgjelinek vmu_bound_t *tb; 15343247Sgjelinek vmu_object_t *to; 15353247Sgjelinek vmu_entity_t *te; 15363247Sgjelinek vmu_zone_t *tz; 15373247Sgjelinek 15383247Sgjelinek while (vmu_data.vmu_free_bounds != NULL) { 15393247Sgjelinek tb = vmu_data.vmu_free_bounds; 15403247Sgjelinek vmu_data.vmu_free_bounds = vmu_data.vmu_free_bounds->vmb_next; 15413247Sgjelinek kmem_cache_free(vmu_bound_cache, tb); 15423247Sgjelinek } 15433247Sgjelinek while (vmu_data.vmu_free_objects != NULL) { 15443247Sgjelinek to = vmu_data.vmu_free_objects; 15453247Sgjelinek vmu_data.vmu_free_objects = 15463247Sgjelinek vmu_data.vmu_free_objects->vmo_next; 15473247Sgjelinek kmem_cache_free(vmu_object_cache, to); 15483247Sgjelinek } 15493247Sgjelinek while (vmu_data.vmu_free_entities != NULL) { 15503247Sgjelinek te = vmu_data.vmu_free_entities; 15513247Sgjelinek vmu_data.vmu_free_entities = 15523247Sgjelinek vmu_data.vmu_free_entities->vme_next; 15533247Sgjelinek if (te->vme_vnode_hash != NULL) 15543247Sgjelinek mod_hash_destroy_hash(te->vme_vnode_hash); 15553247Sgjelinek if (te->vme_amp_hash != NULL) 15563247Sgjelinek mod_hash_destroy_hash(te->vme_amp_hash); 15573247Sgjelinek if (te->vme_anon_hash != NULL) 15583247Sgjelinek mod_hash_destroy_hash(te->vme_anon_hash); 15593247Sgjelinek kmem_free(te, sizeof (vmu_entity_t)); 15603247Sgjelinek } 15613247Sgjelinek while (vmu_data.vmu_free_zones != NULL) { 15623247Sgjelinek tz = vmu_data.vmu_free_zones; 15633247Sgjelinek vmu_data.vmu_free_zones = 15643247Sgjelinek vmu_data.vmu_free_zones->vmz_next; 15653247Sgjelinek if (tz->vmz_projects_hash != NULL) 15663247Sgjelinek mod_hash_destroy_hash(tz->vmz_projects_hash); 15673247Sgjelinek if (tz->vmz_tasks_hash != NULL) 15683247Sgjelinek mod_hash_destroy_hash(tz->vmz_tasks_hash); 15693247Sgjelinek if (tz->vmz_rusers_hash != NULL) 15703247Sgjelinek mod_hash_destroy_hash(tz->vmz_rusers_hash); 15713247Sgjelinek if (tz->vmz_eusers_hash != NULL) 15723247Sgjelinek mod_hash_destroy_hash(tz->vmz_eusers_hash); 15733247Sgjelinek kmem_free(tz, sizeof (vmu_zone_t)); 15743247Sgjelinek } 15753247Sgjelinek } 15763247Sgjelinek 15773247Sgjelinek extern kcondvar_t *pr_pid_cv; 15783247Sgjelinek 15793247Sgjelinek /* 15803247Sgjelinek * Determine which entity types are relevant and allocate the hashes to 15813247Sgjelinek * track them. Then walk the process table and count rss and swap 15823247Sgjelinek * for each process'es address space. Address space object such as 15833247Sgjelinek * vnodes, amps and anons are tracked per entity, so that they are 15843247Sgjelinek * not double counted in the results. 15853247Sgjelinek * 15863247Sgjelinek */ 15873247Sgjelinek static void 15883247Sgjelinek vmu_calculate() 15893247Sgjelinek { 15903247Sgjelinek int i = 0; 15913247Sgjelinek int ret; 15923247Sgjelinek proc_t *p; 15933247Sgjelinek 15943247Sgjelinek vmu_clear_calc(); 15953247Sgjelinek 15963247Sgjelinek if (vmu_data.vmu_calc_flags & VMUSAGE_SYSTEM) 15973247Sgjelinek vmu_data.vmu_system = vmu_alloc_entity(0, VMUSAGE_SYSTEM, 15983247Sgjelinek ALL_ZONES); 15993247Sgjelinek 16003247Sgjelinek /* 16013247Sgjelinek * Walk process table and calculate rss of each proc. 16023247Sgjelinek * 16033247Sgjelinek * Pidlock and p_lock cannot be held while doing the rss calculation. 16043247Sgjelinek * This is because: 16053247Sgjelinek * 1. The calculation allocates using KM_SLEEP. 16063247Sgjelinek * 2. The calculation grabs a_lock, which cannot be grabbed 16073247Sgjelinek * after p_lock. 16083247Sgjelinek * 16093247Sgjelinek * Since pidlock must be dropped, we cannot simply just walk the 16103247Sgjelinek * practive list. Instead, we walk the process table, and sprlock 16113247Sgjelinek * each process to ensure that it does not exit during the 16123247Sgjelinek * calculation. 16133247Sgjelinek */ 16143247Sgjelinek 16153247Sgjelinek mutex_enter(&pidlock); 16163247Sgjelinek for (i = 0; i < v.v_proc; i++) { 16173247Sgjelinek again: 16183247Sgjelinek p = pid_entry(i); 16193247Sgjelinek if (p == NULL) 16203247Sgjelinek continue; 16213247Sgjelinek 16223247Sgjelinek mutex_enter(&p->p_lock); 16233247Sgjelinek mutex_exit(&pidlock); 16243247Sgjelinek 16253247Sgjelinek if (panicstr) { 16263247Sgjelinek mutex_exit(&p->p_lock); 16273247Sgjelinek return; 16283247Sgjelinek } 16293247Sgjelinek 16303247Sgjelinek /* Try to set P_PR_LOCK */ 16313247Sgjelinek ret = sprtrylock_proc(p); 16323247Sgjelinek if (ret == -1) { 16333247Sgjelinek /* Process in invalid state */ 16343247Sgjelinek mutex_exit(&p->p_lock); 16353247Sgjelinek mutex_enter(&pidlock); 16363247Sgjelinek continue; 16373247Sgjelinek } else if (ret == 1) { 16383247Sgjelinek /* 16393247Sgjelinek * P_PR_LOCK is already set. Wait and try again. 16403247Sgjelinek * This also drops p_lock. 16413247Sgjelinek */ 16423247Sgjelinek sprwaitlock_proc(p); 16433247Sgjelinek mutex_enter(&pidlock); 16443247Sgjelinek goto again; 16453247Sgjelinek } 16463247Sgjelinek mutex_exit(&p->p_lock); 16473247Sgjelinek 16483247Sgjelinek vmu_calculate_proc(p); 16493247Sgjelinek 16503247Sgjelinek mutex_enter(&p->p_lock); 16513247Sgjelinek sprunlock(p); 16523247Sgjelinek mutex_enter(&pidlock); 16533247Sgjelinek } 16543247Sgjelinek mutex_exit(&pidlock); 16553247Sgjelinek 16563247Sgjelinek vmu_free_extra(); 16573247Sgjelinek } 16583247Sgjelinek 16593247Sgjelinek /* 16603247Sgjelinek * allocate a new cache for N results satisfying flags 16613247Sgjelinek */ 16623247Sgjelinek vmu_cache_t * 16633247Sgjelinek vmu_cache_alloc(size_t nres, uint_t flags) 16643247Sgjelinek { 16653247Sgjelinek vmu_cache_t *cache; 16663247Sgjelinek 16673247Sgjelinek cache = kmem_zalloc(sizeof (vmu_cache_t), KM_SLEEP); 16683247Sgjelinek cache->vmc_results = kmem_zalloc(sizeof (vmusage_t) * nres, KM_SLEEP); 16693247Sgjelinek cache->vmc_nresults = nres; 16703247Sgjelinek cache->vmc_flags = flags; 16713247Sgjelinek cache->vmc_refcnt = 1; 16723247Sgjelinek return (cache); 16733247Sgjelinek } 16743247Sgjelinek 16753247Sgjelinek /* 16763247Sgjelinek * Make sure cached results are not freed 16773247Sgjelinek */ 16783247Sgjelinek static void 16793247Sgjelinek vmu_cache_hold(vmu_cache_t *cache) 16803247Sgjelinek { 16813247Sgjelinek ASSERT(MUTEX_HELD(&vmu_data.vmu_lock)); 16823247Sgjelinek cache->vmc_refcnt++; 16833247Sgjelinek } 16843247Sgjelinek 16853247Sgjelinek /* 16863247Sgjelinek * free cache data 16873247Sgjelinek */ 16883247Sgjelinek static void 16893247Sgjelinek vmu_cache_rele(vmu_cache_t *cache) 16903247Sgjelinek { 16913247Sgjelinek ASSERT(MUTEX_HELD(&vmu_data.vmu_lock)); 16923247Sgjelinek ASSERT(cache->vmc_refcnt > 0); 16933247Sgjelinek cache->vmc_refcnt--; 16943247Sgjelinek if (cache->vmc_refcnt == 0) { 16953247Sgjelinek kmem_free(cache->vmc_results, sizeof (vmusage_t) * 16963247Sgjelinek cache->vmc_nresults); 16973247Sgjelinek kmem_free(cache, sizeof (vmu_cache_t)); 16983247Sgjelinek } 16993247Sgjelinek } 17003247Sgjelinek 17013247Sgjelinek /* 17023247Sgjelinek * Copy out the cached results to a caller. Inspect the callers flags 17033247Sgjelinek * and zone to determine which cached results should be copied. 17043247Sgjelinek */ 17053247Sgjelinek static int 17063247Sgjelinek vmu_copyout_results(vmu_cache_t *cache, vmusage_t *buf, size_t *nres, 17073247Sgjelinek uint_t flags) 17083247Sgjelinek { 17093247Sgjelinek vmusage_t *result, *out_result; 17103247Sgjelinek vmusage_t dummy; 17113247Sgjelinek size_t i, count = 0; 17123247Sgjelinek size_t bufsize; 17133247Sgjelinek int ret = 0; 17143247Sgjelinek uint_t types = 0; 17153247Sgjelinek 17163247Sgjelinek if (nres != NULL) { 17173247Sgjelinek if (copyin((caddr_t)nres, &bufsize, sizeof (size_t))) 17183247Sgjelinek return (set_errno(EFAULT)); 17193247Sgjelinek } else { 17203247Sgjelinek bufsize = 0; 17213247Sgjelinek } 17223247Sgjelinek 17233247Sgjelinek /* figure out what results the caller is interested in. */ 17243247Sgjelinek if ((flags & VMUSAGE_SYSTEM) && curproc->p_zone == global_zone) 17253247Sgjelinek types |= VMUSAGE_SYSTEM; 17263247Sgjelinek if (flags & (VMUSAGE_ZONE | VMUSAGE_ALL_ZONES)) 17273247Sgjelinek types |= VMUSAGE_ZONE; 17283247Sgjelinek if (flags & (VMUSAGE_PROJECTS | VMUSAGE_ALL_PROJECTS | 17293247Sgjelinek VMUSAGE_COL_PROJECTS)) 17303247Sgjelinek types |= VMUSAGE_PROJECTS; 17313247Sgjelinek if (flags & (VMUSAGE_TASKS | VMUSAGE_ALL_TASKS)) 17323247Sgjelinek types |= VMUSAGE_TASKS; 17333247Sgjelinek if (flags & (VMUSAGE_RUSERS | VMUSAGE_ALL_RUSERS | VMUSAGE_COL_RUSERS)) 17343247Sgjelinek types |= VMUSAGE_RUSERS; 17353247Sgjelinek if (flags & (VMUSAGE_EUSERS | VMUSAGE_ALL_EUSERS | VMUSAGE_COL_EUSERS)) 17363247Sgjelinek types |= VMUSAGE_EUSERS; 17373247Sgjelinek 17383247Sgjelinek /* count results for current zone */ 17393247Sgjelinek out_result = buf; 17403247Sgjelinek for (result = cache->vmc_results, i = 0; 17413247Sgjelinek i < cache->vmc_nresults; result++, i++) { 17423247Sgjelinek 17433247Sgjelinek /* Do not return "other-zone" results to non-global zones */ 17443247Sgjelinek if (curproc->p_zone != global_zone && 17453247Sgjelinek curproc->p_zone->zone_id != result->vmu_zoneid) 17463247Sgjelinek continue; 17473247Sgjelinek 17483247Sgjelinek /* 17493247Sgjelinek * If non-global zone requests VMUSAGE_SYSTEM, fake 17503247Sgjelinek * up VMUSAGE_ZONE result as VMUSAGE_SYSTEM result. 17513247Sgjelinek */ 17523247Sgjelinek if (curproc->p_zone != global_zone && 17533247Sgjelinek (flags & VMUSAGE_SYSTEM) != 0 && 17543247Sgjelinek result->vmu_type == VMUSAGE_ZONE) { 17553247Sgjelinek count++; 17563247Sgjelinek if (out_result != NULL) { 17573247Sgjelinek if (bufsize < count) { 17583247Sgjelinek ret = set_errno(EOVERFLOW); 17593247Sgjelinek } else { 17603247Sgjelinek dummy = *result; 17613247Sgjelinek dummy.vmu_zoneid = ALL_ZONES; 17623247Sgjelinek dummy.vmu_id = 0; 17633247Sgjelinek dummy.vmu_type = VMUSAGE_SYSTEM; 17643247Sgjelinek if (copyout(&dummy, out_result, 17653247Sgjelinek sizeof (vmusage_t))) 17663247Sgjelinek return (set_errno( 17673247Sgjelinek EFAULT)); 17683247Sgjelinek out_result++; 17693247Sgjelinek } 17703247Sgjelinek } 17713247Sgjelinek } 17723247Sgjelinek 17733247Sgjelinek /* Skip results that do not match requested type */ 17743247Sgjelinek if ((result->vmu_type & types) == 0) 17753247Sgjelinek continue; 17763247Sgjelinek 17773247Sgjelinek /* Skip collated results if not requested */ 17783247Sgjelinek if (result->vmu_zoneid == ALL_ZONES) { 17793247Sgjelinek if (result->vmu_type == VMUSAGE_PROJECTS && 17803247Sgjelinek (flags & VMUSAGE_COL_PROJECTS) == 0) 17813247Sgjelinek continue; 17823247Sgjelinek if (result->vmu_type == VMUSAGE_EUSERS && 17833247Sgjelinek (flags & VMUSAGE_COL_EUSERS) == 0) 17843247Sgjelinek continue; 17853247Sgjelinek if (result->vmu_type == VMUSAGE_RUSERS && 17863247Sgjelinek (flags & VMUSAGE_COL_RUSERS) == 0) 17873247Sgjelinek continue; 17883247Sgjelinek } 17893247Sgjelinek 17903247Sgjelinek /* Skip "other zone" results if not requested */ 17913247Sgjelinek if (result->vmu_zoneid != curproc->p_zone->zone_id) { 17923247Sgjelinek if (result->vmu_type == VMUSAGE_ZONE && 17933247Sgjelinek (flags & VMUSAGE_ALL_ZONES) == 0) 17943247Sgjelinek continue; 17953247Sgjelinek if (result->vmu_type == VMUSAGE_PROJECTS && 17963247Sgjelinek (flags & (VMUSAGE_ALL_PROJECTS | 17973247Sgjelinek VMUSAGE_COL_PROJECTS)) == 0) 17983247Sgjelinek continue; 17993247Sgjelinek if (result->vmu_type == VMUSAGE_TASKS && 18003247Sgjelinek (flags & VMUSAGE_ALL_TASKS) == 0) 18013247Sgjelinek continue; 18023247Sgjelinek if (result->vmu_type == VMUSAGE_RUSERS && 18033247Sgjelinek (flags & (VMUSAGE_ALL_RUSERS | 18043247Sgjelinek VMUSAGE_COL_RUSERS)) == 0) 18053247Sgjelinek continue; 18063247Sgjelinek if (result->vmu_type == VMUSAGE_EUSERS && 18073247Sgjelinek (flags & (VMUSAGE_ALL_EUSERS | 18083247Sgjelinek VMUSAGE_COL_EUSERS)) == 0) 18093247Sgjelinek continue; 18103247Sgjelinek } 18113247Sgjelinek count++; 18123247Sgjelinek if (out_result != NULL) { 18133247Sgjelinek if (bufsize < count) { 18143247Sgjelinek ret = set_errno(EOVERFLOW); 18153247Sgjelinek } else { 18163247Sgjelinek if (copyout(result, out_result, 18173247Sgjelinek sizeof (vmusage_t))) 18183247Sgjelinek return (set_errno(EFAULT)); 18193247Sgjelinek out_result++; 18203247Sgjelinek } 18213247Sgjelinek } 18223247Sgjelinek } 18233247Sgjelinek if (nres != NULL) 18243247Sgjelinek if (copyout(&count, (void *)nres, sizeof (size_t))) 18253247Sgjelinek return (set_errno(EFAULT)); 18263247Sgjelinek 18273247Sgjelinek return (ret); 18283247Sgjelinek } 18293247Sgjelinek 18303247Sgjelinek /* 18313247Sgjelinek * vm_getusage() 18323247Sgjelinek * 18333247Sgjelinek * Counts rss and swap by zone, project, task, and/or user. The flags argument 18343247Sgjelinek * determines the type of results structures returned. Flags requesting 18353247Sgjelinek * results from more than one zone are "flattened" to the local zone if the 18363247Sgjelinek * caller is not the global zone. 18373247Sgjelinek * 18383247Sgjelinek * args: 18393247Sgjelinek * flags: bitmap consisting of one or more of VMUSAGE_*. 18403247Sgjelinek * age: maximum allowable age (time since counting was done) in 18413247Sgjelinek * seconds of the results. Results from previous callers are 18423247Sgjelinek * cached in kernel. 18433247Sgjelinek * buf: pointer to buffer array of vmusage_t. If NULL, then only nres 18443247Sgjelinek * set on success. 18453247Sgjelinek * nres: Set to number of vmusage_t structures pointed to by buf 18463247Sgjelinek * before calling vm_getusage(). 18473247Sgjelinek * On return 0 (success) or ENOSPC, is set to the number of result 18483247Sgjelinek * structures returned or attempted to return. 18493247Sgjelinek * 18503247Sgjelinek * returns 0 on success, -1 on failure: 18513247Sgjelinek * EINTR (interrupted) 18523247Sgjelinek * ENOSPC (nres to small for results, nres set to needed value for success) 18533247Sgjelinek * EINVAL (flags invalid) 18543247Sgjelinek * EFAULT (bad address for buf or nres) 18553247Sgjelinek */ 18563247Sgjelinek int 18573247Sgjelinek vm_getusage(uint_t flags, time_t age, vmusage_t *buf, size_t *nres) 18583247Sgjelinek { 18593247Sgjelinek vmu_entity_t *entity; 18603247Sgjelinek vmusage_t *result; 18613247Sgjelinek int ret = 0; 18623247Sgjelinek int cacherecent = 0; 18633247Sgjelinek hrtime_t now; 18643247Sgjelinek uint_t flags_orig; 18653247Sgjelinek 18663247Sgjelinek /* 18673247Sgjelinek * Non-global zones cannot request system wide and/or collated 18683247Sgjelinek * results, or the system result, so munge the flags accordingly. 18693247Sgjelinek */ 18703247Sgjelinek flags_orig = flags; 18713247Sgjelinek if (curproc->p_zone != global_zone) { 18723247Sgjelinek if (flags & (VMUSAGE_ALL_PROJECTS | VMUSAGE_COL_PROJECTS)) { 18733247Sgjelinek flags &= ~(VMUSAGE_ALL_PROJECTS | VMUSAGE_COL_PROJECTS); 18743247Sgjelinek flags |= VMUSAGE_PROJECTS; 18753247Sgjelinek } 18763247Sgjelinek if (flags & (VMUSAGE_ALL_RUSERS | VMUSAGE_COL_RUSERS)) { 18773247Sgjelinek flags &= ~(VMUSAGE_ALL_RUSERS | VMUSAGE_COL_RUSERS); 18783247Sgjelinek flags |= VMUSAGE_RUSERS; 18793247Sgjelinek } 18803247Sgjelinek if (flags & (VMUSAGE_ALL_EUSERS | VMUSAGE_COL_EUSERS)) { 18813247Sgjelinek flags &= ~(VMUSAGE_ALL_EUSERS | VMUSAGE_COL_EUSERS); 18823247Sgjelinek flags |= VMUSAGE_EUSERS; 18833247Sgjelinek } 18843247Sgjelinek if (flags & VMUSAGE_SYSTEM) { 18853247Sgjelinek flags &= ~VMUSAGE_SYSTEM; 18863247Sgjelinek flags |= VMUSAGE_ZONE; 18873247Sgjelinek } 18883247Sgjelinek } 18893247Sgjelinek 18903247Sgjelinek /* Check for unknown flags */ 18913247Sgjelinek if ((flags & (~VMUSAGE_MASK)) != 0) 18923247Sgjelinek return (set_errno(EINVAL)); 18933247Sgjelinek 18943247Sgjelinek /* Check for no flags */ 18953247Sgjelinek if ((flags & VMUSAGE_MASK) == 0) 18963247Sgjelinek return (set_errno(EINVAL)); 18973247Sgjelinek 18983247Sgjelinek mutex_enter(&vmu_data.vmu_lock); 18993247Sgjelinek now = gethrtime(); 19003247Sgjelinek 19013247Sgjelinek start: 19023247Sgjelinek if (vmu_data.vmu_cache != NULL) { 19033247Sgjelinek 19043247Sgjelinek vmu_cache_t *cache; 19053247Sgjelinek 19063247Sgjelinek if ((vmu_data.vmu_cache->vmc_timestamp + 19073247Sgjelinek ((hrtime_t)age * NANOSEC)) > now) 19083247Sgjelinek cacherecent = 1; 19093247Sgjelinek 19103247Sgjelinek if ((vmu_data.vmu_cache->vmc_flags & flags) == flags && 19113247Sgjelinek cacherecent == 1) { 19123247Sgjelinek cache = vmu_data.vmu_cache; 19133247Sgjelinek vmu_cache_hold(cache); 19143247Sgjelinek mutex_exit(&vmu_data.vmu_lock); 19153247Sgjelinek 19163247Sgjelinek ret = vmu_copyout_results(cache, buf, nres, flags_orig); 19173247Sgjelinek mutex_enter(&vmu_data.vmu_lock); 19183247Sgjelinek vmu_cache_rele(cache); 19193247Sgjelinek if (vmu_data.vmu_pending_waiters > 0) 19203247Sgjelinek cv_broadcast(&vmu_data.vmu_cv); 19213247Sgjelinek mutex_exit(&vmu_data.vmu_lock); 19223247Sgjelinek return (ret); 19233247Sgjelinek } 19243247Sgjelinek /* 19253247Sgjelinek * If the cache is recent, it is likely that there are other 19263247Sgjelinek * consumers of vm_getusage running, so add their flags to the 19273247Sgjelinek * desired flags for the calculation. 19283247Sgjelinek */ 19293247Sgjelinek if (cacherecent == 1) 19303247Sgjelinek flags = vmu_data.vmu_cache->vmc_flags | flags; 19313247Sgjelinek } 19323247Sgjelinek if (vmu_data.vmu_calc_thread == NULL) { 19333247Sgjelinek 19343247Sgjelinek vmu_cache_t *cache; 19353247Sgjelinek 19363247Sgjelinek vmu_data.vmu_calc_thread = curthread; 19373247Sgjelinek vmu_data.vmu_calc_flags = flags; 19383247Sgjelinek vmu_data.vmu_entities = NULL; 19393247Sgjelinek vmu_data.vmu_nentities = 0; 19403247Sgjelinek if (vmu_data.vmu_pending_waiters > 0) 19413247Sgjelinek vmu_data.vmu_calc_flags |= 19423247Sgjelinek vmu_data.vmu_pending_flags; 19433247Sgjelinek 19443247Sgjelinek vmu_data.vmu_pending_flags = 0; 19453247Sgjelinek mutex_exit(&vmu_data.vmu_lock); 19463247Sgjelinek vmu_calculate(); 19473247Sgjelinek mutex_enter(&vmu_data.vmu_lock); 19483247Sgjelinek /* copy results to cache */ 19493247Sgjelinek if (vmu_data.vmu_cache != NULL) 19503247Sgjelinek vmu_cache_rele(vmu_data.vmu_cache); 19513247Sgjelinek cache = vmu_data.vmu_cache = 19523247Sgjelinek vmu_cache_alloc(vmu_data.vmu_nentities, 19533247Sgjelinek vmu_data.vmu_calc_flags); 19543247Sgjelinek 19553247Sgjelinek result = cache->vmc_results; 19563247Sgjelinek for (entity = vmu_data.vmu_entities; entity != NULL; 19573247Sgjelinek entity = entity->vme_next) { 19583247Sgjelinek *result = entity->vme_result; 19593247Sgjelinek result++; 19603247Sgjelinek } 19613247Sgjelinek cache->vmc_timestamp = gethrtime(); 19623247Sgjelinek vmu_cache_hold(cache); 19633247Sgjelinek 19643247Sgjelinek vmu_data.vmu_calc_flags = 0; 19653247Sgjelinek vmu_data.vmu_calc_thread = NULL; 19663247Sgjelinek 19673247Sgjelinek if (vmu_data.vmu_pending_waiters > 0) 19683247Sgjelinek cv_broadcast(&vmu_data.vmu_cv); 19693247Sgjelinek 19703247Sgjelinek mutex_exit(&vmu_data.vmu_lock); 19713247Sgjelinek 19723247Sgjelinek /* copy cache */ 19733247Sgjelinek ret = vmu_copyout_results(cache, buf, nres, flags_orig); 19743247Sgjelinek mutex_enter(&vmu_data.vmu_lock); 19753247Sgjelinek vmu_cache_rele(cache); 19763247Sgjelinek mutex_exit(&vmu_data.vmu_lock); 19773247Sgjelinek 19783247Sgjelinek return (ret); 19793247Sgjelinek } 19803247Sgjelinek vmu_data.vmu_pending_flags |= flags; 19813247Sgjelinek vmu_data.vmu_pending_waiters++; 19823247Sgjelinek while (vmu_data.vmu_calc_thread != NULL) { 19833247Sgjelinek if (cv_wait_sig(&vmu_data.vmu_cv, 19843247Sgjelinek &vmu_data.vmu_lock) == 0) { 19853247Sgjelinek vmu_data.vmu_pending_waiters--; 19863247Sgjelinek mutex_exit(&vmu_data.vmu_lock); 19873247Sgjelinek return (set_errno(EINTR)); 19883247Sgjelinek } 19893247Sgjelinek } 19903247Sgjelinek vmu_data.vmu_pending_waiters--; 19913247Sgjelinek goto start; 19923247Sgjelinek } 1993