10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*3085Strevtom * Common Development and Distribution License (the "License"). 6*3085Strevtom * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 22*3085Strevtom * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 270Sstevel@tonic-gate 280Sstevel@tonic-gate /* 290Sstevel@tonic-gate * The following routines implement the hat layer's 300Sstevel@tonic-gate * recording of the referenced and modified bits. 310Sstevel@tonic-gate */ 320Sstevel@tonic-gate 330Sstevel@tonic-gate #include <sys/types.h> 340Sstevel@tonic-gate #include <sys/param.h> 350Sstevel@tonic-gate #include <sys/systm.h> 360Sstevel@tonic-gate #include <sys/debug.h> 370Sstevel@tonic-gate #include <sys/kmem.h> 380Sstevel@tonic-gate 390Sstevel@tonic-gate /* 400Sstevel@tonic-gate * Note, usage of cmn_err requires you not hold any hat layer locks. 410Sstevel@tonic-gate */ 420Sstevel@tonic-gate #include <sys/cmn_err.h> 430Sstevel@tonic-gate 440Sstevel@tonic-gate #include <vm/as.h> 450Sstevel@tonic-gate #include <vm/hat.h> 460Sstevel@tonic-gate 470Sstevel@tonic-gate kmutex_t hat_statlock; /* protects all hat statistics data */ 480Sstevel@tonic-gate struct hrmstat *hrm_memlist; /* tracks memory alloced for hrm_blist blocks */ 490Sstevel@tonic-gate struct hrmstat **hrm_hashtab; /* hash table for finding blocks quickly */ 500Sstevel@tonic-gate struct hrmstat *hrm_blist; 510Sstevel@tonic-gate int hrm_blist_incr = HRM_BLIST_INCR; 520Sstevel@tonic-gate int hrm_blist_lowater = HRM_BLIST_INCR/2; 530Sstevel@tonic-gate int hrm_blist_num = 0; 540Sstevel@tonic-gate int hrm_blist_total = 0; 550Sstevel@tonic-gate int hrm_mlockinited = 0; 560Sstevel@tonic-gate int hrm_allocfailmsg = 0; /* print a message when allocations fail */ 570Sstevel@tonic-gate int hrm_allocfail = 0; 580Sstevel@tonic-gate 590Sstevel@tonic-gate static struct hrmstat *hrm_balloc(void); 60830Speterte static void hrm_init(void); 610Sstevel@tonic-gate static void hrm_link(struct hrmstat *); 620Sstevel@tonic-gate static void hrm_setbits(struct hrmstat *, caddr_t, uint_t); 630Sstevel@tonic-gate static void hrm_hashout(struct hrmstat *); 640Sstevel@tonic-gate static void hrm_getblk(int); 650Sstevel@tonic-gate 660Sstevel@tonic-gate #define hrm_hash(as, addr) \ 670Sstevel@tonic-gate (HRM_HASHMASK & \ 680Sstevel@tonic-gate (((uintptr_t)(addr) >> HRM_BASESHIFT) ^ ((uintptr_t)(as) >> 2))) 690Sstevel@tonic-gate 700Sstevel@tonic-gate #define hrm_match(hrm, as, addr) \ 710Sstevel@tonic-gate (((hrm)->hrm_as == (as) && \ 720Sstevel@tonic-gate ((hrm)->hrm_base == ((uintptr_t)(addr) & HRM_BASEMASK))) ? 1 : 0) 730Sstevel@tonic-gate 740Sstevel@tonic-gate /* 750Sstevel@tonic-gate * reserve enough statistic blocks for 760Sstevel@tonic-gate * chunk of bytes (pages) in a given as. 770Sstevel@tonic-gate */ 780Sstevel@tonic-gate /* ARGSUSED */ 790Sstevel@tonic-gate void 800Sstevel@tonic-gate hat_resvstat(size_t chunk, struct as *as, caddr_t addr) 810Sstevel@tonic-gate { 820Sstevel@tonic-gate int nhrm = btop(chunk)/HRM_PAGES; 830Sstevel@tonic-gate 840Sstevel@tonic-gate if (nhrm < HRM_BLIST_INCR) 850Sstevel@tonic-gate nhrm = 0; /* preallocate at least HRM_BLIST_INCR */ 860Sstevel@tonic-gate hrm_getblk(nhrm); 870Sstevel@tonic-gate } 880Sstevel@tonic-gate 890Sstevel@tonic-gate /* 900Sstevel@tonic-gate * Start the statistics gathering for an address space. 910Sstevel@tonic-gate * Return -1 if we can't do it, otherwise return an opaque 920Sstevel@tonic-gate * identifier to be used when querying for the gathered statistics. 930Sstevel@tonic-gate * The identifier is an unused bit in a_vbits. 940Sstevel@tonic-gate * Bit 0 is reserved for swsmon. 950Sstevel@tonic-gate */ 960Sstevel@tonic-gate int 970Sstevel@tonic-gate hat_startstat(struct as *as) 980Sstevel@tonic-gate { 990Sstevel@tonic-gate uint_t nbits; /* number of bits */ 1000Sstevel@tonic-gate uint_t bn; /* bit number */ 1010Sstevel@tonic-gate uint_t id; /* new vbit, identifier */ 1020Sstevel@tonic-gate uint_t vbits; /* used vbits of address space */ 1030Sstevel@tonic-gate size_t chunk; /* mapped size for stats */ 1040Sstevel@tonic-gate /* 1050Sstevel@tonic-gate * Initialize global data, if needed. 1060Sstevel@tonic-gate */ 107830Speterte hrm_init(); 1080Sstevel@tonic-gate 1090Sstevel@tonic-gate /* 1100Sstevel@tonic-gate * If the refmod saving memory allocator runs out, print 1110Sstevel@tonic-gate * a warning message about how to fix it, see comment at 1120Sstevel@tonic-gate * the beginning of hat_setstat. 1130Sstevel@tonic-gate */ 1140Sstevel@tonic-gate if (hrm_allocfailmsg) { 1150Sstevel@tonic-gate cmn_err(CE_WARN, 1160Sstevel@tonic-gate "hrm_balloc failures occured, increase hrm_blist_incr"); 1170Sstevel@tonic-gate hrm_allocfailmsg = 0; 1180Sstevel@tonic-gate } 1190Sstevel@tonic-gate 1200Sstevel@tonic-gate /* 1210Sstevel@tonic-gate * Verify that a buffer of statistics blocks exists 1220Sstevel@tonic-gate * and allocate more, if needed. 1230Sstevel@tonic-gate */ 1240Sstevel@tonic-gate 1250Sstevel@tonic-gate chunk = hat_get_mapped_size(as->a_hat); 1260Sstevel@tonic-gate chunk = (btop(chunk)/HRM_PAGES); 1270Sstevel@tonic-gate if (chunk < HRM_BLIST_INCR) 1280Sstevel@tonic-gate chunk = 0; 1290Sstevel@tonic-gate 1300Sstevel@tonic-gate hrm_getblk((int)chunk); 1310Sstevel@tonic-gate 1320Sstevel@tonic-gate /* 1330Sstevel@tonic-gate * Find a unused id in the given address space. 1340Sstevel@tonic-gate */ 1350Sstevel@tonic-gate hat_enter(as->a_hat); 1360Sstevel@tonic-gate vbits = as->a_vbits; 1370Sstevel@tonic-gate nbits = sizeof (as->a_vbits) * NBBY; 1380Sstevel@tonic-gate for (bn = 1, id = 2; bn < (nbits - 1); bn++, id <<= 1) 1390Sstevel@tonic-gate if ((id & vbits) == 0) 1400Sstevel@tonic-gate break; 1410Sstevel@tonic-gate if (bn >= (nbits - 1)) { 1420Sstevel@tonic-gate hat_exit(as->a_hat); 1430Sstevel@tonic-gate return (-1); 1440Sstevel@tonic-gate } 1450Sstevel@tonic-gate as->a_vbits |= id; 1460Sstevel@tonic-gate hat_exit(as->a_hat); 1470Sstevel@tonic-gate (void) hat_stats_enable(as->a_hat); 1480Sstevel@tonic-gate return (id); 1490Sstevel@tonic-gate } 1500Sstevel@tonic-gate 1510Sstevel@tonic-gate /* 1520Sstevel@tonic-gate * Record referenced and modified information for an address space. 1530Sstevel@tonic-gate * Rmbits is a word containing the referenced bit in bit position 1 1540Sstevel@tonic-gate * and the modified bit in bit position 0. 1550Sstevel@tonic-gate * 1560Sstevel@tonic-gate * For current informational uses, one can rerun any program using 1570Sstevel@tonic-gate * this facility after modifying the hrm_blist_incr to be a larger 1580Sstevel@tonic-gate * amount so that a larger buffer of blocks will be maintained. 1590Sstevel@tonic-gate */ 1600Sstevel@tonic-gate void 1610Sstevel@tonic-gate hat_setstat(struct as *as, caddr_t addr, size_t len, uint_t rmbits) 1620Sstevel@tonic-gate { 1630Sstevel@tonic-gate struct hrmstat *hrm; 1640Sstevel@tonic-gate uint_t vbits, newbits, nb; 1650Sstevel@tonic-gate int h; 1660Sstevel@tonic-gate 1670Sstevel@tonic-gate ASSERT(len == PAGESIZE); 1680Sstevel@tonic-gate ASSERT((rmbits & ~(P_MOD|P_REF)) == 0); 1690Sstevel@tonic-gate 1700Sstevel@tonic-gate if (rmbits == 0) 1710Sstevel@tonic-gate return; 1720Sstevel@tonic-gate 1730Sstevel@tonic-gate /* 1740Sstevel@tonic-gate * Initialize global data, if needed. 1750Sstevel@tonic-gate */ 176830Speterte hrm_init(); 1770Sstevel@tonic-gate 1780Sstevel@tonic-gate mutex_enter(&hat_statlock); 1790Sstevel@tonic-gate 1800Sstevel@tonic-gate /* 181830Speterte * The previous owner of hat_statlock could have been 182830Speterte * hat_freestat(). Check whether hrm_hashtab is NULL, if it is, 183830Speterte * we bail out. 184830Speterte */ 185830Speterte if (hrm_hashtab == NULL) { 186830Speterte mutex_exit(&hat_statlock); 187830Speterte return; 188830Speterte } 189830Speterte 190830Speterte /* 1910Sstevel@tonic-gate * Search the hash list for the as and addr we are looking for 1920Sstevel@tonic-gate * and set the ref and mod bits in every block that matches. 1930Sstevel@tonic-gate */ 1940Sstevel@tonic-gate vbits = 0; 1950Sstevel@tonic-gate h = hrm_hash(as, addr); 1960Sstevel@tonic-gate for (hrm = hrm_hashtab[h]; hrm; hrm = hrm->hrm_hnext) { 1970Sstevel@tonic-gate if (hrm_match(hrm, as, addr)) { 1980Sstevel@tonic-gate hrm_setbits(hrm, addr, rmbits); 1990Sstevel@tonic-gate vbits |= hrm->hrm_id; 2000Sstevel@tonic-gate } 2010Sstevel@tonic-gate } 2020Sstevel@tonic-gate 2030Sstevel@tonic-gate /* 2040Sstevel@tonic-gate * If we didn't find a block for all of the enabled 2050Sstevel@tonic-gate * vpages bits, then allocate and initialize a block 2060Sstevel@tonic-gate * for each bit that was not found. 2070Sstevel@tonic-gate */ 2080Sstevel@tonic-gate if (vbits != as->a_vbits) { 2090Sstevel@tonic-gate newbits = vbits ^ as->a_vbits; 2100Sstevel@tonic-gate while (newbits) { 2110Sstevel@tonic-gate if (ffs(newbits)) 2120Sstevel@tonic-gate nb = 1 << (ffs(newbits)-1); 2130Sstevel@tonic-gate hrm = (struct hrmstat *)hrm_balloc(); 2140Sstevel@tonic-gate if (hrm == NULL) { 2150Sstevel@tonic-gate hrm_allocfailmsg = 1; 2160Sstevel@tonic-gate hrm_allocfail++; 2170Sstevel@tonic-gate mutex_exit(&hat_statlock); 2180Sstevel@tonic-gate return; 2190Sstevel@tonic-gate } 2200Sstevel@tonic-gate hrm->hrm_as = as; 2210Sstevel@tonic-gate hrm->hrm_base = (uintptr_t)addr & HRM_BASEMASK; 2220Sstevel@tonic-gate hrm->hrm_id = nb; 2230Sstevel@tonic-gate hrm_link(hrm); 2240Sstevel@tonic-gate hrm_setbits(hrm, addr, rmbits); 2250Sstevel@tonic-gate newbits &= ~nb; 2260Sstevel@tonic-gate } 2270Sstevel@tonic-gate } 2280Sstevel@tonic-gate mutex_exit(&hat_statlock); 2290Sstevel@tonic-gate } 2300Sstevel@tonic-gate 2310Sstevel@tonic-gate /* 2320Sstevel@tonic-gate * Free the resources used to maintain the referenced and modified 2330Sstevel@tonic-gate * statistics for the virtual page view of an address space 2340Sstevel@tonic-gate * identified by id. 2350Sstevel@tonic-gate */ 2360Sstevel@tonic-gate void 2370Sstevel@tonic-gate hat_freestat(struct as *as, int id) 2380Sstevel@tonic-gate { 2390Sstevel@tonic-gate struct hrmstat *hrm, *prev_ahrm; 240*3085Strevtom struct hrmstat **hashtab; 2410Sstevel@tonic-gate 2420Sstevel@tonic-gate hat_stats_disable(as->a_hat); /* tell the hat layer to stop */ 2430Sstevel@tonic-gate hat_enter(as->a_hat); 2440Sstevel@tonic-gate if (id == 0) 2450Sstevel@tonic-gate as->a_vbits = 0; 2460Sstevel@tonic-gate else 2470Sstevel@tonic-gate as->a_vbits &= ~id; 2480Sstevel@tonic-gate 2490Sstevel@tonic-gate if ((hrm = as->a_hrm) == NULL) { 2500Sstevel@tonic-gate hat_exit(as->a_hat); 2510Sstevel@tonic-gate return; 2520Sstevel@tonic-gate } 2530Sstevel@tonic-gate hat_exit(as->a_hat); 2540Sstevel@tonic-gate 2550Sstevel@tonic-gate mutex_enter(&hat_statlock); 2560Sstevel@tonic-gate if (hrm_hashtab == NULL) { 2570Sstevel@tonic-gate /* can't happen? */ 2580Sstevel@tonic-gate mutex_exit(&hat_statlock); 2590Sstevel@tonic-gate return; 2600Sstevel@tonic-gate } 2610Sstevel@tonic-gate for (prev_ahrm = NULL; hrm; hrm = hrm->hrm_anext) { 2620Sstevel@tonic-gate if ((id == hrm->hrm_id) || (id == NULL)) { 2630Sstevel@tonic-gate 2640Sstevel@tonic-gate hrm_hashout(hrm); 2650Sstevel@tonic-gate hrm->hrm_hnext = hrm_blist; 2660Sstevel@tonic-gate hrm_blist = hrm; 2670Sstevel@tonic-gate hrm_blist_num++; 2680Sstevel@tonic-gate 2690Sstevel@tonic-gate if (prev_ahrm == NULL) 2700Sstevel@tonic-gate as->a_hrm = hrm->hrm_anext; 2710Sstevel@tonic-gate else 2720Sstevel@tonic-gate prev_ahrm->hrm_anext = hrm->hrm_anext; 2730Sstevel@tonic-gate 2740Sstevel@tonic-gate } else 2750Sstevel@tonic-gate prev_ahrm = hrm; 2760Sstevel@tonic-gate } 2770Sstevel@tonic-gate 2780Sstevel@tonic-gate /* 2790Sstevel@tonic-gate * If all statistics blocks are free, 2800Sstevel@tonic-gate * return the memory to the system. 2810Sstevel@tonic-gate */ 2820Sstevel@tonic-gate if (hrm_blist_num == hrm_blist_total) { 2830Sstevel@tonic-gate /* zero the block list since we are giving back its memory */ 2840Sstevel@tonic-gate hrm_blist = NULL; 2850Sstevel@tonic-gate hrm_blist_num = 0; 2860Sstevel@tonic-gate hrm_blist_total = 0; 287*3085Strevtom hrm = hrm_memlist; 288*3085Strevtom hrm_memlist = NULL; 289*3085Strevtom hashtab = hrm_hashtab; 290*3085Strevtom hrm_hashtab = NULL; 291*3085Strevtom } else { 292*3085Strevtom hashtab = NULL; 293*3085Strevtom } 294*3085Strevtom 295*3085Strevtom mutex_exit(&hat_statlock); 296*3085Strevtom 297*3085Strevtom if (hashtab != NULL) { 298*3085Strevtom struct hrmstat *next; 299*3085Strevtom 300*3085Strevtom kmem_free(hashtab, HRM_HASHSIZE * sizeof (char *)); 301*3085Strevtom while (hrm != NULL) { 302*3085Strevtom next = hrm->hrm_hnext; 3030Sstevel@tonic-gate kmem_free(hrm, hrm->hrm_base); 304*3085Strevtom hrm = next; 3050Sstevel@tonic-gate } 3060Sstevel@tonic-gate } 3070Sstevel@tonic-gate } 3080Sstevel@tonic-gate 3090Sstevel@tonic-gate /* 3100Sstevel@tonic-gate * Initialize any global state for the statistics handling. 3110Sstevel@tonic-gate * Hrm_lock protects the globally allocted memory: 3120Sstevel@tonic-gate * hrm_memlist and hrm_hashtab. 3130Sstevel@tonic-gate */ 314830Speterte static void 3150Sstevel@tonic-gate hrm_init(void) 3160Sstevel@tonic-gate { 3170Sstevel@tonic-gate /* 3180Sstevel@tonic-gate * Alloacte the hashtable if it doesn't exist yet. 3190Sstevel@tonic-gate */ 3200Sstevel@tonic-gate mutex_enter(&hat_statlock); 3210Sstevel@tonic-gate if (hrm_hashtab == NULL) 3220Sstevel@tonic-gate hrm_hashtab = 3230Sstevel@tonic-gate kmem_zalloc(HRM_HASHSIZE * sizeof (char *), KM_SLEEP); 3240Sstevel@tonic-gate mutex_exit(&hat_statlock); 3250Sstevel@tonic-gate } 3260Sstevel@tonic-gate 3270Sstevel@tonic-gate /* 3280Sstevel@tonic-gate * Grab memory for statistics gathering of the hat layer. 3290Sstevel@tonic-gate */ 3300Sstevel@tonic-gate static void 3310Sstevel@tonic-gate hrm_getblk(int chunk) 3320Sstevel@tonic-gate { 3330Sstevel@tonic-gate struct hrmstat *hrm, *l; 3340Sstevel@tonic-gate int i; 3350Sstevel@tonic-gate int hrm_incr; 3360Sstevel@tonic-gate 3370Sstevel@tonic-gate mutex_enter(&hat_statlock); 3380Sstevel@tonic-gate if ((hrm_blist == NULL) || 3390Sstevel@tonic-gate (hrm_blist_num <= hrm_blist_lowater) || 340830Speterte (chunk && (hrm_blist_num < chunk))) { 3410Sstevel@tonic-gate 3420Sstevel@tonic-gate mutex_exit(&hat_statlock); 3430Sstevel@tonic-gate 3440Sstevel@tonic-gate hrm_incr = chunk? chunk : hrm_blist_incr; 3450Sstevel@tonic-gate hrm = kmem_zalloc(sizeof (struct hrmstat) * hrm_incr, KM_SLEEP); 3460Sstevel@tonic-gate hrm->hrm_base = sizeof (struct hrmstat) * hrm_incr; 3470Sstevel@tonic-gate 3480Sstevel@tonic-gate /* 3490Sstevel@tonic-gate * thread the allocated blocks onto a freelist 3500Sstevel@tonic-gate * using the first block to hold information for 3510Sstevel@tonic-gate * freeing them all later 3520Sstevel@tonic-gate */ 3530Sstevel@tonic-gate mutex_enter(&hat_statlock); 3540Sstevel@tonic-gate hrm->hrm_hnext = hrm_memlist; 3550Sstevel@tonic-gate hrm_memlist = hrm; 3560Sstevel@tonic-gate 3570Sstevel@tonic-gate hrm_blist_total += (hrm_incr - 1); 3580Sstevel@tonic-gate for (i = 1; i < hrm_incr; i++) { 3590Sstevel@tonic-gate l = &hrm[i]; 3600Sstevel@tonic-gate l->hrm_hnext = hrm_blist; 3610Sstevel@tonic-gate hrm_blist = l; 3620Sstevel@tonic-gate hrm_blist_num++; 3630Sstevel@tonic-gate } 3640Sstevel@tonic-gate } 3650Sstevel@tonic-gate mutex_exit(&hat_statlock); 3660Sstevel@tonic-gate } 3670Sstevel@tonic-gate 3680Sstevel@tonic-gate static void 3690Sstevel@tonic-gate hrm_hashin(struct hrmstat *hrm) 3700Sstevel@tonic-gate { 3710Sstevel@tonic-gate int h; 3720Sstevel@tonic-gate 3730Sstevel@tonic-gate ASSERT(MUTEX_HELD(&hat_statlock)); 3740Sstevel@tonic-gate h = hrm_hash(hrm->hrm_as, hrm->hrm_base); 3750Sstevel@tonic-gate 3760Sstevel@tonic-gate hrm->hrm_hnext = hrm_hashtab[h]; 3770Sstevel@tonic-gate hrm_hashtab[h] = hrm; 3780Sstevel@tonic-gate } 3790Sstevel@tonic-gate 3800Sstevel@tonic-gate static void 3810Sstevel@tonic-gate hrm_hashout(struct hrmstat *hrm) 3820Sstevel@tonic-gate { 3830Sstevel@tonic-gate struct hrmstat *list, **prev_hrm; 3840Sstevel@tonic-gate int h; 3850Sstevel@tonic-gate 3860Sstevel@tonic-gate ASSERT(MUTEX_HELD(&hat_statlock)); 3870Sstevel@tonic-gate h = hrm_hash(hrm->hrm_as, hrm->hrm_base); 3880Sstevel@tonic-gate list = hrm_hashtab[h]; 3890Sstevel@tonic-gate prev_hrm = &hrm_hashtab[h]; 3900Sstevel@tonic-gate 3910Sstevel@tonic-gate while (list) { 3920Sstevel@tonic-gate if (list == hrm) { 3930Sstevel@tonic-gate *prev_hrm = list->hrm_hnext; 3940Sstevel@tonic-gate return; 3950Sstevel@tonic-gate } 3960Sstevel@tonic-gate prev_hrm = &list->hrm_hnext; 3970Sstevel@tonic-gate list = list->hrm_hnext; 3980Sstevel@tonic-gate } 3990Sstevel@tonic-gate } 4000Sstevel@tonic-gate 4010Sstevel@tonic-gate 4020Sstevel@tonic-gate /* 4030Sstevel@tonic-gate * Link a statistic block into an address space and also put it 4040Sstevel@tonic-gate * on the hash list for future references. 4050Sstevel@tonic-gate */ 4060Sstevel@tonic-gate static void 4070Sstevel@tonic-gate hrm_link(struct hrmstat *hrm) 4080Sstevel@tonic-gate { 4090Sstevel@tonic-gate struct as *as = hrm->hrm_as; 4100Sstevel@tonic-gate 4110Sstevel@tonic-gate ASSERT(MUTEX_HELD(&hat_statlock)); 4120Sstevel@tonic-gate hrm->hrm_anext = as->a_hrm; 4130Sstevel@tonic-gate as->a_hrm = hrm; 4140Sstevel@tonic-gate hrm_hashin(hrm); 4150Sstevel@tonic-gate } 4160Sstevel@tonic-gate 4170Sstevel@tonic-gate /* 4180Sstevel@tonic-gate * Allocate a block for statistics keeping. 4190Sstevel@tonic-gate * Returns NULL if blocks are unavailable. 4200Sstevel@tonic-gate */ 4210Sstevel@tonic-gate static struct hrmstat * 4220Sstevel@tonic-gate hrm_balloc(void) 4230Sstevel@tonic-gate { 4240Sstevel@tonic-gate struct hrmstat *hrm; 4250Sstevel@tonic-gate 4260Sstevel@tonic-gate ASSERT(MUTEX_HELD(&hat_statlock)); 4270Sstevel@tonic-gate 4280Sstevel@tonic-gate hrm = hrm_blist; 4290Sstevel@tonic-gate if (hrm != NULL) { 4300Sstevel@tonic-gate hrm_blist = hrm->hrm_hnext; 4310Sstevel@tonic-gate hrm_blist_num--; 4320Sstevel@tonic-gate hrm->hrm_hnext = NULL; 4330Sstevel@tonic-gate } 4340Sstevel@tonic-gate return (hrm); 4350Sstevel@tonic-gate } 4360Sstevel@tonic-gate 4370Sstevel@tonic-gate /* 4380Sstevel@tonic-gate * Set the ref and mod bits for addr within statistics block hrm. 4390Sstevel@tonic-gate */ 4400Sstevel@tonic-gate static void 4410Sstevel@tonic-gate hrm_setbits(struct hrmstat *hrm, caddr_t addr, uint_t bits) 4420Sstevel@tonic-gate { 4430Sstevel@tonic-gate uint_t po, bo, spb; 4440Sstevel@tonic-gate uint_t nbits; 4450Sstevel@tonic-gate 4460Sstevel@tonic-gate po = ((uintptr_t)addr & HRM_BASEOFFSET) >> MMU_PAGESHIFT; /* pg off */ 4470Sstevel@tonic-gate bo = po / (NBBY / 2); /* which byte in bit array */ 4480Sstevel@tonic-gate spb = (3 - (po & 3)) * 2; /* shift position within byte */ 4490Sstevel@tonic-gate nbits = bits << spb; /* bit mask */ 4500Sstevel@tonic-gate hrm->hrm_bits[bo] |= nbits; 4510Sstevel@tonic-gate } 4520Sstevel@tonic-gate 4530Sstevel@tonic-gate /* 4540Sstevel@tonic-gate * Return collected statistics about an address space. 4550Sstevel@tonic-gate * If clearflag is set, atomically read and zero the bits. 4560Sstevel@tonic-gate * 4570Sstevel@tonic-gate * Fill in the data array supplied with the referenced and 4580Sstevel@tonic-gate * modified bits collected for address range [addr ... addr + len] 4590Sstevel@tonic-gate * in address space, as, uniquely identified by id. 4600Sstevel@tonic-gate * The destination is a byte array. We fill in three bits per byte: 4610Sstevel@tonic-gate * referenced, modified, and hwmapped bits. 4620Sstevel@tonic-gate * Kernel only interface, can't fault on destination data array. 4630Sstevel@tonic-gate * 4640Sstevel@tonic-gate */ 4650Sstevel@tonic-gate void 4660Sstevel@tonic-gate hat_getstat(struct as *as, caddr_t addr, size_t len, uint_t id, 4670Sstevel@tonic-gate caddr_t datap, int clearflag) 4680Sstevel@tonic-gate { 4690Sstevel@tonic-gate size_t np; /* number of pages */ 4700Sstevel@tonic-gate caddr_t a; 4710Sstevel@tonic-gate char *dp; 4720Sstevel@tonic-gate 4730Sstevel@tonic-gate np = btop(len); 4740Sstevel@tonic-gate bzero(datap, np); 4750Sstevel@tonic-gate 4760Sstevel@tonic-gate hat_sync(as->a_hat, addr, len, clearflag); 4770Sstevel@tonic-gate 4780Sstevel@tonic-gate /* allocate more statistics blocks if needed */ 4790Sstevel@tonic-gate hrm_getblk(0); 4800Sstevel@tonic-gate 4810Sstevel@tonic-gate mutex_enter(&hat_statlock); 4820Sstevel@tonic-gate if (hrm_hashtab == NULL) { 4830Sstevel@tonic-gate /* can happen when victim process exits */ 4840Sstevel@tonic-gate mutex_exit(&hat_statlock); 4850Sstevel@tonic-gate return; 4860Sstevel@tonic-gate } 4870Sstevel@tonic-gate dp = datap; 4880Sstevel@tonic-gate a = (caddr_t)((uintptr_t)addr & (uintptr_t)PAGEMASK); 4890Sstevel@tonic-gate while (a < addr + len) { 4900Sstevel@tonic-gate struct hrmstat *hrm; 4910Sstevel@tonic-gate size_t n; /* number of pages, temp */ 4920Sstevel@tonic-gate int h; /* hash index */ 4930Sstevel@tonic-gate uint_t po; 4940Sstevel@tonic-gate 4950Sstevel@tonic-gate h = hrm_hash(as, a); 4960Sstevel@tonic-gate n = (HRM_PAGES - 4970Sstevel@tonic-gate (((uintptr_t)a & HRM_PAGEMASK) >> MMU_PAGESHIFT)); 4980Sstevel@tonic-gate if (n > np) 4990Sstevel@tonic-gate n = np; 5000Sstevel@tonic-gate po = ((uintptr_t)a & HRM_BASEOFFSET) >> MMU_PAGESHIFT; 5010Sstevel@tonic-gate 5020Sstevel@tonic-gate for (hrm = hrm_hashtab[h]; hrm; hrm = hrm->hrm_hnext) { 5030Sstevel@tonic-gate if (hrm->hrm_as == as && 5040Sstevel@tonic-gate hrm->hrm_base == ((uintptr_t)a & HRM_BASEMASK) && 5050Sstevel@tonic-gate id == hrm->hrm_id) { 5060Sstevel@tonic-gate int i, nr; 5070Sstevel@tonic-gate uint_t bo, spb; 5080Sstevel@tonic-gate 5090Sstevel@tonic-gate /* 5100Sstevel@tonic-gate * Extract leading unaligned bits. 5110Sstevel@tonic-gate */ 5120Sstevel@tonic-gate i = 0; 5130Sstevel@tonic-gate while (i < n && (po & 3)) { 5140Sstevel@tonic-gate bo = po / (NBBY / 2); 5150Sstevel@tonic-gate spb = (3 - (po & 3)) * 2; 5160Sstevel@tonic-gate *dp++ |= (hrm->hrm_bits[bo] >> spb) & 3; 5170Sstevel@tonic-gate if (clearflag) 5180Sstevel@tonic-gate hrm->hrm_bits[bo] &= ~(3<<spb); 5190Sstevel@tonic-gate po++; 5200Sstevel@tonic-gate i++; 5210Sstevel@tonic-gate } 5220Sstevel@tonic-gate /* 5230Sstevel@tonic-gate * Extract aligned bits. 5240Sstevel@tonic-gate */ 5250Sstevel@tonic-gate nr = n/4*4; 5260Sstevel@tonic-gate bo = po / (NBBY / 2); 5270Sstevel@tonic-gate while (i < nr) { 5280Sstevel@tonic-gate int bits = hrm->hrm_bits[bo]; 5290Sstevel@tonic-gate *dp++ |= (bits >> 6) & 3; 5300Sstevel@tonic-gate *dp++ |= (bits >> 4) & 3; 5310Sstevel@tonic-gate *dp++ |= (bits >> 2) & 3; 5320Sstevel@tonic-gate *dp++ |= (bits >> 0) & 3; 5330Sstevel@tonic-gate if (clearflag) 5340Sstevel@tonic-gate hrm->hrm_bits[bo] = 0; 5350Sstevel@tonic-gate bo++; 5360Sstevel@tonic-gate po += 4; 5370Sstevel@tonic-gate i += 4; 5380Sstevel@tonic-gate } 5390Sstevel@tonic-gate /* 5400Sstevel@tonic-gate * Extract trailing unaligned bits. 5410Sstevel@tonic-gate */ 5420Sstevel@tonic-gate while (i < n) { 5430Sstevel@tonic-gate bo = po / (NBBY / 2); 5440Sstevel@tonic-gate spb = (3 - (po & 3)) * 2; 5450Sstevel@tonic-gate *dp++ |= (hrm->hrm_bits[bo] >> spb) & 3; 5460Sstevel@tonic-gate if (clearflag) 5470Sstevel@tonic-gate hrm->hrm_bits[bo] &= ~(3<<spb); 5480Sstevel@tonic-gate po++; 5490Sstevel@tonic-gate i++; 5500Sstevel@tonic-gate } 5510Sstevel@tonic-gate 5520Sstevel@tonic-gate break; 5530Sstevel@tonic-gate } 5540Sstevel@tonic-gate } 5550Sstevel@tonic-gate if (hrm == NULL) 5560Sstevel@tonic-gate dp += n; 5570Sstevel@tonic-gate np -= n; 5580Sstevel@tonic-gate a += n * MMU_PAGESIZE; 5590Sstevel@tonic-gate } 5600Sstevel@tonic-gate mutex_exit(&hat_statlock); 5610Sstevel@tonic-gate } 562