FreeBSD/sys/arm64/arm64/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)
{
	pv_entry_t pv, pvf;
	pmap_t pmap;
	struct rwlock *lock;
	pd_entry_t *pde, tpde;
	pt_entry_t *pte, tpte;
	vm_paddr_t pa;
	int cleared, md_gen, not_cleared, lvl;
	struct spglist free;
	KASSERT((m->oflags & VPO_UNMANAGED) == 0,
	    ("pmap_ts_referenced: page %p is not managed", m));
	SLIST_INIT(&free);
	cleared = 0;
	pa = VM_PAGE_TO_PHYS(m);
	lock = PHYS_TO_PV_LIST_LOCK(pa);
	rw_rlock(&pvh_global_lock);
	rw_wlock(lock);
retry:
	not_cleared = 0;
	if ((pvf = TAILQ_FIRST(&m->md.pv_list)) == NULL)
		goto out;
	pv = pvf;
	do {
		if (pvf == NULL)
			pvf = pv;
		pmap = PV_PMAP(pv);
		if (!PMAP_TRYLOCK(pmap)) {
			md_gen = m->md.pv_gen;
			rw_wunlock(lock);
			PMAP_LOCK(pmap);
			rw_wlock(lock);
			if (md_gen != m->md.pv_gen) {
				PMAP_UNLOCK(pmap);
				goto retry;
			}
		}
		pde = pmap_pde(pmap, pv->pv_va, &lvl);
		KASSERT(pde != NULL, ("pmap_ts_referenced: no l2 table found"));
		KASSERT(lvl == 2,
		    ("pmap_ts_referenced: invalid pde level %d", lvl));
		tpde = pmap_load(pde);
		KASSERT((tpde & ATTR_DESCR_MASK) == L2_TABLE,
		    ("pmap_ts_referenced: found an invalid l2 table"));
		pte = pmap_l2_to_l3(pde, pv->pv_va);
		tpte = pmap_load(pte);
		if ((tpte & ATTR_AF) != 0) {
			if (safe_to_clear_referenced(pmap, tpte)) {
				panic("ARM64TODO: safe_to_clear_referenced\n");
			} else if ((tpte & ATTR_SW_WIRED) == 0) {
				pmap_remove_l3(pmap, pte, pv->pv_va, tpde,
				    &free, &lock);
				pmap_invalidate_page(pmap, pv->pv_va);
				cleared++;
				if (pvf == pv)
					pvf = NULL;
				pv = NULL;
				KASSERT(lock == VM_PAGE_TO_PV_LIST_LOCK(m),
				    ("inconsistent pv lock %p %p for page %p",
				    lock, VM_PAGE_TO_PV_LIST_LOCK(m), m));
			} else
				not_cleared++;
		}
		PMAP_UNLOCK(pmap);
		if (pv != NULL && 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);
			m->md.pv_gen++;
		}
	} while ((pv = TAILQ_FIRST(&m->md.pv_list)) != pvf && cleared +
	    not_cleared < PMAP_TS_REFERENCED_MAX);
out:
	rw_wunlock(lock);
	rw_runlock(&pvh_global_lock);
	pmap_free_zero_pages(&free);
	return (cleared + not_cleared);
}
int pmap_ts_referenced ( vm_page_t m ) { pv_entry_t pv , pvf ; pmap_t pmap ; struct rwlock * lock ; pd_entry_t * pde , tpde ; pt_entry_t * pte , tpte ; vm_paddr_t pa ; int cleared , md_gen , not_cleared , lvl ; struct spglist free ; KASSERT ( ( m -> oflags & VPO_UNMANAGED ) == 0 , ( " " , m ) ) ; SLIST_INIT ( & free ) ; cleared = 0 ; pa = VM_PAGE_TO_PHYS ( m ) ; lock = PHYS_TO_PV_LIST_LOCK ( pa ) ; rw_rlock ( & pvh_global_lock ) ; rw_wlock ( lock ) ; retry : not_cleared = 0 ; if ( ( pvf = TAILQ_FIRST ( & m -> md . pv_list ) ) == NULL ) goto out ; pv = pvf ; do { if ( pvf == NULL ) pvf = pv ; pmap = PV_PMAP ( pv ) ; if ( ! PMAP_TRYLOCK ( pmap ) ) { md_gen = m -> md . pv_gen ; rw_wunlock ( lock ) ; PMAP_LOCK ( pmap ) ; rw_wlock ( lock ) ; if ( md_gen != m -> md . pv_gen ) { PMAP_UNLOCK ( pmap ) ; goto retry ; } } pde = pmap_pde ( pmap , pv -> pv_va , & lvl ) ; KASSERT ( pde != NULL , ( " " ) ) ; KASSERT ( lvl == 2 , ( " " , lvl ) ) ; tpde = pmap_load ( pde ) ; KASSERT ( ( tpde & ATTR_DESCR_MASK ) == L2_TABLE , ( " " ) ) ; pte = pmap_l2_to_l3 ( pde , pv -> pv_va ) ; tpte = pmap_load ( pte ) ; if ( ( tpte & ATTR_AF ) != 0 ) { if ( safe_to_clear_referenced ( pmap , tpte ) ) { panic ( " \n " ) ; } else if ( ( tpte & ATTR_SW_WIRED ) == 0 ) { pmap_remove_l3 ( pmap , pte , pv -> pv_va , tpde , & free , & lock ) ; pmap_invalidate_page ( pmap , pv -> pv_va ) ; cleared ++ ; if ( pvf == pv ) pvf = NULL ; pv = NULL ; KASSERT ( lock == VM_PAGE_TO_PV_LIST_LOCK ( m ) , ( " " , lock , VM_PAGE_TO_PV_LIST_LOCK ( m ) , m ) ) ; } else not_cleared ++ ; } PMAP_UNLOCK ( pmap ) ; if ( pv != NULL && 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 ) ; m -> md . pv_gen ++ ; } } while ( ( pv = TAILQ_FIRST ( & m -> md . pv_list ) ) != pvf && cleared + not_cleared < PMAP_TS_REFERENCED_MAX ) ; out : rw_wunlock ( lock ) ; rw_runlock ( & pvh_global_lock ) ; pmap_free_zero_pages ( & free ) ; return ( cleared + not_cleared ) ; }
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 .
/*
				 * TODO: We don't handle the access flag
				 * at all. We need to be able to set it in
				 * the exception handler.
				 */ /*
				 * Wired pages cannot be paged out so
				 * doing accessed bit emulation for
				 * them is wasted effort. We do the
				 * hard work for unwired pages only.
				 */ /* Rotate the PV list if it has more than one entry. */