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