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 /* 23*3671Ssl108498 * 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 776*3671Ssl108498 /* 777*3671Ssl108498 * Verify first and last bound are covered by new bounds if they 778*3671Ssl108498 * have unknown type. 779*3671Ssl108498 */ 780*3671Ssl108498 ASSERT((*first)->vmb_type != VMUSAGE_BOUND_UNKNOWN || 781*3671Ssl108498 (*first)->vmb_start >= new_next->vmb_start); 782*3671Ssl108498 ASSERT((*last)->vmb_type != VMUSAGE_BOUND_UNKNOWN || 783*3671Ssl108498 (*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 } 11133247Sgjelinek if (svd->amp != NULL && svd->type == MAP_PRIVATE) { 11143247Sgjelinek private_amp = svd->amp; 11153247Sgjelinek p_start = svd->anon_index; 11163247Sgjelinek p_end = svd->anon_index + btop(seg->s_size) - 1; 11173247Sgjelinek } 11183247Sgjelinek } else if (seg->s_ops == &segspt_shmops) { 11193247Sgjelinek shared = B_TRUE; 11203247Sgjelinek shmd = (struct shm_data *)seg->s_data; 11213247Sgjelinek shared_object = vmu_find_insert_object( 11223247Sgjelinek vmu_data.vmu_all_amps_hash, (caddr_t)shmd->shm_amp, 11233247Sgjelinek VMUSAGE_TYPE_AMP); 11243247Sgjelinek s_start = 0; 11253247Sgjelinek s_end = btop(seg->s_size) - 1; 11263247Sgjelinek sptd = shmd->shm_sptseg->s_data; 11273247Sgjelinek 11283247Sgjelinek /* ism segments are always incore and do not reserve swap */ 11293247Sgjelinek if (sptd->spt_flags & SHM_SHARE_MMU) 11303247Sgjelinek incore = B_TRUE; 11313247Sgjelinek 11323247Sgjelinek } else { 11333247Sgjelinek return; 11343247Sgjelinek } 11353247Sgjelinek 11363247Sgjelinek /* 11373247Sgjelinek * If there is a private amp, count anon pages that exist. If an 11383247Sgjelinek * anon has a refcnt > 1 (cow sharing), then save the anon in a 11393247Sgjelinek * hash so that it is not double counted. 11403247Sgjelinek * 11413247Sgjelinek * If there is also a shared object, they figure out the bounds 11423247Sgjelinek * which are not mapped by the private amp. 11433247Sgjelinek */ 11443247Sgjelinek if (private_amp != NULL) { 11453247Sgjelinek 11463247Sgjelinek /* Enter as writer to prevent cow anons from being freed */ 11473247Sgjelinek ANON_LOCK_ENTER(&private_amp->a_rwlock, RW_WRITER); 11483247Sgjelinek 11493247Sgjelinek p_index = p_start; 11503247Sgjelinek s_index = s_start; 11513247Sgjelinek 11523247Sgjelinek while (p_index <= p_end) { 11533247Sgjelinek 11543247Sgjelinek pgcnt_t p_index_next; 11553247Sgjelinek pgcnt_t p_bound_size; 11563247Sgjelinek int cnt; 11573247Sgjelinek anoff_t off; 11583247Sgjelinek struct vnode *vn; 11593247Sgjelinek struct anon *ap; 11603247Sgjelinek page_t *page; /* For handling of large */ 11613247Sgjelinek pgcnt_t pgcnt = 1; /* pages */ 11623247Sgjelinek pgcnt_t pgstart; 11633247Sgjelinek pgcnt_t pgend; 11643247Sgjelinek uint_t pgshft; 11653247Sgjelinek pgcnt_t pgmsk; 11663247Sgjelinek 11673247Sgjelinek p_index_next = p_index; 11683247Sgjelinek ap = anon_get_next_ptr(private_amp->ahp, 11693247Sgjelinek &p_index_next); 11703247Sgjelinek 11713247Sgjelinek /* 11723247Sgjelinek * If next anon is past end of mapping, simulate 11733247Sgjelinek * end of anon so loop terminates. 11743247Sgjelinek */ 11753247Sgjelinek if (p_index_next > p_end) { 11763247Sgjelinek p_index_next = p_end + 1; 11773247Sgjelinek ap = NULL; 11783247Sgjelinek } 11793247Sgjelinek /* 11803247Sgjelinek * For cow segments, keep track of bounds not 11813247Sgjelinek * backed by private amp so they can be looked 11823247Sgjelinek * up in the backing vnode 11833247Sgjelinek */ 11843247Sgjelinek if (p_index_next != p_index) { 11853247Sgjelinek 11863247Sgjelinek /* 11873247Sgjelinek * Compute index difference between anon and 11883247Sgjelinek * previous anon. 11893247Sgjelinek */ 11903247Sgjelinek p_bound_size = p_index_next - p_index - 1; 11913247Sgjelinek 11923247Sgjelinek if (shared_object != NULL) { 11933247Sgjelinek cur = vmu_alloc_bound(); 11943247Sgjelinek cur->vmb_next = NULL; 11953247Sgjelinek cur->vmb_start = s_index; 11963247Sgjelinek cur->vmb_end = s_index + p_bound_size; 11973247Sgjelinek cur->vmb_type = VMUSAGE_BOUND_UNKNOWN; 11983247Sgjelinek if (first == NULL) { 11993247Sgjelinek first = cur; 12003247Sgjelinek last = cur; 12013247Sgjelinek } else { 12023247Sgjelinek last->vmb_next = cur; 12033247Sgjelinek last = cur; 12043247Sgjelinek } 12053247Sgjelinek } 12063247Sgjelinek p_index = p_index + p_bound_size + 1; 12073247Sgjelinek s_index = s_index + p_bound_size + 1; 12083247Sgjelinek } 12093247Sgjelinek 12103247Sgjelinek /* Detect end of anons in amp */ 12113247Sgjelinek if (ap == NULL) 12123247Sgjelinek break; 12133247Sgjelinek 12143247Sgjelinek cnt = ap->an_refcnt; 12153247Sgjelinek swap_xlate(ap, &vn, &off); 12163247Sgjelinek 12173247Sgjelinek if (vn == NULL || vn->v_pages == NULL || 12183247Sgjelinek (page = page_exists(vn, off)) == NULL) { 12193247Sgjelinek p_index++; 12203247Sgjelinek s_index++; 12213247Sgjelinek continue; 12223247Sgjelinek } 12233247Sgjelinek 12243247Sgjelinek /* 12253247Sgjelinek * If large page is found, compute portion of large 12263247Sgjelinek * page in mapping, and increment indicies to the next 12273247Sgjelinek * large page. 12283247Sgjelinek */ 12293247Sgjelinek if (page->p_szc > 0) { 12303247Sgjelinek 12313247Sgjelinek pgcnt = page_get_pagecnt(page->p_szc); 12323247Sgjelinek pgshft = page_get_shift(page->p_szc); 12333247Sgjelinek pgmsk = (0x1 << (pgshft - PAGESHIFT)) - 1; 12343247Sgjelinek 12353247Sgjelinek /* First page in large page */ 12363247Sgjelinek pgstart = p_index & ~pgmsk; 12373247Sgjelinek /* Last page in large page */ 12383247Sgjelinek pgend = pgstart + pgcnt - 1; 12393247Sgjelinek /* 12403247Sgjelinek * Artifically end page if page extends past 12413247Sgjelinek * end of mapping. 12423247Sgjelinek */ 12433247Sgjelinek if (pgend > p_end) 12443247Sgjelinek pgend = p_end; 12453247Sgjelinek 12463247Sgjelinek /* 12473247Sgjelinek * Compute number of pages from large page 12483247Sgjelinek * which are mapped. 12493247Sgjelinek */ 12503247Sgjelinek pgcnt = pgend - p_index + 1; 12513247Sgjelinek 12523247Sgjelinek /* 12533247Sgjelinek * Point indicies at page after large page, 12543247Sgjelinek * or at page after end of mapping. 12553247Sgjelinek */ 12563247Sgjelinek p_index += pgcnt; 12573247Sgjelinek s_index += pgcnt; 12583247Sgjelinek } else { 12593247Sgjelinek p_index++; 12603247Sgjelinek s_index++; 12613247Sgjelinek } 12623247Sgjelinek 12633247Sgjelinek /* 12643247Sgjelinek * Assume anon structs with a refcnt 12653247Sgjelinek * of 1 are not cow shared, so there 12663247Sgjelinek * is no reason to track them per entity. 12673247Sgjelinek */ 12683247Sgjelinek if (cnt == 1) { 12693247Sgjelinek panon += pgcnt; 12703247Sgjelinek continue; 12713247Sgjelinek } 12723247Sgjelinek for (entity = vmu_entities; entity != NULL; 12733247Sgjelinek entity = entity->vme_next_calc) { 12743247Sgjelinek 12753247Sgjelinek result = &entity->vme_result; 12763247Sgjelinek /* 12773247Sgjelinek * Track cow anons per entity so 12783247Sgjelinek * they are not double counted. 12793247Sgjelinek */ 12803247Sgjelinek if (vmu_find_insert_anon(entity->vme_anon_hash, 12813247Sgjelinek (caddr_t)ap) == 0) 12823247Sgjelinek continue; 12833247Sgjelinek 12843247Sgjelinek result->vmu_rss_all += (pgcnt << PAGESHIFT); 12853247Sgjelinek result->vmu_rss_private += 12863247Sgjelinek (pgcnt << PAGESHIFT); 12873247Sgjelinek } 12883247Sgjelinek } 12893247Sgjelinek ANON_LOCK_EXIT(&private_amp->a_rwlock); 12903247Sgjelinek } 12913247Sgjelinek 12923247Sgjelinek /* Add up resident anon and swap reserved for private mappings */ 12933247Sgjelinek if (swresv > 0 || panon > 0) { 12943247Sgjelinek for (entity = vmu_entities; entity != NULL; 12953247Sgjelinek entity = entity->vme_next_calc) { 12963247Sgjelinek result = &entity->vme_result; 12973247Sgjelinek result->vmu_swap_all += swresv; 12983247Sgjelinek result->vmu_swap_private += swresv; 12993247Sgjelinek result->vmu_rss_all += (panon << PAGESHIFT); 13003247Sgjelinek result->vmu_rss_private += (panon << PAGESHIFT); 13013247Sgjelinek } 13023247Sgjelinek } 13033247Sgjelinek 13043247Sgjelinek /* Compute resident pages backing shared amp or named vnode */ 13053247Sgjelinek if (shared_object != NULL) { 13063247Sgjelinek if (first == NULL) { 13073247Sgjelinek /* 13083247Sgjelinek * No private amp, or private amp has no anon 13093247Sgjelinek * structs. This means entire segment is backed by 13103247Sgjelinek * the shared object. 13113247Sgjelinek */ 13123247Sgjelinek first = vmu_alloc_bound(); 13133247Sgjelinek first->vmb_next = NULL; 13143247Sgjelinek first->vmb_start = s_start; 13153247Sgjelinek first->vmb_end = s_end; 13163247Sgjelinek first->vmb_type = VMUSAGE_BOUND_UNKNOWN; 13173247Sgjelinek } 13183247Sgjelinek /* 13193247Sgjelinek * Iterate bounds not backed by private amp, and compute 13203247Sgjelinek * resident pages. 13213247Sgjelinek */ 13223247Sgjelinek cur = first; 13233247Sgjelinek while (cur != NULL) { 13243247Sgjelinek 13253247Sgjelinek if (vmu_insert_lookup_object_bounds(shared_object, 13263247Sgjelinek cur->vmb_start, cur->vmb_end, VMUSAGE_BOUND_UNKNOWN, 13273247Sgjelinek &first, &last) > 0) { 13283247Sgjelinek /* new bounds, find incore/not-incore */ 13293247Sgjelinek if (shared_object->vmo_type == 13303247Sgjelinek VMUSAGE_TYPE_VNODE) 13313247Sgjelinek vmu_vnode_update_incore_bounds( 13323247Sgjelinek (vnode_t *) 13333247Sgjelinek shared_object->vmo_key, &first, 13343247Sgjelinek &last); 13353247Sgjelinek else 13363247Sgjelinek vmu_amp_update_incore_bounds( 13373247Sgjelinek (struct anon_map *) 13383247Sgjelinek shared_object->vmo_key, &first, 13393247Sgjelinek &last, incore); 13403247Sgjelinek vmu_merge_bounds(&first, &last); 13413247Sgjelinek } 13423247Sgjelinek for (entity = vmu_entities; entity != NULL; 13433247Sgjelinek entity = entity->vme_next_calc) { 13443247Sgjelinek 13453247Sgjelinek result = &entity->vme_result; 13463247Sgjelinek 13473247Sgjelinek entity_object = vmu_find_insert_object( 13483247Sgjelinek shared_object->vmo_type == 13493247Sgjelinek VMUSAGE_TYPE_VNODE ? entity->vme_vnode_hash: 13503247Sgjelinek entity->vme_amp_hash, 13513247Sgjelinek shared_object->vmo_key, 13523247Sgjelinek shared_object->vmo_type); 13533247Sgjelinek 13543247Sgjelinek virt = vmu_insert_lookup_object_bounds( 13553247Sgjelinek entity_object, cur->vmb_start, cur->vmb_end, 13563247Sgjelinek VMUSAGE_BOUND_UNKNOWN, &e_first, &e_last); 13573247Sgjelinek 13583247Sgjelinek if (virt == 0) 13593247Sgjelinek continue; 13603247Sgjelinek /* 13613247Sgjelinek * Range visited for this entity 13623247Sgjelinek */ 13633247Sgjelinek rss = vmu_update_bounds(&e_first, 13643247Sgjelinek &e_last, first, last); 13653247Sgjelinek result->vmu_rss_all += (rss << PAGESHIFT); 13663247Sgjelinek if (shared == B_TRUE && file == B_FALSE) { 13673247Sgjelinek /* shared anon mapping */ 13683247Sgjelinek result->vmu_swap_all += 13693247Sgjelinek (virt << PAGESHIFT); 13703247Sgjelinek result->vmu_swap_shared += 13713247Sgjelinek (virt << PAGESHIFT); 13723247Sgjelinek result->vmu_rss_shared += 13733247Sgjelinek (rss << PAGESHIFT); 13743247Sgjelinek } else if (shared == B_TRUE && file == B_TRUE) { 13753247Sgjelinek /* shared file mapping */ 13763247Sgjelinek result->vmu_rss_shared += 13773247Sgjelinek (rss << PAGESHIFT); 13783247Sgjelinek } else if (shared == B_FALSE && 13793247Sgjelinek file == B_TRUE) { 13803247Sgjelinek /* private file mapping */ 13813247Sgjelinek result->vmu_rss_private += 13823247Sgjelinek (rss << PAGESHIFT); 13833247Sgjelinek } 13843247Sgjelinek vmu_merge_bounds(&e_first, &e_last); 13853247Sgjelinek } 13863247Sgjelinek tmp = cur; 13873247Sgjelinek cur = cur->vmb_next; 13883247Sgjelinek vmu_free_bound(tmp); 13893247Sgjelinek } 13903247Sgjelinek } 13913247Sgjelinek } 13923247Sgjelinek 13933247Sgjelinek /* 13943247Sgjelinek * Based on the current calculation flags, find the relevant entities 13953247Sgjelinek * which are relative to the process. Then calculate each segment 13963247Sgjelinek * in the process'es address space for each relevant entity. 13973247Sgjelinek */ 13983247Sgjelinek static void 13993247Sgjelinek vmu_calculate_proc(proc_t *p) 14003247Sgjelinek { 14013247Sgjelinek vmu_entity_t *entities = NULL; 14023247Sgjelinek vmu_zone_t *zone; 14033247Sgjelinek vmu_entity_t *tmp; 14043247Sgjelinek struct as *as; 14053247Sgjelinek struct seg *seg; 14063247Sgjelinek int ret; 14073247Sgjelinek 14083247Sgjelinek /* Figure out which entities are being computed */ 14093247Sgjelinek if ((vmu_data.vmu_system) != NULL) { 14103247Sgjelinek tmp = vmu_data.vmu_system; 14113247Sgjelinek tmp->vme_next_calc = entities; 14123247Sgjelinek entities = tmp; 14133247Sgjelinek } 14143247Sgjelinek if (vmu_data.vmu_calc_flags & 14153247Sgjelinek (VMUSAGE_ZONE | VMUSAGE_ALL_ZONES | VMUSAGE_PROJECTS | 14163247Sgjelinek VMUSAGE_ALL_PROJECTS | VMUSAGE_TASKS | VMUSAGE_ALL_TASKS | 14173247Sgjelinek VMUSAGE_RUSERS | VMUSAGE_ALL_RUSERS | VMUSAGE_EUSERS | 14183247Sgjelinek VMUSAGE_ALL_EUSERS)) { 14193247Sgjelinek ret = i_mod_hash_find_nosync(vmu_data.vmu_zones_hash, 14203247Sgjelinek (mod_hash_key_t)(uintptr_t)p->p_zone->zone_id, 14213247Sgjelinek (mod_hash_val_t *)&zone); 14223247Sgjelinek if (ret != 0) { 14233247Sgjelinek zone = vmu_alloc_zone(p->p_zone->zone_id); 14243247Sgjelinek ret = i_mod_hash_insert_nosync(vmu_data.vmu_zones_hash, 14253247Sgjelinek (mod_hash_key_t)(uintptr_t)p->p_zone->zone_id, 14263247Sgjelinek (mod_hash_val_t)zone, (mod_hash_hndl_t)0); 14273247Sgjelinek ASSERT(ret == 0); 14283247Sgjelinek } 14293247Sgjelinek if (zone->vmz_zone != NULL) { 14303247Sgjelinek tmp = zone->vmz_zone; 14313247Sgjelinek tmp->vme_next_calc = entities; 14323247Sgjelinek entities = tmp; 14333247Sgjelinek } 14343247Sgjelinek if (vmu_data.vmu_calc_flags & 14353247Sgjelinek (VMUSAGE_PROJECTS | VMUSAGE_ALL_PROJECTS)) { 14363247Sgjelinek tmp = vmu_find_insert_entity(zone->vmz_projects_hash, 14373247Sgjelinek p->p_task->tk_proj->kpj_id, VMUSAGE_PROJECTS, 14383247Sgjelinek zone->vmz_id); 14393247Sgjelinek tmp->vme_next_calc = entities; 14403247Sgjelinek entities = tmp; 14413247Sgjelinek } 14423247Sgjelinek if (vmu_data.vmu_calc_flags & 14433247Sgjelinek (VMUSAGE_TASKS | VMUSAGE_ALL_TASKS)) { 14443247Sgjelinek tmp = vmu_find_insert_entity(zone->vmz_tasks_hash, 14453247Sgjelinek p->p_task->tk_tkid, VMUSAGE_TASKS, zone->vmz_id); 14463247Sgjelinek tmp->vme_next_calc = entities; 14473247Sgjelinek entities = tmp; 14483247Sgjelinek } 14493247Sgjelinek if (vmu_data.vmu_calc_flags & 14503247Sgjelinek (VMUSAGE_RUSERS | VMUSAGE_ALL_RUSERS)) { 14513247Sgjelinek tmp = vmu_find_insert_entity(zone->vmz_rusers_hash, 14523247Sgjelinek crgetruid(p->p_cred), VMUSAGE_RUSERS, zone->vmz_id); 14533247Sgjelinek tmp->vme_next_calc = entities; 14543247Sgjelinek entities = tmp; 14553247Sgjelinek } 14563247Sgjelinek if (vmu_data.vmu_calc_flags & 14573247Sgjelinek (VMUSAGE_EUSERS | VMUSAGE_ALL_EUSERS)) { 14583247Sgjelinek tmp = vmu_find_insert_entity(zone->vmz_eusers_hash, 14593247Sgjelinek crgetuid(p->p_cred), VMUSAGE_EUSERS, zone->vmz_id); 14603247Sgjelinek tmp->vme_next_calc = entities; 14613247Sgjelinek entities = tmp; 14623247Sgjelinek } 14633247Sgjelinek } 14643247Sgjelinek /* Entities which collapse projects and users for all zones */ 14653247Sgjelinek if (vmu_data.vmu_calc_flags & VMUSAGE_COL_PROJECTS) { 14663247Sgjelinek tmp = vmu_find_insert_entity(vmu_data.vmu_projects_col_hash, 14673247Sgjelinek p->p_task->tk_proj->kpj_id, VMUSAGE_PROJECTS, ALL_ZONES); 14683247Sgjelinek tmp->vme_next_calc = entities; 14693247Sgjelinek entities = tmp; 14703247Sgjelinek } 14713247Sgjelinek if (vmu_data.vmu_calc_flags & VMUSAGE_COL_RUSERS) { 14723247Sgjelinek tmp = vmu_find_insert_entity(vmu_data.vmu_rusers_col_hash, 14733247Sgjelinek crgetruid(p->p_cred), VMUSAGE_RUSERS, ALL_ZONES); 14743247Sgjelinek tmp->vme_next_calc = entities; 14753247Sgjelinek entities = tmp; 14763247Sgjelinek } 14773247Sgjelinek if (vmu_data.vmu_calc_flags & VMUSAGE_COL_EUSERS) { 14783247Sgjelinek tmp = vmu_find_insert_entity(vmu_data.vmu_eusers_col_hash, 14793247Sgjelinek crgetuid(p->p_cred), VMUSAGE_EUSERS, ALL_ZONES); 14803247Sgjelinek tmp->vme_next_calc = entities; 14813247Sgjelinek entities = tmp; 14823247Sgjelinek } 14833247Sgjelinek 14843247Sgjelinek ASSERT(entities != NULL); 14853247Sgjelinek /* process all segs in process's address space */ 14863247Sgjelinek as = p->p_as; 14873247Sgjelinek AS_LOCK_ENTER(as, &as->a_lock, RW_READER); 14883247Sgjelinek for (seg = AS_SEGFIRST(as); seg != NULL; 14893247Sgjelinek seg = AS_SEGNEXT(as, seg)) { 14903247Sgjelinek vmu_calculate_seg(entities, seg); 14913247Sgjelinek } 14923247Sgjelinek AS_LOCK_EXIT(as, &as->a_lock); 14933247Sgjelinek } 14943247Sgjelinek 14953247Sgjelinek /* 14963247Sgjelinek * Free data created by previous call to vmu_calculate(). 14973247Sgjelinek */ 14983247Sgjelinek static void 14993247Sgjelinek vmu_clear_calc() 15003247Sgjelinek { 15013247Sgjelinek if (vmu_data.vmu_system != NULL) 15023247Sgjelinek vmu_free_entity(vmu_data.vmu_system); 15033247Sgjelinek vmu_data.vmu_system = NULL; 15043247Sgjelinek if (vmu_data.vmu_zones_hash != NULL) 15053247Sgjelinek i_mod_hash_clear_nosync(vmu_data.vmu_zones_hash); 15063247Sgjelinek if (vmu_data.vmu_projects_col_hash != NULL) 15073247Sgjelinek i_mod_hash_clear_nosync(vmu_data.vmu_projects_col_hash); 15083247Sgjelinek if (vmu_data.vmu_rusers_col_hash != NULL) 15093247Sgjelinek i_mod_hash_clear_nosync(vmu_data.vmu_rusers_col_hash); 15103247Sgjelinek if (vmu_data.vmu_eusers_col_hash != NULL) 15113247Sgjelinek i_mod_hash_clear_nosync(vmu_data.vmu_eusers_col_hash); 15123247Sgjelinek 15133247Sgjelinek i_mod_hash_clear_nosync(vmu_data.vmu_all_vnodes_hash); 15143247Sgjelinek i_mod_hash_clear_nosync(vmu_data.vmu_all_amps_hash); 15153247Sgjelinek } 15163247Sgjelinek 15173247Sgjelinek /* 15183247Sgjelinek * Free unused data structures. These can result if the system workload 15193247Sgjelinek * decreases between calculations. 15203247Sgjelinek */ 15213247Sgjelinek static void 15223247Sgjelinek vmu_free_extra() 15233247Sgjelinek { 15243247Sgjelinek vmu_bound_t *tb; 15253247Sgjelinek vmu_object_t *to; 15263247Sgjelinek vmu_entity_t *te; 15273247Sgjelinek vmu_zone_t *tz; 15283247Sgjelinek 15293247Sgjelinek while (vmu_data.vmu_free_bounds != NULL) { 15303247Sgjelinek tb = vmu_data.vmu_free_bounds; 15313247Sgjelinek vmu_data.vmu_free_bounds = vmu_data.vmu_free_bounds->vmb_next; 15323247Sgjelinek kmem_cache_free(vmu_bound_cache, tb); 15333247Sgjelinek } 15343247Sgjelinek while (vmu_data.vmu_free_objects != NULL) { 15353247Sgjelinek to = vmu_data.vmu_free_objects; 15363247Sgjelinek vmu_data.vmu_free_objects = 15373247Sgjelinek vmu_data.vmu_free_objects->vmo_next; 15383247Sgjelinek kmem_cache_free(vmu_object_cache, to); 15393247Sgjelinek } 15403247Sgjelinek while (vmu_data.vmu_free_entities != NULL) { 15413247Sgjelinek te = vmu_data.vmu_free_entities; 15423247Sgjelinek vmu_data.vmu_free_entities = 15433247Sgjelinek vmu_data.vmu_free_entities->vme_next; 15443247Sgjelinek if (te->vme_vnode_hash != NULL) 15453247Sgjelinek mod_hash_destroy_hash(te->vme_vnode_hash); 15463247Sgjelinek if (te->vme_amp_hash != NULL) 15473247Sgjelinek mod_hash_destroy_hash(te->vme_amp_hash); 15483247Sgjelinek if (te->vme_anon_hash != NULL) 15493247Sgjelinek mod_hash_destroy_hash(te->vme_anon_hash); 15503247Sgjelinek kmem_free(te, sizeof (vmu_entity_t)); 15513247Sgjelinek } 15523247Sgjelinek while (vmu_data.vmu_free_zones != NULL) { 15533247Sgjelinek tz = vmu_data.vmu_free_zones; 15543247Sgjelinek vmu_data.vmu_free_zones = 15553247Sgjelinek vmu_data.vmu_free_zones->vmz_next; 15563247Sgjelinek if (tz->vmz_projects_hash != NULL) 15573247Sgjelinek mod_hash_destroy_hash(tz->vmz_projects_hash); 15583247Sgjelinek if (tz->vmz_tasks_hash != NULL) 15593247Sgjelinek mod_hash_destroy_hash(tz->vmz_tasks_hash); 15603247Sgjelinek if (tz->vmz_rusers_hash != NULL) 15613247Sgjelinek mod_hash_destroy_hash(tz->vmz_rusers_hash); 15623247Sgjelinek if (tz->vmz_eusers_hash != NULL) 15633247Sgjelinek mod_hash_destroy_hash(tz->vmz_eusers_hash); 15643247Sgjelinek kmem_free(tz, sizeof (vmu_zone_t)); 15653247Sgjelinek } 15663247Sgjelinek } 15673247Sgjelinek 15683247Sgjelinek extern kcondvar_t *pr_pid_cv; 15693247Sgjelinek 15703247Sgjelinek /* 15713247Sgjelinek * Determine which entity types are relevant and allocate the hashes to 15723247Sgjelinek * track them. Then walk the process table and count rss and swap 15733247Sgjelinek * for each process'es address space. Address space object such as 15743247Sgjelinek * vnodes, amps and anons are tracked per entity, so that they are 15753247Sgjelinek * not double counted in the results. 15763247Sgjelinek * 15773247Sgjelinek */ 15783247Sgjelinek static void 15793247Sgjelinek vmu_calculate() 15803247Sgjelinek { 15813247Sgjelinek int i = 0; 15823247Sgjelinek int ret; 15833247Sgjelinek proc_t *p; 15843247Sgjelinek 15853247Sgjelinek vmu_clear_calc(); 15863247Sgjelinek 15873247Sgjelinek if (vmu_data.vmu_calc_flags & VMUSAGE_SYSTEM) 15883247Sgjelinek vmu_data.vmu_system = vmu_alloc_entity(0, VMUSAGE_SYSTEM, 15893247Sgjelinek ALL_ZONES); 15903247Sgjelinek 15913247Sgjelinek /* 15923247Sgjelinek * Walk process table and calculate rss of each proc. 15933247Sgjelinek * 15943247Sgjelinek * Pidlock and p_lock cannot be held while doing the rss calculation. 15953247Sgjelinek * This is because: 15963247Sgjelinek * 1. The calculation allocates using KM_SLEEP. 15973247Sgjelinek * 2. The calculation grabs a_lock, which cannot be grabbed 15983247Sgjelinek * after p_lock. 15993247Sgjelinek * 16003247Sgjelinek * Since pidlock must be dropped, we cannot simply just walk the 16013247Sgjelinek * practive list. Instead, we walk the process table, and sprlock 16023247Sgjelinek * each process to ensure that it does not exit during the 16033247Sgjelinek * calculation. 16043247Sgjelinek */ 16053247Sgjelinek 16063247Sgjelinek mutex_enter(&pidlock); 16073247Sgjelinek for (i = 0; i < v.v_proc; i++) { 16083247Sgjelinek again: 16093247Sgjelinek p = pid_entry(i); 16103247Sgjelinek if (p == NULL) 16113247Sgjelinek continue; 16123247Sgjelinek 16133247Sgjelinek mutex_enter(&p->p_lock); 16143247Sgjelinek mutex_exit(&pidlock); 16153247Sgjelinek 16163247Sgjelinek if (panicstr) { 16173247Sgjelinek mutex_exit(&p->p_lock); 16183247Sgjelinek return; 16193247Sgjelinek } 16203247Sgjelinek 16213247Sgjelinek /* Try to set P_PR_LOCK */ 16223247Sgjelinek ret = sprtrylock_proc(p); 16233247Sgjelinek if (ret == -1) { 16243247Sgjelinek /* Process in invalid state */ 16253247Sgjelinek mutex_exit(&p->p_lock); 16263247Sgjelinek mutex_enter(&pidlock); 16273247Sgjelinek continue; 16283247Sgjelinek } else if (ret == 1) { 16293247Sgjelinek /* 16303247Sgjelinek * P_PR_LOCK is already set. Wait and try again. 16313247Sgjelinek * This also drops p_lock. 16323247Sgjelinek */ 16333247Sgjelinek sprwaitlock_proc(p); 16343247Sgjelinek mutex_enter(&pidlock); 16353247Sgjelinek goto again; 16363247Sgjelinek } 16373247Sgjelinek mutex_exit(&p->p_lock); 16383247Sgjelinek 16393247Sgjelinek vmu_calculate_proc(p); 16403247Sgjelinek 16413247Sgjelinek mutex_enter(&p->p_lock); 16423247Sgjelinek sprunlock(p); 16433247Sgjelinek mutex_enter(&pidlock); 16443247Sgjelinek } 16453247Sgjelinek mutex_exit(&pidlock); 16463247Sgjelinek 16473247Sgjelinek vmu_free_extra(); 16483247Sgjelinek } 16493247Sgjelinek 16503247Sgjelinek /* 16513247Sgjelinek * allocate a new cache for N results satisfying flags 16523247Sgjelinek */ 16533247Sgjelinek vmu_cache_t * 16543247Sgjelinek vmu_cache_alloc(size_t nres, uint_t flags) 16553247Sgjelinek { 16563247Sgjelinek vmu_cache_t *cache; 16573247Sgjelinek 16583247Sgjelinek cache = kmem_zalloc(sizeof (vmu_cache_t), KM_SLEEP); 16593247Sgjelinek cache->vmc_results = kmem_zalloc(sizeof (vmusage_t) * nres, KM_SLEEP); 16603247Sgjelinek cache->vmc_nresults = nres; 16613247Sgjelinek cache->vmc_flags = flags; 16623247Sgjelinek cache->vmc_refcnt = 1; 16633247Sgjelinek return (cache); 16643247Sgjelinek } 16653247Sgjelinek 16663247Sgjelinek /* 16673247Sgjelinek * Make sure cached results are not freed 16683247Sgjelinek */ 16693247Sgjelinek static void 16703247Sgjelinek vmu_cache_hold(vmu_cache_t *cache) 16713247Sgjelinek { 16723247Sgjelinek ASSERT(MUTEX_HELD(&vmu_data.vmu_lock)); 16733247Sgjelinek cache->vmc_refcnt++; 16743247Sgjelinek } 16753247Sgjelinek 16763247Sgjelinek /* 16773247Sgjelinek * free cache data 16783247Sgjelinek */ 16793247Sgjelinek static void 16803247Sgjelinek vmu_cache_rele(vmu_cache_t *cache) 16813247Sgjelinek { 16823247Sgjelinek ASSERT(MUTEX_HELD(&vmu_data.vmu_lock)); 16833247Sgjelinek ASSERT(cache->vmc_refcnt > 0); 16843247Sgjelinek cache->vmc_refcnt--; 16853247Sgjelinek if (cache->vmc_refcnt == 0) { 16863247Sgjelinek kmem_free(cache->vmc_results, sizeof (vmusage_t) * 16873247Sgjelinek cache->vmc_nresults); 16883247Sgjelinek kmem_free(cache, sizeof (vmu_cache_t)); 16893247Sgjelinek } 16903247Sgjelinek } 16913247Sgjelinek 16923247Sgjelinek /* 16933247Sgjelinek * Copy out the cached results to a caller. Inspect the callers flags 16943247Sgjelinek * and zone to determine which cached results should be copied. 16953247Sgjelinek */ 16963247Sgjelinek static int 16973247Sgjelinek vmu_copyout_results(vmu_cache_t *cache, vmusage_t *buf, size_t *nres, 16983247Sgjelinek uint_t flags) 16993247Sgjelinek { 17003247Sgjelinek vmusage_t *result, *out_result; 17013247Sgjelinek vmusage_t dummy; 17023247Sgjelinek size_t i, count = 0; 17033247Sgjelinek size_t bufsize; 17043247Sgjelinek int ret = 0; 17053247Sgjelinek uint_t types = 0; 17063247Sgjelinek 17073247Sgjelinek if (nres != NULL) { 17083247Sgjelinek if (copyin((caddr_t)nres, &bufsize, sizeof (size_t))) 17093247Sgjelinek return (set_errno(EFAULT)); 17103247Sgjelinek } else { 17113247Sgjelinek bufsize = 0; 17123247Sgjelinek } 17133247Sgjelinek 17143247Sgjelinek /* figure out what results the caller is interested in. */ 17153247Sgjelinek if ((flags & VMUSAGE_SYSTEM) && curproc->p_zone == global_zone) 17163247Sgjelinek types |= VMUSAGE_SYSTEM; 17173247Sgjelinek if (flags & (VMUSAGE_ZONE | VMUSAGE_ALL_ZONES)) 17183247Sgjelinek types |= VMUSAGE_ZONE; 17193247Sgjelinek if (flags & (VMUSAGE_PROJECTS | VMUSAGE_ALL_PROJECTS | 17203247Sgjelinek VMUSAGE_COL_PROJECTS)) 17213247Sgjelinek types |= VMUSAGE_PROJECTS; 17223247Sgjelinek if (flags & (VMUSAGE_TASKS | VMUSAGE_ALL_TASKS)) 17233247Sgjelinek types |= VMUSAGE_TASKS; 17243247Sgjelinek if (flags & (VMUSAGE_RUSERS | VMUSAGE_ALL_RUSERS | VMUSAGE_COL_RUSERS)) 17253247Sgjelinek types |= VMUSAGE_RUSERS; 17263247Sgjelinek if (flags & (VMUSAGE_EUSERS | VMUSAGE_ALL_EUSERS | VMUSAGE_COL_EUSERS)) 17273247Sgjelinek types |= VMUSAGE_EUSERS; 17283247Sgjelinek 17293247Sgjelinek /* count results for current zone */ 17303247Sgjelinek out_result = buf; 17313247Sgjelinek for (result = cache->vmc_results, i = 0; 17323247Sgjelinek i < cache->vmc_nresults; result++, i++) { 17333247Sgjelinek 17343247Sgjelinek /* Do not return "other-zone" results to non-global zones */ 17353247Sgjelinek if (curproc->p_zone != global_zone && 17363247Sgjelinek curproc->p_zone->zone_id != result->vmu_zoneid) 17373247Sgjelinek continue; 17383247Sgjelinek 17393247Sgjelinek /* 17403247Sgjelinek * If non-global zone requests VMUSAGE_SYSTEM, fake 17413247Sgjelinek * up VMUSAGE_ZONE result as VMUSAGE_SYSTEM result. 17423247Sgjelinek */ 17433247Sgjelinek if (curproc->p_zone != global_zone && 17443247Sgjelinek (flags & VMUSAGE_SYSTEM) != 0 && 17453247Sgjelinek result->vmu_type == VMUSAGE_ZONE) { 17463247Sgjelinek count++; 17473247Sgjelinek if (out_result != NULL) { 17483247Sgjelinek if (bufsize < count) { 17493247Sgjelinek ret = set_errno(EOVERFLOW); 17503247Sgjelinek } else { 17513247Sgjelinek dummy = *result; 17523247Sgjelinek dummy.vmu_zoneid = ALL_ZONES; 17533247Sgjelinek dummy.vmu_id = 0; 17543247Sgjelinek dummy.vmu_type = VMUSAGE_SYSTEM; 17553247Sgjelinek if (copyout(&dummy, out_result, 17563247Sgjelinek sizeof (vmusage_t))) 17573247Sgjelinek return (set_errno( 17583247Sgjelinek EFAULT)); 17593247Sgjelinek out_result++; 17603247Sgjelinek } 17613247Sgjelinek } 17623247Sgjelinek } 17633247Sgjelinek 17643247Sgjelinek /* Skip results that do not match requested type */ 17653247Sgjelinek if ((result->vmu_type & types) == 0) 17663247Sgjelinek continue; 17673247Sgjelinek 17683247Sgjelinek /* Skip collated results if not requested */ 17693247Sgjelinek if (result->vmu_zoneid == ALL_ZONES) { 17703247Sgjelinek if (result->vmu_type == VMUSAGE_PROJECTS && 17713247Sgjelinek (flags & VMUSAGE_COL_PROJECTS) == 0) 17723247Sgjelinek continue; 17733247Sgjelinek if (result->vmu_type == VMUSAGE_EUSERS && 17743247Sgjelinek (flags & VMUSAGE_COL_EUSERS) == 0) 17753247Sgjelinek continue; 17763247Sgjelinek if (result->vmu_type == VMUSAGE_RUSERS && 17773247Sgjelinek (flags & VMUSAGE_COL_RUSERS) == 0) 17783247Sgjelinek continue; 17793247Sgjelinek } 17803247Sgjelinek 17813247Sgjelinek /* Skip "other zone" results if not requested */ 17823247Sgjelinek if (result->vmu_zoneid != curproc->p_zone->zone_id) { 17833247Sgjelinek if (result->vmu_type == VMUSAGE_ZONE && 17843247Sgjelinek (flags & VMUSAGE_ALL_ZONES) == 0) 17853247Sgjelinek continue; 17863247Sgjelinek if (result->vmu_type == VMUSAGE_PROJECTS && 17873247Sgjelinek (flags & (VMUSAGE_ALL_PROJECTS | 17883247Sgjelinek VMUSAGE_COL_PROJECTS)) == 0) 17893247Sgjelinek continue; 17903247Sgjelinek if (result->vmu_type == VMUSAGE_TASKS && 17913247Sgjelinek (flags & VMUSAGE_ALL_TASKS) == 0) 17923247Sgjelinek continue; 17933247Sgjelinek if (result->vmu_type == VMUSAGE_RUSERS && 17943247Sgjelinek (flags & (VMUSAGE_ALL_RUSERS | 17953247Sgjelinek VMUSAGE_COL_RUSERS)) == 0) 17963247Sgjelinek continue; 17973247Sgjelinek if (result->vmu_type == VMUSAGE_EUSERS && 17983247Sgjelinek (flags & (VMUSAGE_ALL_EUSERS | 17993247Sgjelinek VMUSAGE_COL_EUSERS)) == 0) 18003247Sgjelinek continue; 18013247Sgjelinek } 18023247Sgjelinek count++; 18033247Sgjelinek if (out_result != NULL) { 18043247Sgjelinek if (bufsize < count) { 18053247Sgjelinek ret = set_errno(EOVERFLOW); 18063247Sgjelinek } else { 18073247Sgjelinek if (copyout(result, out_result, 18083247Sgjelinek sizeof (vmusage_t))) 18093247Sgjelinek return (set_errno(EFAULT)); 18103247Sgjelinek out_result++; 18113247Sgjelinek } 18123247Sgjelinek } 18133247Sgjelinek } 18143247Sgjelinek if (nres != NULL) 18153247Sgjelinek if (copyout(&count, (void *)nres, sizeof (size_t))) 18163247Sgjelinek return (set_errno(EFAULT)); 18173247Sgjelinek 18183247Sgjelinek return (ret); 18193247Sgjelinek } 18203247Sgjelinek 18213247Sgjelinek /* 18223247Sgjelinek * vm_getusage() 18233247Sgjelinek * 18243247Sgjelinek * Counts rss and swap by zone, project, task, and/or user. The flags argument 18253247Sgjelinek * determines the type of results structures returned. Flags requesting 18263247Sgjelinek * results from more than one zone are "flattened" to the local zone if the 18273247Sgjelinek * caller is not the global zone. 18283247Sgjelinek * 18293247Sgjelinek * args: 18303247Sgjelinek * flags: bitmap consisting of one or more of VMUSAGE_*. 18313247Sgjelinek * age: maximum allowable age (time since counting was done) in 18323247Sgjelinek * seconds of the results. Results from previous callers are 18333247Sgjelinek * cached in kernel. 18343247Sgjelinek * buf: pointer to buffer array of vmusage_t. If NULL, then only nres 18353247Sgjelinek * set on success. 18363247Sgjelinek * nres: Set to number of vmusage_t structures pointed to by buf 18373247Sgjelinek * before calling vm_getusage(). 18383247Sgjelinek * On return 0 (success) or ENOSPC, is set to the number of result 18393247Sgjelinek * structures returned or attempted to return. 18403247Sgjelinek * 18413247Sgjelinek * returns 0 on success, -1 on failure: 18423247Sgjelinek * EINTR (interrupted) 18433247Sgjelinek * ENOSPC (nres to small for results, nres set to needed value for success) 18443247Sgjelinek * EINVAL (flags invalid) 18453247Sgjelinek * EFAULT (bad address for buf or nres) 18463247Sgjelinek */ 18473247Sgjelinek int 18483247Sgjelinek vm_getusage(uint_t flags, time_t age, vmusage_t *buf, size_t *nres) 18493247Sgjelinek { 18503247Sgjelinek vmu_entity_t *entity; 18513247Sgjelinek vmusage_t *result; 18523247Sgjelinek int ret = 0; 18533247Sgjelinek int cacherecent = 0; 18543247Sgjelinek hrtime_t now; 18553247Sgjelinek uint_t flags_orig; 18563247Sgjelinek 18573247Sgjelinek /* 18583247Sgjelinek * Non-global zones cannot request system wide and/or collated 18593247Sgjelinek * results, or the system result, so munge the flags accordingly. 18603247Sgjelinek */ 18613247Sgjelinek flags_orig = flags; 18623247Sgjelinek if (curproc->p_zone != global_zone) { 18633247Sgjelinek if (flags & (VMUSAGE_ALL_PROJECTS | VMUSAGE_COL_PROJECTS)) { 18643247Sgjelinek flags &= ~(VMUSAGE_ALL_PROJECTS | VMUSAGE_COL_PROJECTS); 18653247Sgjelinek flags |= VMUSAGE_PROJECTS; 18663247Sgjelinek } 18673247Sgjelinek if (flags & (VMUSAGE_ALL_RUSERS | VMUSAGE_COL_RUSERS)) { 18683247Sgjelinek flags &= ~(VMUSAGE_ALL_RUSERS | VMUSAGE_COL_RUSERS); 18693247Sgjelinek flags |= VMUSAGE_RUSERS; 18703247Sgjelinek } 18713247Sgjelinek if (flags & (VMUSAGE_ALL_EUSERS | VMUSAGE_COL_EUSERS)) { 18723247Sgjelinek flags &= ~(VMUSAGE_ALL_EUSERS | VMUSAGE_COL_EUSERS); 18733247Sgjelinek flags |= VMUSAGE_EUSERS; 18743247Sgjelinek } 18753247Sgjelinek if (flags & VMUSAGE_SYSTEM) { 18763247Sgjelinek flags &= ~VMUSAGE_SYSTEM; 18773247Sgjelinek flags |= VMUSAGE_ZONE; 18783247Sgjelinek } 18793247Sgjelinek } 18803247Sgjelinek 18813247Sgjelinek /* Check for unknown flags */ 18823247Sgjelinek if ((flags & (~VMUSAGE_MASK)) != 0) 18833247Sgjelinek return (set_errno(EINVAL)); 18843247Sgjelinek 18853247Sgjelinek /* Check for no flags */ 18863247Sgjelinek if ((flags & VMUSAGE_MASK) == 0) 18873247Sgjelinek return (set_errno(EINVAL)); 18883247Sgjelinek 18893247Sgjelinek mutex_enter(&vmu_data.vmu_lock); 18903247Sgjelinek now = gethrtime(); 18913247Sgjelinek 18923247Sgjelinek start: 18933247Sgjelinek if (vmu_data.vmu_cache != NULL) { 18943247Sgjelinek 18953247Sgjelinek vmu_cache_t *cache; 18963247Sgjelinek 18973247Sgjelinek if ((vmu_data.vmu_cache->vmc_timestamp + 18983247Sgjelinek ((hrtime_t)age * NANOSEC)) > now) 18993247Sgjelinek cacherecent = 1; 19003247Sgjelinek 19013247Sgjelinek if ((vmu_data.vmu_cache->vmc_flags & flags) == flags && 19023247Sgjelinek cacherecent == 1) { 19033247Sgjelinek cache = vmu_data.vmu_cache; 19043247Sgjelinek vmu_cache_hold(cache); 19053247Sgjelinek mutex_exit(&vmu_data.vmu_lock); 19063247Sgjelinek 19073247Sgjelinek ret = vmu_copyout_results(cache, buf, nres, flags_orig); 19083247Sgjelinek mutex_enter(&vmu_data.vmu_lock); 19093247Sgjelinek vmu_cache_rele(cache); 19103247Sgjelinek if (vmu_data.vmu_pending_waiters > 0) 19113247Sgjelinek cv_broadcast(&vmu_data.vmu_cv); 19123247Sgjelinek mutex_exit(&vmu_data.vmu_lock); 19133247Sgjelinek return (ret); 19143247Sgjelinek } 19153247Sgjelinek /* 19163247Sgjelinek * If the cache is recent, it is likely that there are other 19173247Sgjelinek * consumers of vm_getusage running, so add their flags to the 19183247Sgjelinek * desired flags for the calculation. 19193247Sgjelinek */ 19203247Sgjelinek if (cacherecent == 1) 19213247Sgjelinek flags = vmu_data.vmu_cache->vmc_flags | flags; 19223247Sgjelinek } 19233247Sgjelinek if (vmu_data.vmu_calc_thread == NULL) { 19243247Sgjelinek 19253247Sgjelinek vmu_cache_t *cache; 19263247Sgjelinek 19273247Sgjelinek vmu_data.vmu_calc_thread = curthread; 19283247Sgjelinek vmu_data.vmu_calc_flags = flags; 19293247Sgjelinek vmu_data.vmu_entities = NULL; 19303247Sgjelinek vmu_data.vmu_nentities = 0; 19313247Sgjelinek if (vmu_data.vmu_pending_waiters > 0) 19323247Sgjelinek vmu_data.vmu_calc_flags |= 19333247Sgjelinek vmu_data.vmu_pending_flags; 19343247Sgjelinek 19353247Sgjelinek vmu_data.vmu_pending_flags = 0; 19363247Sgjelinek mutex_exit(&vmu_data.vmu_lock); 19373247Sgjelinek vmu_calculate(); 19383247Sgjelinek mutex_enter(&vmu_data.vmu_lock); 19393247Sgjelinek /* copy results to cache */ 19403247Sgjelinek if (vmu_data.vmu_cache != NULL) 19413247Sgjelinek vmu_cache_rele(vmu_data.vmu_cache); 19423247Sgjelinek cache = vmu_data.vmu_cache = 19433247Sgjelinek vmu_cache_alloc(vmu_data.vmu_nentities, 19443247Sgjelinek vmu_data.vmu_calc_flags); 19453247Sgjelinek 19463247Sgjelinek result = cache->vmc_results; 19473247Sgjelinek for (entity = vmu_data.vmu_entities; entity != NULL; 19483247Sgjelinek entity = entity->vme_next) { 19493247Sgjelinek *result = entity->vme_result; 19503247Sgjelinek result++; 19513247Sgjelinek } 19523247Sgjelinek cache->vmc_timestamp = gethrtime(); 19533247Sgjelinek vmu_cache_hold(cache); 19543247Sgjelinek 19553247Sgjelinek vmu_data.vmu_calc_flags = 0; 19563247Sgjelinek vmu_data.vmu_calc_thread = NULL; 19573247Sgjelinek 19583247Sgjelinek if (vmu_data.vmu_pending_waiters > 0) 19593247Sgjelinek cv_broadcast(&vmu_data.vmu_cv); 19603247Sgjelinek 19613247Sgjelinek mutex_exit(&vmu_data.vmu_lock); 19623247Sgjelinek 19633247Sgjelinek /* copy cache */ 19643247Sgjelinek ret = vmu_copyout_results(cache, buf, nres, flags_orig); 19653247Sgjelinek mutex_enter(&vmu_data.vmu_lock); 19663247Sgjelinek vmu_cache_rele(cache); 19673247Sgjelinek mutex_exit(&vmu_data.vmu_lock); 19683247Sgjelinek 19693247Sgjelinek return (ret); 19703247Sgjelinek } 19713247Sgjelinek vmu_data.vmu_pending_flags |= flags; 19723247Sgjelinek vmu_data.vmu_pending_waiters++; 19733247Sgjelinek while (vmu_data.vmu_calc_thread != NULL) { 19743247Sgjelinek if (cv_wait_sig(&vmu_data.vmu_cv, 19753247Sgjelinek &vmu_data.vmu_lock) == 0) { 19763247Sgjelinek vmu_data.vmu_pending_waiters--; 19773247Sgjelinek mutex_exit(&vmu_data.vmu_lock); 19783247Sgjelinek return (set_errno(EINTR)); 19793247Sgjelinek } 19803247Sgjelinek } 19813247Sgjelinek vmu_data.vmu_pending_waiters--; 19823247Sgjelinek goto start; 19833247Sgjelinek } 1984