17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5a359d6b1Strevtom * Common Development and Distribution License (the "License"). 6a359d6b1Strevtom * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 22*c6224917SMichael Corcoran * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate /* 277c478bd9Sstevel@tonic-gate * The following routines implement the hat layer's 287c478bd9Sstevel@tonic-gate * recording of the referenced and modified bits. 297c478bd9Sstevel@tonic-gate */ 307c478bd9Sstevel@tonic-gate 317c478bd9Sstevel@tonic-gate #include <sys/types.h> 327c478bd9Sstevel@tonic-gate #include <sys/param.h> 337c478bd9Sstevel@tonic-gate #include <sys/systm.h> 347c478bd9Sstevel@tonic-gate #include <sys/debug.h> 357c478bd9Sstevel@tonic-gate #include <sys/kmem.h> 367c478bd9Sstevel@tonic-gate 377c478bd9Sstevel@tonic-gate /* 387c478bd9Sstevel@tonic-gate * Note, usage of cmn_err requires you not hold any hat layer locks. 397c478bd9Sstevel@tonic-gate */ 407c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h> 417c478bd9Sstevel@tonic-gate 427c478bd9Sstevel@tonic-gate #include <vm/as.h> 437c478bd9Sstevel@tonic-gate #include <vm/hat.h> 447c478bd9Sstevel@tonic-gate 457c478bd9Sstevel@tonic-gate kmutex_t hat_statlock; /* protects all hat statistics data */ 467c478bd9Sstevel@tonic-gate struct hrmstat *hrm_memlist; /* tracks memory alloced for hrm_blist blocks */ 477c478bd9Sstevel@tonic-gate struct hrmstat **hrm_hashtab; /* hash table for finding blocks quickly */ 487c478bd9Sstevel@tonic-gate struct hrmstat *hrm_blist; 497c478bd9Sstevel@tonic-gate int hrm_blist_incr = HRM_BLIST_INCR; 507c478bd9Sstevel@tonic-gate int hrm_blist_lowater = HRM_BLIST_INCR/2; 517c478bd9Sstevel@tonic-gate int hrm_blist_num = 0; 527c478bd9Sstevel@tonic-gate int hrm_blist_total = 0; 537c478bd9Sstevel@tonic-gate int hrm_mlockinited = 0; 547c478bd9Sstevel@tonic-gate int hrm_allocfailmsg = 0; /* print a message when allocations fail */ 557c478bd9Sstevel@tonic-gate int hrm_allocfail = 0; 567c478bd9Sstevel@tonic-gate 577c478bd9Sstevel@tonic-gate static struct hrmstat *hrm_balloc(void); 587c478bd9Sstevel@tonic-gate static void hrm_link(struct hrmstat *); 597c478bd9Sstevel@tonic-gate static void hrm_setbits(struct hrmstat *, caddr_t, uint_t); 607c478bd9Sstevel@tonic-gate static void hrm_hashout(struct hrmstat *); 617c478bd9Sstevel@tonic-gate static void hrm_getblk(int); 627c478bd9Sstevel@tonic-gate 637c478bd9Sstevel@tonic-gate #define hrm_hash(as, addr) \ 647c478bd9Sstevel@tonic-gate (HRM_HASHMASK & \ 657c478bd9Sstevel@tonic-gate (((uintptr_t)(addr) >> HRM_BASESHIFT) ^ ((uintptr_t)(as) >> 2))) 667c478bd9Sstevel@tonic-gate 677c478bd9Sstevel@tonic-gate #define hrm_match(hrm, as, addr) \ 687c478bd9Sstevel@tonic-gate (((hrm)->hrm_as == (as) && \ 697c478bd9Sstevel@tonic-gate ((hrm)->hrm_base == ((uintptr_t)(addr) & HRM_BASEMASK))) ? 1 : 0) 707c478bd9Sstevel@tonic-gate 717c478bd9Sstevel@tonic-gate /* 72*c6224917SMichael Corcoran * Called when an address space maps in more pages while stats are being 73*c6224917SMichael Corcoran * collected. 747c478bd9Sstevel@tonic-gate */ 757c478bd9Sstevel@tonic-gate /* ARGSUSED */ 767c478bd9Sstevel@tonic-gate void 777c478bd9Sstevel@tonic-gate hat_resvstat(size_t chunk, struct as *as, caddr_t addr) 787c478bd9Sstevel@tonic-gate { 797c478bd9Sstevel@tonic-gate } 807c478bd9Sstevel@tonic-gate 817c478bd9Sstevel@tonic-gate /* 827c478bd9Sstevel@tonic-gate * Start the statistics gathering for an address space. 837c478bd9Sstevel@tonic-gate * Return -1 if we can't do it, otherwise return an opaque 847c478bd9Sstevel@tonic-gate * identifier to be used when querying for the gathered statistics. 857c478bd9Sstevel@tonic-gate * The identifier is an unused bit in a_vbits. 867c478bd9Sstevel@tonic-gate * Bit 0 is reserved for swsmon. 877c478bd9Sstevel@tonic-gate */ 887c478bd9Sstevel@tonic-gate int 897c478bd9Sstevel@tonic-gate hat_startstat(struct as *as) 907c478bd9Sstevel@tonic-gate { 917c478bd9Sstevel@tonic-gate uint_t nbits; /* number of bits */ 927c478bd9Sstevel@tonic-gate uint_t bn; /* bit number */ 937c478bd9Sstevel@tonic-gate uint_t id; /* new vbit, identifier */ 947c478bd9Sstevel@tonic-gate uint_t vbits; /* used vbits of address space */ 957c478bd9Sstevel@tonic-gate size_t chunk; /* mapped size for stats */ 967c478bd9Sstevel@tonic-gate 977c478bd9Sstevel@tonic-gate /* 987c478bd9Sstevel@tonic-gate * If the refmod saving memory allocator runs out, print 997c478bd9Sstevel@tonic-gate * a warning message about how to fix it, see comment at 1007c478bd9Sstevel@tonic-gate * the beginning of hat_setstat. 1017c478bd9Sstevel@tonic-gate */ 1027c478bd9Sstevel@tonic-gate if (hrm_allocfailmsg) { 1037c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, 1047c478bd9Sstevel@tonic-gate "hrm_balloc failures occured, increase hrm_blist_incr"); 1057c478bd9Sstevel@tonic-gate hrm_allocfailmsg = 0; 1067c478bd9Sstevel@tonic-gate } 1077c478bd9Sstevel@tonic-gate 1087c478bd9Sstevel@tonic-gate /* 1097c478bd9Sstevel@tonic-gate * Verify that a buffer of statistics blocks exists 1107c478bd9Sstevel@tonic-gate * and allocate more, if needed. 1117c478bd9Sstevel@tonic-gate */ 1127c478bd9Sstevel@tonic-gate 1137c478bd9Sstevel@tonic-gate chunk = hat_get_mapped_size(as->a_hat); 1147c478bd9Sstevel@tonic-gate chunk = (btop(chunk)/HRM_PAGES); 1157c478bd9Sstevel@tonic-gate if (chunk < HRM_BLIST_INCR) 1167c478bd9Sstevel@tonic-gate chunk = 0; 1177c478bd9Sstevel@tonic-gate 1187c478bd9Sstevel@tonic-gate hrm_getblk((int)chunk); 1197c478bd9Sstevel@tonic-gate 1207c478bd9Sstevel@tonic-gate /* 1217c478bd9Sstevel@tonic-gate * Find a unused id in the given address space. 1227c478bd9Sstevel@tonic-gate */ 1237c478bd9Sstevel@tonic-gate hat_enter(as->a_hat); 1247c478bd9Sstevel@tonic-gate vbits = as->a_vbits; 1257c478bd9Sstevel@tonic-gate nbits = sizeof (as->a_vbits) * NBBY; 1267c478bd9Sstevel@tonic-gate for (bn = 1, id = 2; bn < (nbits - 1); bn++, id <<= 1) 1277c478bd9Sstevel@tonic-gate if ((id & vbits) == 0) 1287c478bd9Sstevel@tonic-gate break; 1297c478bd9Sstevel@tonic-gate if (bn >= (nbits - 1)) { 1307c478bd9Sstevel@tonic-gate hat_exit(as->a_hat); 1317c478bd9Sstevel@tonic-gate return (-1); 1327c478bd9Sstevel@tonic-gate } 1337c478bd9Sstevel@tonic-gate as->a_vbits |= id; 1347c478bd9Sstevel@tonic-gate hat_exit(as->a_hat); 1357c478bd9Sstevel@tonic-gate (void) hat_stats_enable(as->a_hat); 1367c478bd9Sstevel@tonic-gate return (id); 1377c478bd9Sstevel@tonic-gate } 1387c478bd9Sstevel@tonic-gate 1397c478bd9Sstevel@tonic-gate /* 1407c478bd9Sstevel@tonic-gate * Record referenced and modified information for an address space. 1417c478bd9Sstevel@tonic-gate * Rmbits is a word containing the referenced bit in bit position 1 1427c478bd9Sstevel@tonic-gate * and the modified bit in bit position 0. 1437c478bd9Sstevel@tonic-gate * 1447c478bd9Sstevel@tonic-gate * For current informational uses, one can rerun any program using 1457c478bd9Sstevel@tonic-gate * this facility after modifying the hrm_blist_incr to be a larger 1467c478bd9Sstevel@tonic-gate * amount so that a larger buffer of blocks will be maintained. 1477c478bd9Sstevel@tonic-gate */ 1487c478bd9Sstevel@tonic-gate void 1497c478bd9Sstevel@tonic-gate hat_setstat(struct as *as, caddr_t addr, size_t len, uint_t rmbits) 1507c478bd9Sstevel@tonic-gate { 1517c478bd9Sstevel@tonic-gate struct hrmstat *hrm; 1527c478bd9Sstevel@tonic-gate uint_t vbits, newbits, nb; 1537c478bd9Sstevel@tonic-gate int h; 1547c478bd9Sstevel@tonic-gate 1557c478bd9Sstevel@tonic-gate ASSERT(len == PAGESIZE); 1567c478bd9Sstevel@tonic-gate ASSERT((rmbits & ~(P_MOD|P_REF)) == 0); 1577c478bd9Sstevel@tonic-gate 1587c478bd9Sstevel@tonic-gate if (rmbits == 0) 1597c478bd9Sstevel@tonic-gate return; 1607c478bd9Sstevel@tonic-gate 1617c478bd9Sstevel@tonic-gate mutex_enter(&hat_statlock); 1627c478bd9Sstevel@tonic-gate 1637c478bd9Sstevel@tonic-gate /* 1647c478bd9Sstevel@tonic-gate * Search the hash list for the as and addr we are looking for 1657c478bd9Sstevel@tonic-gate * and set the ref and mod bits in every block that matches. 1667c478bd9Sstevel@tonic-gate */ 1677c478bd9Sstevel@tonic-gate vbits = 0; 1687c478bd9Sstevel@tonic-gate h = hrm_hash(as, addr); 1697c478bd9Sstevel@tonic-gate for (hrm = hrm_hashtab[h]; hrm; hrm = hrm->hrm_hnext) { 1707c478bd9Sstevel@tonic-gate if (hrm_match(hrm, as, addr)) { 1717c478bd9Sstevel@tonic-gate hrm_setbits(hrm, addr, rmbits); 1727c478bd9Sstevel@tonic-gate vbits |= hrm->hrm_id; 1737c478bd9Sstevel@tonic-gate } 1747c478bd9Sstevel@tonic-gate } 1757c478bd9Sstevel@tonic-gate 1767c478bd9Sstevel@tonic-gate /* 1777c478bd9Sstevel@tonic-gate * If we didn't find a block for all of the enabled 1787c478bd9Sstevel@tonic-gate * vpages bits, then allocate and initialize a block 1797c478bd9Sstevel@tonic-gate * for each bit that was not found. 1807c478bd9Sstevel@tonic-gate */ 1817c478bd9Sstevel@tonic-gate if (vbits != as->a_vbits) { 1829d9461f9Strevtom newbits = (vbits ^ as->a_vbits) & as->a_vbits; 1837c478bd9Sstevel@tonic-gate while (newbits) { 1847c478bd9Sstevel@tonic-gate if (ffs(newbits)) 1857c478bd9Sstevel@tonic-gate nb = 1 << (ffs(newbits)-1); 1867c478bd9Sstevel@tonic-gate hrm = (struct hrmstat *)hrm_balloc(); 1877c478bd9Sstevel@tonic-gate if (hrm == NULL) { 1887c478bd9Sstevel@tonic-gate hrm_allocfailmsg = 1; 1897c478bd9Sstevel@tonic-gate hrm_allocfail++; 1907c478bd9Sstevel@tonic-gate mutex_exit(&hat_statlock); 1917c478bd9Sstevel@tonic-gate return; 1927c478bd9Sstevel@tonic-gate } 1937c478bd9Sstevel@tonic-gate hrm->hrm_as = as; 1947c478bd9Sstevel@tonic-gate hrm->hrm_base = (uintptr_t)addr & HRM_BASEMASK; 1957c478bd9Sstevel@tonic-gate hrm->hrm_id = nb; 1967c478bd9Sstevel@tonic-gate hrm_link(hrm); 1977c478bd9Sstevel@tonic-gate hrm_setbits(hrm, addr, rmbits); 1987c478bd9Sstevel@tonic-gate newbits &= ~nb; 1997c478bd9Sstevel@tonic-gate } 2007c478bd9Sstevel@tonic-gate } 2017c478bd9Sstevel@tonic-gate mutex_exit(&hat_statlock); 2027c478bd9Sstevel@tonic-gate } 2037c478bd9Sstevel@tonic-gate 2047c478bd9Sstevel@tonic-gate /* 2057c478bd9Sstevel@tonic-gate * Free the resources used to maintain the referenced and modified 2067c478bd9Sstevel@tonic-gate * statistics for the virtual page view of an address space 2077c478bd9Sstevel@tonic-gate * identified by id. 2087c478bd9Sstevel@tonic-gate */ 2097c478bd9Sstevel@tonic-gate void 2107c478bd9Sstevel@tonic-gate hat_freestat(struct as *as, int id) 2117c478bd9Sstevel@tonic-gate { 2129d9461f9Strevtom struct hrmstat *hrm; 2139d9461f9Strevtom struct hrmstat *prev_ahrm; 2149d9461f9Strevtom struct hrmstat *hrm_tmplist; 2159d9461f9Strevtom struct hrmstat *hrm_next; 2167c478bd9Sstevel@tonic-gate 2177c478bd9Sstevel@tonic-gate hat_stats_disable(as->a_hat); /* tell the hat layer to stop */ 2187c478bd9Sstevel@tonic-gate hat_enter(as->a_hat); 2197c478bd9Sstevel@tonic-gate if (id == 0) 2207c478bd9Sstevel@tonic-gate as->a_vbits = 0; 2217c478bd9Sstevel@tonic-gate else 2227c478bd9Sstevel@tonic-gate as->a_vbits &= ~id; 2237c478bd9Sstevel@tonic-gate 2247c478bd9Sstevel@tonic-gate if ((hrm = as->a_hrm) == NULL) { 2257c478bd9Sstevel@tonic-gate hat_exit(as->a_hat); 2267c478bd9Sstevel@tonic-gate return; 2277c478bd9Sstevel@tonic-gate } 2287c478bd9Sstevel@tonic-gate hat_exit(as->a_hat); 2297c478bd9Sstevel@tonic-gate 2307c478bd9Sstevel@tonic-gate mutex_enter(&hat_statlock); 2319d9461f9Strevtom 2327c478bd9Sstevel@tonic-gate for (prev_ahrm = NULL; hrm; hrm = hrm->hrm_anext) { 2337c478bd9Sstevel@tonic-gate if ((id == hrm->hrm_id) || (id == NULL)) { 2347c478bd9Sstevel@tonic-gate 2357c478bd9Sstevel@tonic-gate hrm_hashout(hrm); 2367c478bd9Sstevel@tonic-gate hrm->hrm_hnext = hrm_blist; 2377c478bd9Sstevel@tonic-gate hrm_blist = hrm; 2387c478bd9Sstevel@tonic-gate hrm_blist_num++; 2397c478bd9Sstevel@tonic-gate 2407c478bd9Sstevel@tonic-gate if (prev_ahrm == NULL) 2417c478bd9Sstevel@tonic-gate as->a_hrm = hrm->hrm_anext; 2427c478bd9Sstevel@tonic-gate else 2437c478bd9Sstevel@tonic-gate prev_ahrm->hrm_anext = hrm->hrm_anext; 2447c478bd9Sstevel@tonic-gate 2457c478bd9Sstevel@tonic-gate } else 2467c478bd9Sstevel@tonic-gate prev_ahrm = hrm; 2477c478bd9Sstevel@tonic-gate } 2487c478bd9Sstevel@tonic-gate 2497c478bd9Sstevel@tonic-gate /* 2507c478bd9Sstevel@tonic-gate * If all statistics blocks are free, 2517c478bd9Sstevel@tonic-gate * return the memory to the system. 2527c478bd9Sstevel@tonic-gate */ 2537c478bd9Sstevel@tonic-gate if (hrm_blist_num == hrm_blist_total) { 2547c478bd9Sstevel@tonic-gate /* zero the block list since we are giving back its memory */ 2557c478bd9Sstevel@tonic-gate hrm_blist = NULL; 2567c478bd9Sstevel@tonic-gate hrm_blist_num = 0; 2577c478bd9Sstevel@tonic-gate hrm_blist_total = 0; 2589d9461f9Strevtom hrm_tmplist = hrm_memlist; 259a359d6b1Strevtom hrm_memlist = NULL; 260a359d6b1Strevtom } else { 2619d9461f9Strevtom hrm_tmplist = NULL; 2627c478bd9Sstevel@tonic-gate } 263a359d6b1Strevtom 2647c478bd9Sstevel@tonic-gate mutex_exit(&hat_statlock); 265a359d6b1Strevtom 2667c478bd9Sstevel@tonic-gate /* 2679d9461f9Strevtom * If there are any hrmstat structures to be freed, this must only 2689d9461f9Strevtom * be done after we've released hat_statlock. 2697c478bd9Sstevel@tonic-gate */ 2709d9461f9Strevtom while (hrm_tmplist != NULL) { 2719d9461f9Strevtom hrm_next = hrm_tmplist->hrm_hnext; 2729d9461f9Strevtom kmem_free(hrm_tmplist, hrm_tmplist->hrm_base); 2739d9461f9Strevtom hrm_tmplist = hrm_next; 2749d9461f9Strevtom } 2757c478bd9Sstevel@tonic-gate } 2767c478bd9Sstevel@tonic-gate 2777c478bd9Sstevel@tonic-gate /* 2787c478bd9Sstevel@tonic-gate * Grab memory for statistics gathering of the hat layer. 2797c478bd9Sstevel@tonic-gate */ 2807c478bd9Sstevel@tonic-gate static void 2817c478bd9Sstevel@tonic-gate hrm_getblk(int chunk) 2827c478bd9Sstevel@tonic-gate { 2837c478bd9Sstevel@tonic-gate struct hrmstat *hrm, *l; 2847c478bd9Sstevel@tonic-gate int i; 2857c478bd9Sstevel@tonic-gate int hrm_incr; 2867c478bd9Sstevel@tonic-gate 2877c478bd9Sstevel@tonic-gate mutex_enter(&hat_statlock); 288*c6224917SMichael Corcoran /* 289*c6224917SMichael Corcoran * XXX The whole private freelist management here really should be 290*c6224917SMichael Corcoran * overhauled. 291*c6224917SMichael Corcoran * 292*c6224917SMichael Corcoran * The freelist should have some knowledge of how much memory is 293*c6224917SMichael Corcoran * needed by a process and thus when hat_resvstat get's called, we can 294*c6224917SMichael Corcoran * increment the freelist needs for that process within this subsystem. 295*c6224917SMichael Corcoran * Thus there will be reservations for all processes which are being 296*c6224917SMichael Corcoran * watched which should be accurate, and consume less memory overall. 297*c6224917SMichael Corcoran * 298*c6224917SMichael Corcoran * For now, just make sure there's enough entries on the freelist to 299*c6224917SMichael Corcoran * handle the current chunk. 300*c6224917SMichael Corcoran */ 3017c478bd9Sstevel@tonic-gate if ((hrm_blist == NULL) || 3027c478bd9Sstevel@tonic-gate (hrm_blist_num <= hrm_blist_lowater) || 303*c6224917SMichael Corcoran (chunk && (hrm_blist_num < chunk + hrm_blist_incr))) { 3047c478bd9Sstevel@tonic-gate mutex_exit(&hat_statlock); 3057c478bd9Sstevel@tonic-gate 306*c6224917SMichael Corcoran hrm_incr = chunk + hrm_blist_incr; 3077c478bd9Sstevel@tonic-gate hrm = kmem_zalloc(sizeof (struct hrmstat) * hrm_incr, KM_SLEEP); 3087c478bd9Sstevel@tonic-gate hrm->hrm_base = sizeof (struct hrmstat) * hrm_incr; 3097c478bd9Sstevel@tonic-gate 3107c478bd9Sstevel@tonic-gate /* 3117c478bd9Sstevel@tonic-gate * thread the allocated blocks onto a freelist 3127c478bd9Sstevel@tonic-gate * using the first block to hold information for 3137c478bd9Sstevel@tonic-gate * freeing them all later 3147c478bd9Sstevel@tonic-gate */ 3157c478bd9Sstevel@tonic-gate mutex_enter(&hat_statlock); 3167c478bd9Sstevel@tonic-gate hrm->hrm_hnext = hrm_memlist; 3177c478bd9Sstevel@tonic-gate hrm_memlist = hrm; 3187c478bd9Sstevel@tonic-gate 3197c478bd9Sstevel@tonic-gate hrm_blist_total += (hrm_incr - 1); 3207c478bd9Sstevel@tonic-gate for (i = 1; i < hrm_incr; i++) { 3217c478bd9Sstevel@tonic-gate l = &hrm[i]; 3227c478bd9Sstevel@tonic-gate l->hrm_hnext = hrm_blist; 3237c478bd9Sstevel@tonic-gate hrm_blist = l; 3247c478bd9Sstevel@tonic-gate hrm_blist_num++; 3257c478bd9Sstevel@tonic-gate } 3267c478bd9Sstevel@tonic-gate } 3277c478bd9Sstevel@tonic-gate mutex_exit(&hat_statlock); 3287c478bd9Sstevel@tonic-gate } 3297c478bd9Sstevel@tonic-gate 3307c478bd9Sstevel@tonic-gate static void 3317c478bd9Sstevel@tonic-gate hrm_hashin(struct hrmstat *hrm) 3327c478bd9Sstevel@tonic-gate { 3337c478bd9Sstevel@tonic-gate int h; 3347c478bd9Sstevel@tonic-gate 3357c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&hat_statlock)); 3367c478bd9Sstevel@tonic-gate h = hrm_hash(hrm->hrm_as, hrm->hrm_base); 3377c478bd9Sstevel@tonic-gate 3387c478bd9Sstevel@tonic-gate hrm->hrm_hnext = hrm_hashtab[h]; 3397c478bd9Sstevel@tonic-gate hrm_hashtab[h] = hrm; 3407c478bd9Sstevel@tonic-gate } 3417c478bd9Sstevel@tonic-gate 3427c478bd9Sstevel@tonic-gate static void 3437c478bd9Sstevel@tonic-gate hrm_hashout(struct hrmstat *hrm) 3447c478bd9Sstevel@tonic-gate { 3457c478bd9Sstevel@tonic-gate struct hrmstat *list, **prev_hrm; 3467c478bd9Sstevel@tonic-gate int h; 3477c478bd9Sstevel@tonic-gate 3487c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&hat_statlock)); 3497c478bd9Sstevel@tonic-gate h = hrm_hash(hrm->hrm_as, hrm->hrm_base); 3507c478bd9Sstevel@tonic-gate list = hrm_hashtab[h]; 3517c478bd9Sstevel@tonic-gate prev_hrm = &hrm_hashtab[h]; 3527c478bd9Sstevel@tonic-gate 3537c478bd9Sstevel@tonic-gate while (list) { 3547c478bd9Sstevel@tonic-gate if (list == hrm) { 3557c478bd9Sstevel@tonic-gate *prev_hrm = list->hrm_hnext; 3567c478bd9Sstevel@tonic-gate return; 3577c478bd9Sstevel@tonic-gate } 3587c478bd9Sstevel@tonic-gate prev_hrm = &list->hrm_hnext; 3597c478bd9Sstevel@tonic-gate list = list->hrm_hnext; 3607c478bd9Sstevel@tonic-gate } 3617c478bd9Sstevel@tonic-gate } 3627c478bd9Sstevel@tonic-gate 3637c478bd9Sstevel@tonic-gate 3647c478bd9Sstevel@tonic-gate /* 3657c478bd9Sstevel@tonic-gate * Link a statistic block into an address space and also put it 3667c478bd9Sstevel@tonic-gate * on the hash list for future references. 3677c478bd9Sstevel@tonic-gate */ 3687c478bd9Sstevel@tonic-gate static void 3697c478bd9Sstevel@tonic-gate hrm_link(struct hrmstat *hrm) 3707c478bd9Sstevel@tonic-gate { 3717c478bd9Sstevel@tonic-gate struct as *as = hrm->hrm_as; 3727c478bd9Sstevel@tonic-gate 3737c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&hat_statlock)); 3747c478bd9Sstevel@tonic-gate hrm->hrm_anext = as->a_hrm; 3757c478bd9Sstevel@tonic-gate as->a_hrm = hrm; 3767c478bd9Sstevel@tonic-gate hrm_hashin(hrm); 3777c478bd9Sstevel@tonic-gate } 3787c478bd9Sstevel@tonic-gate 3797c478bd9Sstevel@tonic-gate /* 3807c478bd9Sstevel@tonic-gate * Allocate a block for statistics keeping. 3817c478bd9Sstevel@tonic-gate * Returns NULL if blocks are unavailable. 3827c478bd9Sstevel@tonic-gate */ 3837c478bd9Sstevel@tonic-gate static struct hrmstat * 3847c478bd9Sstevel@tonic-gate hrm_balloc(void) 3857c478bd9Sstevel@tonic-gate { 3867c478bd9Sstevel@tonic-gate struct hrmstat *hrm; 3877c478bd9Sstevel@tonic-gate 3887c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&hat_statlock)); 3897c478bd9Sstevel@tonic-gate 3907c478bd9Sstevel@tonic-gate hrm = hrm_blist; 3917c478bd9Sstevel@tonic-gate if (hrm != NULL) { 3927c478bd9Sstevel@tonic-gate hrm_blist = hrm->hrm_hnext; 3937c478bd9Sstevel@tonic-gate hrm_blist_num--; 3947c478bd9Sstevel@tonic-gate hrm->hrm_hnext = NULL; 3957c478bd9Sstevel@tonic-gate } 3967c478bd9Sstevel@tonic-gate return (hrm); 3977c478bd9Sstevel@tonic-gate } 3987c478bd9Sstevel@tonic-gate 3997c478bd9Sstevel@tonic-gate /* 4007c478bd9Sstevel@tonic-gate * Set the ref and mod bits for addr within statistics block hrm. 4017c478bd9Sstevel@tonic-gate */ 4027c478bd9Sstevel@tonic-gate static void 4037c478bd9Sstevel@tonic-gate hrm_setbits(struct hrmstat *hrm, caddr_t addr, uint_t bits) 4047c478bd9Sstevel@tonic-gate { 4057c478bd9Sstevel@tonic-gate uint_t po, bo, spb; 4067c478bd9Sstevel@tonic-gate uint_t nbits; 4077c478bd9Sstevel@tonic-gate 4087c478bd9Sstevel@tonic-gate po = ((uintptr_t)addr & HRM_BASEOFFSET) >> MMU_PAGESHIFT; /* pg off */ 4097c478bd9Sstevel@tonic-gate bo = po / (NBBY / 2); /* which byte in bit array */ 4107c478bd9Sstevel@tonic-gate spb = (3 - (po & 3)) * 2; /* shift position within byte */ 4117c478bd9Sstevel@tonic-gate nbits = bits << spb; /* bit mask */ 4127c478bd9Sstevel@tonic-gate hrm->hrm_bits[bo] |= nbits; 4137c478bd9Sstevel@tonic-gate } 4147c478bd9Sstevel@tonic-gate 4157c478bd9Sstevel@tonic-gate /* 4167c478bd9Sstevel@tonic-gate * Return collected statistics about an address space. 4177c478bd9Sstevel@tonic-gate * If clearflag is set, atomically read and zero the bits. 4187c478bd9Sstevel@tonic-gate * 4197c478bd9Sstevel@tonic-gate * Fill in the data array supplied with the referenced and 4207c478bd9Sstevel@tonic-gate * modified bits collected for address range [addr ... addr + len] 4217c478bd9Sstevel@tonic-gate * in address space, as, uniquely identified by id. 4227c478bd9Sstevel@tonic-gate * The destination is a byte array. We fill in three bits per byte: 4237c478bd9Sstevel@tonic-gate * referenced, modified, and hwmapped bits. 4247c478bd9Sstevel@tonic-gate * Kernel only interface, can't fault on destination data array. 4257c478bd9Sstevel@tonic-gate * 4267c478bd9Sstevel@tonic-gate */ 4277c478bd9Sstevel@tonic-gate void 4287c478bd9Sstevel@tonic-gate hat_getstat(struct as *as, caddr_t addr, size_t len, uint_t id, 4297c478bd9Sstevel@tonic-gate caddr_t datap, int clearflag) 4307c478bd9Sstevel@tonic-gate { 4317c478bd9Sstevel@tonic-gate size_t np; /* number of pages */ 4327c478bd9Sstevel@tonic-gate caddr_t a; 4337c478bd9Sstevel@tonic-gate char *dp; 4347c478bd9Sstevel@tonic-gate 4357c478bd9Sstevel@tonic-gate np = btop(len); 4367c478bd9Sstevel@tonic-gate bzero(datap, np); 4377c478bd9Sstevel@tonic-gate 438*c6224917SMichael Corcoran /* allocate enough statistics blocks to cover the len passed in */ 439*c6224917SMichael Corcoran hrm_getblk(np / HRM_PAGES); 440*c6224917SMichael Corcoran 4417c478bd9Sstevel@tonic-gate hat_sync(as->a_hat, addr, len, clearflag); 4427c478bd9Sstevel@tonic-gate 4437c478bd9Sstevel@tonic-gate /* allocate more statistics blocks if needed */ 4447c478bd9Sstevel@tonic-gate hrm_getblk(0); 4457c478bd9Sstevel@tonic-gate 4467c478bd9Sstevel@tonic-gate mutex_enter(&hat_statlock); 4477c478bd9Sstevel@tonic-gate if (hrm_hashtab == NULL) { 4487c478bd9Sstevel@tonic-gate /* can happen when victim process exits */ 4497c478bd9Sstevel@tonic-gate mutex_exit(&hat_statlock); 4507c478bd9Sstevel@tonic-gate return; 4517c478bd9Sstevel@tonic-gate } 4527c478bd9Sstevel@tonic-gate dp = datap; 4537c478bd9Sstevel@tonic-gate a = (caddr_t)((uintptr_t)addr & (uintptr_t)PAGEMASK); 4547c478bd9Sstevel@tonic-gate while (a < addr + len) { 4557c478bd9Sstevel@tonic-gate struct hrmstat *hrm; 4567c478bd9Sstevel@tonic-gate size_t n; /* number of pages, temp */ 4577c478bd9Sstevel@tonic-gate int h; /* hash index */ 4587c478bd9Sstevel@tonic-gate uint_t po; 4597c478bd9Sstevel@tonic-gate 4607c478bd9Sstevel@tonic-gate h = hrm_hash(as, a); 4617c478bd9Sstevel@tonic-gate n = (HRM_PAGES - 4627c478bd9Sstevel@tonic-gate (((uintptr_t)a & HRM_PAGEMASK) >> MMU_PAGESHIFT)); 4637c478bd9Sstevel@tonic-gate if (n > np) 4647c478bd9Sstevel@tonic-gate n = np; 4657c478bd9Sstevel@tonic-gate po = ((uintptr_t)a & HRM_BASEOFFSET) >> MMU_PAGESHIFT; 4667c478bd9Sstevel@tonic-gate 4677c478bd9Sstevel@tonic-gate for (hrm = hrm_hashtab[h]; hrm; hrm = hrm->hrm_hnext) { 4687c478bd9Sstevel@tonic-gate if (hrm->hrm_as == as && 4697c478bd9Sstevel@tonic-gate hrm->hrm_base == ((uintptr_t)a & HRM_BASEMASK) && 4707c478bd9Sstevel@tonic-gate id == hrm->hrm_id) { 4717c478bd9Sstevel@tonic-gate int i, nr; 4727c478bd9Sstevel@tonic-gate uint_t bo, spb; 4737c478bd9Sstevel@tonic-gate 4747c478bd9Sstevel@tonic-gate /* 4757c478bd9Sstevel@tonic-gate * Extract leading unaligned bits. 4767c478bd9Sstevel@tonic-gate */ 4777c478bd9Sstevel@tonic-gate i = 0; 4787c478bd9Sstevel@tonic-gate while (i < n && (po & 3)) { 4797c478bd9Sstevel@tonic-gate bo = po / (NBBY / 2); 4807c478bd9Sstevel@tonic-gate spb = (3 - (po & 3)) * 2; 4817c478bd9Sstevel@tonic-gate *dp++ |= (hrm->hrm_bits[bo] >> spb) & 3; 4827c478bd9Sstevel@tonic-gate if (clearflag) 4837c478bd9Sstevel@tonic-gate hrm->hrm_bits[bo] &= ~(3<<spb); 4847c478bd9Sstevel@tonic-gate po++; 4857c478bd9Sstevel@tonic-gate i++; 4867c478bd9Sstevel@tonic-gate } 4877c478bd9Sstevel@tonic-gate /* 4887c478bd9Sstevel@tonic-gate * Extract aligned bits. 4897c478bd9Sstevel@tonic-gate */ 4907c478bd9Sstevel@tonic-gate nr = n/4*4; 4917c478bd9Sstevel@tonic-gate bo = po / (NBBY / 2); 4927c478bd9Sstevel@tonic-gate while (i < nr) { 4937c478bd9Sstevel@tonic-gate int bits = hrm->hrm_bits[bo]; 4947c478bd9Sstevel@tonic-gate *dp++ |= (bits >> 6) & 3; 4957c478bd9Sstevel@tonic-gate *dp++ |= (bits >> 4) & 3; 4967c478bd9Sstevel@tonic-gate *dp++ |= (bits >> 2) & 3; 4977c478bd9Sstevel@tonic-gate *dp++ |= (bits >> 0) & 3; 4987c478bd9Sstevel@tonic-gate if (clearflag) 4997c478bd9Sstevel@tonic-gate hrm->hrm_bits[bo] = 0; 5007c478bd9Sstevel@tonic-gate bo++; 5017c478bd9Sstevel@tonic-gate po += 4; 5027c478bd9Sstevel@tonic-gate i += 4; 5037c478bd9Sstevel@tonic-gate } 5047c478bd9Sstevel@tonic-gate /* 5057c478bd9Sstevel@tonic-gate * Extract trailing unaligned bits. 5067c478bd9Sstevel@tonic-gate */ 5077c478bd9Sstevel@tonic-gate while (i < n) { 5087c478bd9Sstevel@tonic-gate bo = po / (NBBY / 2); 5097c478bd9Sstevel@tonic-gate spb = (3 - (po & 3)) * 2; 5107c478bd9Sstevel@tonic-gate *dp++ |= (hrm->hrm_bits[bo] >> spb) & 3; 5117c478bd9Sstevel@tonic-gate if (clearflag) 5127c478bd9Sstevel@tonic-gate hrm->hrm_bits[bo] &= ~(3<<spb); 5137c478bd9Sstevel@tonic-gate po++; 5147c478bd9Sstevel@tonic-gate i++; 5157c478bd9Sstevel@tonic-gate } 5167c478bd9Sstevel@tonic-gate 5177c478bd9Sstevel@tonic-gate break; 5187c478bd9Sstevel@tonic-gate } 5197c478bd9Sstevel@tonic-gate } 5207c478bd9Sstevel@tonic-gate if (hrm == NULL) 5217c478bd9Sstevel@tonic-gate dp += n; 5227c478bd9Sstevel@tonic-gate np -= n; 5237c478bd9Sstevel@tonic-gate a += n * MMU_PAGESIZE; 5247c478bd9Sstevel@tonic-gate } 5257c478bd9Sstevel@tonic-gate mutex_exit(&hat_statlock); 5267c478bd9Sstevel@tonic-gate } 527