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.
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.
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]
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * The following routines implement the hat layer's
28 * recording of the referenced and modified bits.
31 #include <sys/types.h>
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/debug.h>
38 * Note, usage of cmn_err requires you not hold any hat layer locks.
40 #include <sys/cmn_err.h>
45 kmutex_t hat_statlock
; /* protects all hat statistics data */
46 struct hrmstat
*hrm_memlist
; /* tracks memory alloced for hrm_blist blocks */
47 struct hrmstat
**hrm_hashtab
; /* hash table for finding blocks quickly */
48 struct hrmstat
*hrm_blist
;
49 int hrm_blist_incr
= HRM_BLIST_INCR
;
50 int hrm_blist_lowater
= HRM_BLIST_INCR
/2;
51 int hrm_blist_num
= 0;
52 int hrm_blist_total
= 0;
53 int hrm_mlockinited
= 0;
54 int hrm_allocfailmsg
= 0; /* print a message when allocations fail */
55 int hrm_allocfail
= 0;
57 static struct hrmstat
*hrm_balloc(void);
58 static void hrm_link(struct hrmstat
*);
59 static void hrm_setbits(struct hrmstat
*, caddr_t
, uint_t
);
60 static void hrm_hashout(struct hrmstat
*);
61 static void hrm_getblk(int);
63 #define hrm_hash(as, addr) \
65 (((uintptr_t)(addr) >> HRM_BASESHIFT) ^ ((uintptr_t)(as) >> 2)))
67 #define hrm_match(hrm, as, addr) \
68 (((hrm)->hrm_as == (as) && \
69 ((hrm)->hrm_base == ((uintptr_t)(addr) & HRM_BASEMASK))) ? 1 : 0)
72 * Called when an address space maps in more pages while stats are being
77 hat_resvstat(size_t chunk
, struct as
*as
, caddr_t addr
)
82 * Start the statistics gathering for an address space.
83 * Return -1 if we can't do it, otherwise return an opaque
84 * identifier to be used when querying for the gathered statistics.
85 * The identifier is an unused bit in a_vbits.
86 * Bit 0 is reserved for swsmon.
89 hat_startstat(struct as
*as
)
91 uint_t nbits
; /* number of bits */
92 uint_t bn
; /* bit number */
93 uint_t id
; /* new vbit, identifier */
94 uint_t vbits
; /* used vbits of address space */
95 size_t chunk
; /* mapped size for stats */
98 * If the refmod saving memory allocator runs out, print
99 * a warning message about how to fix it, see comment at
100 * the beginning of hat_setstat.
102 if (hrm_allocfailmsg
) {
104 "hrm_balloc failures occured, increase hrm_blist_incr");
105 hrm_allocfailmsg
= 0;
109 * Verify that a buffer of statistics blocks exists
110 * and allocate more, if needed.
113 chunk
= hat_get_mapped_size(as
->a_hat
);
114 chunk
= (btop(chunk
)/HRM_PAGES
);
115 if (chunk
< HRM_BLIST_INCR
)
118 hrm_getblk((int)chunk
);
121 * Find a unused id in the given address space.
123 hat_enter(as
->a_hat
);
125 nbits
= sizeof (as
->a_vbits
) * NBBY
;
126 for (bn
= 1, id
= 2; bn
< (nbits
- 1); bn
++, id
<<= 1)
127 if ((id
& vbits
) == 0)
129 if (bn
>= (nbits
- 1)) {
135 (void) hat_stats_enable(as
->a_hat
);
140 * Record referenced and modified information for an address space.
141 * Rmbits is a word containing the referenced bit in bit position 1
142 * and the modified bit in bit position 0.
144 * For current informational uses, one can rerun any program using
145 * this facility after modifying the hrm_blist_incr to be a larger
146 * amount so that a larger buffer of blocks will be maintained.
149 hat_setstat(struct as
*as
, caddr_t addr
, size_t len
, uint_t rmbits
)
152 uint_t vbits
, newbits
, nb
;
155 ASSERT(len
== PAGESIZE
);
156 ASSERT((rmbits
& ~(P_MOD
|P_REF
)) == 0);
161 mutex_enter(&hat_statlock
);
164 * Search the hash list for the as and addr we are looking for
165 * and set the ref and mod bits in every block that matches.
168 h
= hrm_hash(as
, addr
);
169 for (hrm
= hrm_hashtab
[h
]; hrm
; hrm
= hrm
->hrm_hnext
) {
170 if (hrm_match(hrm
, as
, addr
)) {
171 hrm_setbits(hrm
, addr
, rmbits
);
172 vbits
|= hrm
->hrm_id
;
177 * If we didn't find a block for all of the enabled
178 * vpages bits, then allocate and initialize a block
179 * for each bit that was not found.
181 if (vbits
!= as
->a_vbits
) {
182 newbits
= (vbits
^ as
->a_vbits
) & as
->a_vbits
;
185 nb
= 1 << (ffs(newbits
)-1);
186 hrm
= (struct hrmstat
*)hrm_balloc();
188 hrm_allocfailmsg
= 1;
190 mutex_exit(&hat_statlock
);
194 hrm
->hrm_base
= (uintptr_t)addr
& HRM_BASEMASK
;
197 hrm_setbits(hrm
, addr
, rmbits
);
201 mutex_exit(&hat_statlock
);
205 * Free the resources used to maintain the referenced and modified
206 * statistics for the virtual page view of an address space
210 hat_freestat(struct as
*as
, int id
)
213 struct hrmstat
*prev_ahrm
;
214 struct hrmstat
*hrm_tmplist
;
215 struct hrmstat
*hrm_next
;
217 hat_stats_disable(as
->a_hat
); /* tell the hat layer to stop */
218 hat_enter(as
->a_hat
);
224 if ((hrm
= as
->a_hrm
) == NULL
) {
230 mutex_enter(&hat_statlock
);
232 for (prev_ahrm
= NULL
; hrm
; hrm
= hrm
->hrm_anext
) {
233 if ((id
== hrm
->hrm_id
) || (id
== 0)) {
236 hrm
->hrm_hnext
= hrm_blist
;
240 if (prev_ahrm
== NULL
)
241 as
->a_hrm
= hrm
->hrm_anext
;
243 prev_ahrm
->hrm_anext
= hrm
->hrm_anext
;
250 * If all statistics blocks are free,
251 * return the memory to the system.
253 if (hrm_blist_num
== hrm_blist_total
) {
254 /* zero the block list since we are giving back its memory */
258 hrm_tmplist
= hrm_memlist
;
264 mutex_exit(&hat_statlock
);
267 * If there are any hrmstat structures to be freed, this must only
268 * be done after we've released hat_statlock.
270 while (hrm_tmplist
!= NULL
) {
271 hrm_next
= hrm_tmplist
->hrm_hnext
;
272 kmem_free(hrm_tmplist
, hrm_tmplist
->hrm_base
);
273 hrm_tmplist
= hrm_next
;
278 * Grab memory for statistics gathering of the hat layer.
281 hrm_getblk(int chunk
)
283 struct hrmstat
*hrm
, *l
;
287 mutex_enter(&hat_statlock
);
289 * XXX The whole private freelist management here really should be
292 * The freelist should have some knowledge of how much memory is
293 * needed by a process and thus when hat_resvstat get's called, we can
294 * increment the freelist needs for that process within this subsystem.
295 * Thus there will be reservations for all processes which are being
296 * watched which should be accurate, and consume less memory overall.
298 * For now, just make sure there's enough entries on the freelist to
299 * handle the current chunk.
301 if ((hrm_blist
== NULL
) ||
302 (hrm_blist_num
<= hrm_blist_lowater
) ||
303 (chunk
&& (hrm_blist_num
< chunk
+ hrm_blist_incr
))) {
304 mutex_exit(&hat_statlock
);
306 hrm_incr
= chunk
+ hrm_blist_incr
;
307 hrm
= kmem_zalloc(sizeof (struct hrmstat
) * hrm_incr
, KM_SLEEP
);
308 hrm
->hrm_base
= sizeof (struct hrmstat
) * hrm_incr
;
311 * thread the allocated blocks onto a freelist
312 * using the first block to hold information for
313 * freeing them all later
315 mutex_enter(&hat_statlock
);
316 hrm
->hrm_hnext
= hrm_memlist
;
319 hrm_blist_total
+= (hrm_incr
- 1);
320 for (i
= 1; i
< hrm_incr
; i
++) {
322 l
->hrm_hnext
= hrm_blist
;
327 mutex_exit(&hat_statlock
);
331 hrm_hashin(struct hrmstat
*hrm
)
335 ASSERT(MUTEX_HELD(&hat_statlock
));
336 h
= hrm_hash(hrm
->hrm_as
, hrm
->hrm_base
);
338 hrm
->hrm_hnext
= hrm_hashtab
[h
];
339 hrm_hashtab
[h
] = hrm
;
343 hrm_hashout(struct hrmstat
*hrm
)
345 struct hrmstat
*list
, **prev_hrm
;
348 ASSERT(MUTEX_HELD(&hat_statlock
));
349 h
= hrm_hash(hrm
->hrm_as
, hrm
->hrm_base
);
350 list
= hrm_hashtab
[h
];
351 prev_hrm
= &hrm_hashtab
[h
];
355 *prev_hrm
= list
->hrm_hnext
;
358 prev_hrm
= &list
->hrm_hnext
;
359 list
= list
->hrm_hnext
;
365 * Link a statistic block into an address space and also put it
366 * on the hash list for future references.
369 hrm_link(struct hrmstat
*hrm
)
371 struct as
*as
= hrm
->hrm_as
;
373 ASSERT(MUTEX_HELD(&hat_statlock
));
374 hrm
->hrm_anext
= as
->a_hrm
;
380 * Allocate a block for statistics keeping.
381 * Returns NULL if blocks are unavailable.
383 static struct hrmstat
*
388 ASSERT(MUTEX_HELD(&hat_statlock
));
392 hrm_blist
= hrm
->hrm_hnext
;
394 hrm
->hrm_hnext
= NULL
;
400 * Set the ref and mod bits for addr within statistics block hrm.
403 hrm_setbits(struct hrmstat
*hrm
, caddr_t addr
, uint_t bits
)
408 po
= ((uintptr_t)addr
& HRM_BASEOFFSET
) >> MMU_PAGESHIFT
; /* pg off */
409 bo
= po
/ (NBBY
/ 2); /* which byte in bit array */
410 spb
= (3 - (po
& 3)) * 2; /* shift position within byte */
411 nbits
= bits
<< spb
; /* bit mask */
412 hrm
->hrm_bits
[bo
] |= nbits
;
416 * Return collected statistics about an address space.
417 * If clearflag is set, atomically read and zero the bits.
419 * Fill in the data array supplied with the referenced and
420 * modified bits collected for address range [addr ... addr + len]
421 * in address space, as, uniquely identified by id.
422 * The destination is a byte array. We fill in three bits per byte:
423 * referenced, modified, and hwmapped bits.
424 * Kernel only interface, can't fault on destination data array.
428 hat_getstat(struct as
*as
, caddr_t addr
, size_t len
, uint_t id
,
429 caddr_t datap
, int clearflag
)
431 size_t np
; /* number of pages */
438 /* allocate enough statistics blocks to cover the len passed in */
439 hrm_getblk(np
/ HRM_PAGES
);
441 hat_sync(as
->a_hat
, addr
, len
, clearflag
);
443 /* allocate more statistics blocks if needed */
446 mutex_enter(&hat_statlock
);
447 if (hrm_hashtab
== NULL
) {
448 /* can happen when victim process exits */
449 mutex_exit(&hat_statlock
);
453 a
= (caddr_t
)((uintptr_t)addr
& (uintptr_t)PAGEMASK
);
454 while (a
< addr
+ len
) {
456 size_t n
; /* number of pages, temp */
457 int h
; /* hash index */
462 (((uintptr_t)a
& HRM_PAGEMASK
) >> MMU_PAGESHIFT
));
465 po
= ((uintptr_t)a
& HRM_BASEOFFSET
) >> MMU_PAGESHIFT
;
467 for (hrm
= hrm_hashtab
[h
]; hrm
; hrm
= hrm
->hrm_hnext
) {
468 if (hrm
->hrm_as
== as
&&
469 hrm
->hrm_base
== ((uintptr_t)a
& HRM_BASEMASK
) &&
475 * Extract leading unaligned bits.
478 while (i
< n
&& (po
& 3)) {
479 bo
= po
/ (NBBY
/ 2);
480 spb
= (3 - (po
& 3)) * 2;
481 *dp
++ |= (hrm
->hrm_bits
[bo
] >> spb
) & 3;
483 hrm
->hrm_bits
[bo
] &= ~(3<<spb
);
488 * Extract aligned bits.
491 bo
= po
/ (NBBY
/ 2);
493 int bits
= hrm
->hrm_bits
[bo
];
494 *dp
++ |= (bits
>> 6) & 3;
495 *dp
++ |= (bits
>> 4) & 3;
496 *dp
++ |= (bits
>> 2) & 3;
497 *dp
++ |= (bits
>> 0) & 3;
499 hrm
->hrm_bits
[bo
] = 0;
505 * Extract trailing unaligned bits.
508 bo
= po
/ (NBBY
/ 2);
509 spb
= (3 - (po
& 3)) * 2;
510 *dp
++ |= (hrm
->hrm_bits
[bo
] >> spb
) & 3;
512 hrm
->hrm_bits
[bo
] &= ~(3<<spb
);
523 a
+= n
* MMU_PAGESIZE
;
525 mutex_exit(&hat_statlock
);