FreeBSD/sys/i386/i386/pmap.c
/*
 *	pmap_ts_referenced:
 *
 *	Return a count of reference bits for a page, clearing those bits.
 *	It is not necessary for every reference bit to be cleared, but it
 *	is necessary that 0 only be returned when there are truly no
 *	reference bits set.
 *
 *	XXX: The exact number of bits to check and clear is a matter that
 *	should be tested and standardized at some point in the future for
 *	optimal aging of shared pages.
 */
Return a count of reference bits for a page, clearing those bits.
It is not necessary for every reference bit to be cleared, but it
is necessary that 0 only be returned when there are truly no
reference bits set.

The exact number of bits to check and clear is a matter that
should be tested and standardized at some point in the future for
optimal aging of shared pages.
Return a count of reference bits for a page clearing those bits . It is not necessary for every reference bit to be cleared but it is necessary that 0 only be returned when there are truly no reference bits set . The exact number of bits to check and clear is a matter that should be tested and standardized at some point in the future for optimal aging of shared pages .
int
pmap_ts_referenced(vm_page_t m)
{
	struct md_page *pvh;
	pv_entry_t pv, pvf;
	pmap_t pmap;
	pd_entry_t *pde;
	pt_entry_t *pte;
	vm_paddr_t pa;
	int rtval = 0;
	KASSERT((m->oflags & VPO_UNMANAGED) == 0,
	    ("pmap_ts_referenced: page %p is not managed", m));
	pa = VM_PAGE_TO_PHYS(m);
	pvh = pa_to_pvh(pa);
	rw_wlock(&pvh_global_lock);
	sched_pin();
	if ((m->flags & PG_FICTITIOUS) != 0 ||
	    (pvf = TAILQ_FIRST(&pvh->pv_list)) == NULL)
		goto small_mappings;
	pv = pvf;
	do {
		pmap = PV_PMAP(pv);
		PMAP_LOCK(pmap);
		pde = pmap_pde(pmap, pv->pv_va);
		if ((*pde & PG_A) != 0) {
			if ((((pa >> PAGE_SHIFT) ^ (pv->pv_va >> PDRSHIFT) ^
			    (uintptr_t)pmap) & (NPTEPG - 1)) == 0 &&
			    (*pde & PG_W) == 0) {
				atomic_clear_int((u_int *)pde, PG_A);
				pmap_invalidate_page(pmap, pv->pv_va);
			}
			rtval++;
		}
		PMAP_UNLOCK(pmap);
		if (TAILQ_NEXT(pv, pv_next) != NULL) {
			TAILQ_REMOVE(&pvh->pv_list, pv, pv_next);
			TAILQ_INSERT_TAIL(&pvh->pv_list, pv, pv_next);
		}
		if (rtval >= PMAP_TS_REFERENCED_MAX)
			goto out;
	} while ((pv = TAILQ_FIRST(&pvh->pv_list)) != pvf);
small_mappings:
	if ((pvf = TAILQ_FIRST(&m->md.pv_list)) == NULL)
		goto out;
	pv = pvf;
	do {
		pmap = PV_PMAP(pv);
		PMAP_LOCK(pmap);
		pde = pmap_pde(pmap, pv->pv_va);
		KASSERT((*pde & PG_PS) == 0,
		    ("pmap_ts_referenced: found a 4mpage in page %p's pv list",
		    m));
		pte = pmap_pte_quick(pmap, pv->pv_va);
		if ((*pte & PG_A) != 0) {
			atomic_clear_int((u_int *)pte, PG_A);
			pmap_invalidate_page(pmap, pv->pv_va);
			rtval++;
		}
		PMAP_UNLOCK(pmap);
		if (TAILQ_NEXT(pv, pv_next) != NULL) {
			TAILQ_REMOVE(&m->md.pv_list, pv, pv_next);
			TAILQ_INSERT_TAIL(&m->md.pv_list, pv, pv_next);
		}
	} while ((pv = TAILQ_FIRST(&m->md.pv_list)) != pvf && rtval <
	    PMAP_TS_REFERENCED_MAX);
out:
	sched_unpin();
	rw_wunlock(&pvh_global_lock);
	return (rtval);
}
int pmap_ts_referenced ( vm_page_t m ) { struct md_page * pvh ; pv_entry_t pv , pvf ; pmap_t pmap ; pd_entry_t * pde ; pt_entry_t * pte ; vm_paddr_t pa ; int rtval = 0 ; KASSERT ( ( m -> oflags & VPO_UNMANAGED ) == 0 , ( " " , m ) ) ; pa = VM_PAGE_TO_PHYS ( m ) ; pvh = pa_to_pvh ( pa ) ; rw_wlock ( & pvh_global_lock ) ; sched_pin ( ) ; if ( ( m -> flags & PG_FICTITIOUS ) != 0 || ( pvf = TAILQ_FIRST ( & pvh -> pv_list ) ) == NULL ) goto small_mappings ; pv = pvf ; do { pmap = PV_PMAP ( pv ) ; PMAP_LOCK ( pmap ) ; pde = pmap_pde ( pmap , pv -> pv_va ) ; if ( ( * pde & PG_A ) != 0 ) { if ( ( ( ( pa >> PAGE_SHIFT ) ^ ( pv -> pv_va >> PDRSHIFT ) ^ ( uintptr_t ) pmap ) & ( NPTEPG - 1 ) ) == 0 && ( * pde & PG_W ) == 0 ) { atomic_clear_int ( ( u_int * ) pde , PG_A ) ; pmap_invalidate_page ( pmap , pv -> pv_va ) ; } rtval ++ ; } PMAP_UNLOCK ( pmap ) ; if ( TAILQ_NEXT ( pv , pv_next ) != NULL ) { TAILQ_REMOVE ( & pvh -> pv_list , pv , pv_next ) ; TAILQ_INSERT_TAIL ( & pvh -> pv_list , pv , pv_next ) ; } if ( rtval >= PMAP_TS_REFERENCED_MAX ) goto out ; } while ( ( pv = TAILQ_FIRST ( & pvh -> pv_list ) ) != pvf ) ; small_mappings : if ( ( pvf = TAILQ_FIRST ( & m -> md . pv_list ) ) == NULL ) goto out ; pv = pvf ; do { pmap = PV_PMAP ( pv ) ; PMAP_LOCK ( pmap ) ; pde = pmap_pde ( pmap , pv -> pv_va ) ; KASSERT ( ( * pde & PG_PS ) == 0 , ( " " , m ) ) ; pte = pmap_pte_quick ( pmap , pv -> pv_va ) ; if ( ( * pte & PG_A ) != 0 ) { atomic_clear_int ( ( u_int * ) pte , PG_A ) ; pmap_invalidate_page ( pmap , pv -> pv_va ) ; rtval ++ ; } PMAP_UNLOCK ( pmap ) ; if ( TAILQ_NEXT ( pv , pv_next ) != NULL ) { TAILQ_REMOVE ( & m -> md . pv_list , pv , pv_next ) ; TAILQ_INSERT_TAIL ( & m -> md . pv_list , pv , pv_next ) ; } } while ( ( pv = TAILQ_FIRST ( & m -> md . pv_list ) ) != pvf && rtval < PMAP_TS_REFERENCED_MAX ) ; out : sched_unpin ( ) ; rw_wunlock ( & pvh_global_lock ) ; return ( rtval ) ; }
pmap_ts_referenced:
      Return a count of reference bits for a page, clearing those bits.
pmap_ts_referenced : Return a count of reference bits for a page clearing those bits .
/*
			 * Since this reference bit is shared by either 1024
			 * or 512 4KB pages, it should not be cleared every
			 * time it is tested.  Apply a simple "hash" function
			 * on the physical page number, the virtual superpage
			 * number, and the pmap address to select one 4KB page
			 * out of the 1024 or 512 on which testing the
			 * reference bit will result in clearing that bit.
			 * This function is designed to avoid the selection of
			 * the same 4KB page for every 2- or 4MB page mapping.
			 *
			 * On demotion, a mapping that hasn't been referenced
			 * is simply destroyed.  To avoid the possibility of a
			 * subsequent page fault on a demoted wired mapping,
			 * always leave its reference bit set.  Moreover,
			 * since the superpage is wired, the current state of
			 * its reference bit won't affect page replacement.
			 */ /* Rotate the PV list if it has more than one entry. */ /* Rotate the PV list if it has more than one entry. */