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