Home | History | Annotate | Download | only in vm
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #pragma ident	"@(#)hat_refmod.c	1.38	06/11/27 SMI"
     27 
     28 /*
     29  * The following routines implement the hat layer's
     30  * recording of the referenced and modified bits.
     31  */
     32 
     33 #include <sys/types.h>
     34 #include <sys/param.h>
     35 #include <sys/systm.h>
     36 #include <sys/debug.h>
     37 #include <sys/kmem.h>
     38 
     39 /*
     40  * Note, usage of cmn_err requires you not hold any hat layer locks.
     41  */
     42 #include <sys/cmn_err.h>
     43 
     44 #include <vm/as.h>
     45 #include <vm/hat.h>
     46 
     47 kmutex_t hat_statlock;		/* protects all hat statistics data */
     48 struct hrmstat *hrm_memlist;	/* tracks memory alloced for hrm_blist blocks */
     49 struct hrmstat **hrm_hashtab;	/* hash table for finding blocks quickly */
     50 struct hrmstat *hrm_blist;
     51 int hrm_blist_incr = HRM_BLIST_INCR;
     52 int hrm_blist_lowater = HRM_BLIST_INCR/2;
     53 int hrm_blist_num = 0;
     54 int hrm_blist_total = 0;
     55 int hrm_mlockinited = 0;
     56 int hrm_allocfailmsg = 0;	/* print a message when allocations fail */
     57 int hrm_allocfail = 0;
     58 
     59 static struct hrmstat	*hrm_balloc(void);
     60 static void	hrm_link(struct hrmstat *);
     61 static void	hrm_setbits(struct hrmstat *, caddr_t, uint_t);
     62 static void	hrm_hashout(struct hrmstat *);
     63 static void	hrm_getblk(int);
     64 
     65 #define	hrm_hash(as, addr) \
     66 	(HRM_HASHMASK & \
     67 	(((uintptr_t)(addr) >> HRM_BASESHIFT) ^ ((uintptr_t)(as) >> 2)))
     68 
     69 #define	hrm_match(hrm, as, addr) \
     70 	(((hrm)->hrm_as == (as) && \
     71 	((hrm)->hrm_base == ((uintptr_t)(addr) & HRM_BASEMASK))) ? 1 : 0)
     72 
     73 /*
     74  * reserve enough statistic blocks for
     75  * chunk of bytes (pages) in a given as.
     76  */
     77 /* ARGSUSED */
     78 void
     79 hat_resvstat(size_t chunk, struct as *as, caddr_t addr)
     80 {
     81 	int nhrm = btop(chunk)/HRM_PAGES;
     82 
     83 	if (nhrm < HRM_BLIST_INCR)
     84 		nhrm = 0;	/* preallocate at least HRM_BLIST_INCR */
     85 	hrm_getblk(nhrm);
     86 }
     87 
     88 /*
     89  * Start the statistics gathering for an address space.
     90  * Return -1 if we can't do it, otherwise return an opaque
     91  * identifier to be used when querying for the gathered statistics.
     92  * The identifier is an unused bit in a_vbits.
     93  * Bit 0 is reserved for swsmon.
     94  */
     95 int
     96 hat_startstat(struct as *as)
     97 {
     98 	uint_t nbits;		/* number of bits */
     99 	uint_t bn;		/* bit number */
    100 	uint_t id;		/* new vbit, identifier */
    101 	uint_t vbits;		/* used vbits of address space */
    102 	size_t chunk;		/* mapped size for stats */
    103 
    104 	/*
    105 	 * If the refmod saving memory allocator runs out, print
    106 	 * a warning message about how to fix it, see comment at
    107 	 * the beginning of hat_setstat.
    108 	 */
    109 	if (hrm_allocfailmsg) {
    110 		cmn_err(CE_WARN,
    111 		    "hrm_balloc failures occured, increase hrm_blist_incr");
    112 		hrm_allocfailmsg = 0;
    113 	}
    114 
    115 	/*
    116 	 * Verify that a buffer of statistics blocks exists
    117 	 * and allocate more, if needed.
    118 	 */
    119 
    120 	chunk = hat_get_mapped_size(as->a_hat);
    121 	chunk = (btop(chunk)/HRM_PAGES);
    122 	if (chunk < HRM_BLIST_INCR)
    123 		chunk = 0;
    124 
    125 	hrm_getblk((int)chunk);
    126 
    127 	/*
    128 	 * Find a unused id in the given address space.
    129 	 */
    130 	hat_enter(as->a_hat);
    131 	vbits = as->a_vbits;
    132 	nbits = sizeof (as->a_vbits) * NBBY;
    133 	for (bn = 1, id = 2; bn < (nbits - 1); bn++, id <<= 1)
    134 		if ((id & vbits) == 0)
    135 			break;
    136 	if (bn >= (nbits - 1)) {
    137 		hat_exit(as->a_hat);
    138 		return (-1);
    139 	}
    140 	as->a_vbits |= id;
    141 	hat_exit(as->a_hat);
    142 	(void) hat_stats_enable(as->a_hat);
    143 	return (id);
    144 }
    145 
    146 /*
    147  * Record referenced and modified information for an address space.
    148  * Rmbits is a word containing the referenced bit in bit position 1
    149  * and the modified bit in bit position 0.
    150  *
    151  * For current informational uses, one can rerun any program using
    152  * this facility after modifying the hrm_blist_incr to be a larger
    153  * amount so that a larger buffer of blocks will be maintained.
    154  */
    155 void
    156 hat_setstat(struct as *as, caddr_t addr, size_t len, uint_t rmbits)
    157 {
    158 	struct hrmstat	*hrm;
    159 	uint_t		vbits, newbits, nb;
    160 	int		h;
    161 
    162 	ASSERT(len == PAGESIZE);
    163 	ASSERT((rmbits & ~(P_MOD|P_REF)) == 0);
    164 
    165 	if (rmbits == 0)
    166 		return;
    167 
    168 	mutex_enter(&hat_statlock);
    169 
    170 	/*
    171 	 * Search the hash list for the as and addr we are looking for
    172 	 * and set the ref and mod bits in every block that matches.
    173 	 */
    174 	vbits = 0;
    175 	h = hrm_hash(as, addr);
    176 	for (hrm = hrm_hashtab[h]; hrm; hrm = hrm->hrm_hnext) {
    177 		if (hrm_match(hrm, as, addr)) {
    178 			hrm_setbits(hrm, addr, rmbits);
    179 			vbits |= hrm->hrm_id;
    180 		}
    181 	}
    182 
    183 	/*
    184 	 * If we didn't find a block for all of the enabled
    185 	 * vpages bits, then allocate and initialize a block
    186 	 * for each bit that was not found.
    187 	 */
    188 	if (vbits != as->a_vbits) {
    189 		newbits = (vbits ^ as->a_vbits) & as->a_vbits;
    190 		while (newbits) {
    191 			if (ffs(newbits))
    192 				nb = 1 << (ffs(newbits)-1);
    193 			hrm = (struct hrmstat *)hrm_balloc();
    194 			if (hrm == NULL) {
    195 				hrm_allocfailmsg = 1;
    196 				hrm_allocfail++;
    197 				mutex_exit(&hat_statlock);
    198 				return;
    199 			}
    200 			hrm->hrm_as = as;
    201 			hrm->hrm_base = (uintptr_t)addr & HRM_BASEMASK;
    202 			hrm->hrm_id = nb;
    203 			hrm_link(hrm);
    204 			hrm_setbits(hrm, addr, rmbits);
    205 			newbits &= ~nb;
    206 		}
    207 	}
    208 	mutex_exit(&hat_statlock);
    209 }
    210 
    211 /*
    212  * Free the resources used to maintain the referenced and modified
    213  * statistics for the virtual page view of an address space
    214  * identified by id.
    215  */
    216 void
    217 hat_freestat(struct as *as, int id)
    218 {
    219 	struct hrmstat *hrm;
    220 	struct hrmstat *prev_ahrm;
    221 	struct hrmstat *hrm_tmplist;
    222 	struct hrmstat *hrm_next;
    223 
    224 	hat_stats_disable(as->a_hat);	/* tell the hat layer to stop */
    225 	hat_enter(as->a_hat);
    226 	if (id == 0)
    227 		as->a_vbits = 0;
    228 	else
    229 		as->a_vbits &= ~id;
    230 
    231 	if ((hrm = as->a_hrm) == NULL) {
    232 		hat_exit(as->a_hat);
    233 		return;
    234 	}
    235 	hat_exit(as->a_hat);
    236 
    237 	mutex_enter(&hat_statlock);
    238 
    239 	for (prev_ahrm = NULL; hrm; hrm = hrm->hrm_anext) {
    240 		if ((id == hrm->hrm_id) || (id == NULL)) {
    241 
    242 			hrm_hashout(hrm);
    243 			hrm->hrm_hnext = hrm_blist;
    244 			hrm_blist = hrm;
    245 			hrm_blist_num++;
    246 
    247 			if (prev_ahrm == NULL)
    248 				as->a_hrm = hrm->hrm_anext;
    249 			else
    250 				prev_ahrm->hrm_anext = hrm->hrm_anext;
    251 
    252 		} else
    253 			prev_ahrm = hrm;
    254 	}
    255 
    256 	/*
    257 	 * If all statistics blocks are free,
    258 	 * return the memory to the system.
    259 	 */
    260 	if (hrm_blist_num == hrm_blist_total) {
    261 		/* zero the block list since we are giving back its memory */
    262 		hrm_blist = NULL;
    263 		hrm_blist_num = 0;
    264 		hrm_blist_total = 0;
    265 		hrm_tmplist = hrm_memlist;
    266 		hrm_memlist = NULL;
    267 	} else {
    268 		hrm_tmplist = NULL;
    269 	}
    270 
    271 	mutex_exit(&hat_statlock);
    272 
    273 	/*
    274 	 * If there are any hrmstat structures to be freed, this must only
    275 	 * be done after we've released hat_statlock.
    276 	 */
    277 	while (hrm_tmplist != NULL) {
    278 		hrm_next = hrm_tmplist->hrm_hnext;
    279 		kmem_free(hrm_tmplist, hrm_tmplist->hrm_base);
    280 		hrm_tmplist = hrm_next;
    281 	}
    282 }
    283 
    284 /*
    285  * Grab memory for statistics gathering of the hat layer.
    286  */
    287 static void
    288 hrm_getblk(int chunk)
    289 {
    290 	struct hrmstat *hrm, *l;
    291 	int i;
    292 	int hrm_incr;
    293 
    294 	mutex_enter(&hat_statlock);
    295 	if ((hrm_blist == NULL) ||
    296 	    (hrm_blist_num <= hrm_blist_lowater) ||
    297 	    (chunk && (hrm_blist_num < chunk))) {
    298 
    299 		mutex_exit(&hat_statlock);
    300 
    301 		hrm_incr = chunk? chunk : hrm_blist_incr;
    302 		hrm = kmem_zalloc(sizeof (struct hrmstat) * hrm_incr, KM_SLEEP);
    303 		hrm->hrm_base = sizeof (struct hrmstat) * hrm_incr;
    304 
    305 		/*
    306 		 * thread the allocated blocks onto a freelist
    307 		 * using the first block to hold information for
    308 		 * freeing them all later
    309 		 */
    310 		mutex_enter(&hat_statlock);
    311 		hrm->hrm_hnext = hrm_memlist;
    312 		hrm_memlist = hrm;
    313 
    314 		hrm_blist_total += (hrm_incr - 1);
    315 		for (i = 1; i < hrm_incr; i++) {
    316 			l = &hrm[i];
    317 			l->hrm_hnext = hrm_blist;
    318 			hrm_blist = l;
    319 			hrm_blist_num++;
    320 		}
    321 	}
    322 	mutex_exit(&hat_statlock);
    323 }
    324 
    325 static void
    326 hrm_hashin(struct hrmstat *hrm)
    327 {
    328 	int 		h;
    329 
    330 	ASSERT(MUTEX_HELD(&hat_statlock));
    331 	h = hrm_hash(hrm->hrm_as, hrm->hrm_base);
    332 
    333 	hrm->hrm_hnext = hrm_hashtab[h];
    334 	hrm_hashtab[h] = hrm;
    335 }
    336 
    337 static void
    338 hrm_hashout(struct hrmstat *hrm)
    339 {
    340 	struct hrmstat	*list, **prev_hrm;
    341 	int		h;
    342 
    343 	ASSERT(MUTEX_HELD(&hat_statlock));
    344 	h = hrm_hash(hrm->hrm_as, hrm->hrm_base);
    345 	list = hrm_hashtab[h];
    346 	prev_hrm = &hrm_hashtab[h];
    347 
    348 	while (list) {
    349 		if (list == hrm) {
    350 			*prev_hrm = list->hrm_hnext;
    351 			return;
    352 		}
    353 		prev_hrm = &list->hrm_hnext;
    354 		list = list->hrm_hnext;
    355 	}
    356 }
    357 
    358 
    359 /*
    360  * Link a statistic block into an address space and also put it
    361  * on the hash list for future references.
    362  */
    363 static void
    364 hrm_link(struct hrmstat *hrm)
    365 {
    366 	struct as *as = hrm->hrm_as;
    367 
    368 	ASSERT(MUTEX_HELD(&hat_statlock));
    369 	hrm->hrm_anext = as->a_hrm;
    370 	as->a_hrm = hrm;
    371 	hrm_hashin(hrm);
    372 }
    373 
    374 /*
    375  * Allocate a block for statistics keeping.
    376  * Returns NULL if blocks are unavailable.
    377  */
    378 static struct hrmstat *
    379 hrm_balloc(void)
    380 {
    381 	struct hrmstat *hrm;
    382 
    383 	ASSERT(MUTEX_HELD(&hat_statlock));
    384 
    385 	hrm = hrm_blist;
    386 	if (hrm != NULL) {
    387 		hrm_blist = hrm->hrm_hnext;
    388 		hrm_blist_num--;
    389 		hrm->hrm_hnext = NULL;
    390 	}
    391 	return (hrm);
    392 }
    393 
    394 /*
    395  * Set the ref and mod bits for addr within statistics block hrm.
    396  */
    397 static void
    398 hrm_setbits(struct hrmstat *hrm, caddr_t addr, uint_t bits)
    399 {
    400 	uint_t po, bo, spb;
    401 	uint_t nbits;
    402 
    403 	po = ((uintptr_t)addr & HRM_BASEOFFSET) >> MMU_PAGESHIFT; /* pg off */
    404 	bo = po / (NBBY / 2);			/* which byte in bit array */
    405 	spb = (3 - (po & 3)) * 2;		/* shift position within byte */
    406 	nbits = bits << spb;			/* bit mask */
    407 	hrm->hrm_bits[bo] |= nbits;
    408 }
    409 
    410 /*
    411  * Return collected statistics about an address space.
    412  * If clearflag is set, atomically read and zero the bits.
    413  *
    414  * Fill in the data array supplied with the referenced and
    415  * modified bits collected for address range [addr ... addr + len]
    416  * in address space, as, uniquely identified by id.
    417  * The destination is a byte array.  We fill in three bits per byte:
    418  * referenced, modified, and hwmapped bits.
    419  * Kernel only interface, can't fault on destination data array.
    420  *
    421  */
    422 void
    423 hat_getstat(struct as *as, caddr_t addr, size_t len, uint_t id,
    424     caddr_t datap, int clearflag)
    425 {
    426 	size_t	np;		/* number of pages */
    427 	caddr_t	a;
    428 	char 	*dp;
    429 
    430 	np = btop(len);
    431 	bzero(datap, np);
    432 
    433 	hat_sync(as->a_hat, addr, len, clearflag);
    434 
    435 	/* allocate more statistics blocks if needed */
    436 	hrm_getblk(0);
    437 
    438 	mutex_enter(&hat_statlock);
    439 	if (hrm_hashtab == NULL) {
    440 		/* can happen when victim process exits */
    441 		mutex_exit(&hat_statlock);
    442 		return;
    443 	}
    444 	dp = datap;
    445 	a = (caddr_t)((uintptr_t)addr & (uintptr_t)PAGEMASK);
    446 	while (a < addr + len) {
    447 		struct hrmstat	*hrm;
    448 		size_t	n;		/* number of pages, temp */
    449 		int	h;		/* hash index */
    450 		uint_t	po;
    451 
    452 		h = hrm_hash(as, a);
    453 		n = (HRM_PAGES -
    454 			(((uintptr_t)a & HRM_PAGEMASK) >> MMU_PAGESHIFT));
    455 		if (n > np)
    456 			n = np;
    457 		po = ((uintptr_t)a & HRM_BASEOFFSET) >> MMU_PAGESHIFT;
    458 
    459 		for (hrm = hrm_hashtab[h]; hrm; hrm = hrm->hrm_hnext) {
    460 			if (hrm->hrm_as == as &&
    461 			    hrm->hrm_base == ((uintptr_t)a & HRM_BASEMASK) &&
    462 			    id == hrm->hrm_id) {
    463 				int i, nr;
    464 				uint_t bo, spb;
    465 
    466 				/*
    467 				 * Extract leading unaligned bits.
    468 				 */
    469 				i = 0;
    470 				while (i < n && (po & 3)) {
    471 					bo = po / (NBBY / 2);
    472 					spb = (3 - (po & 3)) * 2;
    473 					*dp++ |= (hrm->hrm_bits[bo] >> spb) & 3;
    474 					if (clearflag)
    475 						hrm->hrm_bits[bo] &= ~(3<<spb);
    476 					po++;
    477 					i++;
    478 				}
    479 				/*
    480 				 * Extract aligned bits.
    481 				 */
    482 				nr = n/4*4;
    483 				bo = po / (NBBY / 2);
    484 				while (i < nr) {
    485 					int bits = hrm->hrm_bits[bo];
    486 					*dp++ |= (bits >> 6) & 3;
    487 					*dp++ |= (bits >> 4) & 3;
    488 					*dp++ |= (bits >> 2) & 3;
    489 					*dp++ |= (bits >> 0) & 3;
    490 					if (clearflag)
    491 						hrm->hrm_bits[bo] = 0;
    492 					bo++;
    493 					po += 4;
    494 					i += 4;
    495 				}
    496 				/*
    497 				 * Extract trailing unaligned bits.
    498 				 */
    499 				while (i < n) {
    500 					bo = po / (NBBY / 2);
    501 					spb = (3 - (po & 3)) * 2;
    502 					*dp++ |= (hrm->hrm_bits[bo] >> spb) & 3;
    503 					if (clearflag)
    504 						hrm->hrm_bits[bo] &= ~(3<<spb);
    505 					po++;
    506 					i++;
    507 				}
    508 
    509 				break;
    510 			}
    511 		}
    512 		if (hrm == NULL)
    513 			dp += n;
    514 		np -= n;
    515 		a += n * MMU_PAGESIZE;
    516 	}
    517 	mutex_exit(&hat_statlock);
    518 }
    519