#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/proc.h>
#include <sys/vmmeter.h>
#include <sys/vnode.h>
#include <sys/kernel.h>
#include <sys/alist.h>
#include <sys/sysctl.h>
#include <sys/cpu_topology.h>
#include <vm/vm.h>
#include <vm/vm_param.h>
#include <sys/lock.h>
#include <vm/vm_kern.h>
#include <vm/pmap.h>
#include <vm/vm_map.h>
#include <vm/vm_object.h>
#include <vm/vm_page.h>
#include <vm/vm_pageout.h>
#include <vm/vm_pager.h>
#include <vm/vm_extern.h>
#include <vm/swap_pager.h>
#include <machine/inttypes.h>
#include <machine/md_var.h>
#include <machine/specialreg.h>
#include <machine/bus_dma.h>
#include <vm/vm_page2.h>
#include <sys/spinlock2.h>
struct vm_page_hash_elm {
vm_page_t m;
vm_object_t object;
vm_pindex_t pindex;
int ticks;
int unused;
};
#define VM_PAGE_HASH_SET 4
#define VM_PAGE_HASH_MAX (8 * 1024 * 1024)
__read_mostly static int set_assoc_mask = 16 - 1;
static void vm_page_queue_init(void);
static void vm_page_free_wakeup(void);
static vm_page_t vm_page_select_cache(u_short pg_color);
static vm_page_t _vm_page_list_find_wide(int basequeue, int index, int *lastp);
static vm_page_t _vm_page_list_find2_wide(int bq1, int bq2, int index,
int *lastp1, int *lastp);
static void _vm_page_deactivate_locked(vm_page_t m, int athead);
static void vm_numa_add_topology_mem(cpu_node_t *cpup, int physid, long bytes);
struct vpgqueues vm_page_queues[PQ_COUNT];
static volatile int vm_pages_waiting;
static struct alist vm_contig_alist;
static struct almeta vm_contig_ameta[ALIST_RECORDS_65536];
static struct spinlock vm_contig_spin = SPINLOCK_INITIALIZER(&vm_contig_spin, "vm_contig_spin");
__read_mostly static int vm_page_hash_vnode_only;
__read_mostly static int vm_page_hash_size;
__read_mostly static struct vm_page_hash_elm *vm_page_hash;
static u_long vm_dma_reserved = 0;
TUNABLE_ULONG("vm.dma_reserved", &vm_dma_reserved);
SYSCTL_ULONG(_vm, OID_AUTO, dma_reserved, CTLFLAG_RD, &vm_dma_reserved, 0,
"Memory reserved for DMA");
SYSCTL_UINT(_vm, OID_AUTO, dma_free_pages, CTLFLAG_RD,
&vm_contig_alist.bl_free, 0, "Memory reserved for DMA");
SYSCTL_INT(_vm, OID_AUTO, page_hash_vnode_only, CTLFLAG_RW,
&vm_page_hash_vnode_only, 0, "Only hash vnode pages");
#if 0
static int vm_page_hash_debug;
SYSCTL_INT(_vm, OID_AUTO, page_hash_debug, CTLFLAG_RW,
&vm_page_hash_debug, 0, "Only hash vnode pages");
#endif
static int vm_contig_verbose = 0;
TUNABLE_INT("vm.contig_verbose", &vm_contig_verbose);
RB_GENERATE2(vm_page_rb_tree, vm_page, rb_entry, rb_vm_page_compare,
vm_pindex_t, pindex);
static void
vm_page_queue_init(void)
{
int i;
for (i = 0; i < PQ_L2_SIZE; i++)
vm_page_queues[PQ_FREE+i].cnt_offset =
offsetof(struct vmstats, v_free_count);
for (i = 0; i < PQ_L2_SIZE; i++)
vm_page_queues[PQ_CACHE+i].cnt_offset =
offsetof(struct vmstats, v_cache_count);
for (i = 0; i < PQ_L2_SIZE; i++)
vm_page_queues[PQ_INACTIVE+i].cnt_offset =
offsetof(struct vmstats, v_inactive_count);
for (i = 0; i < PQ_L2_SIZE; i++)
vm_page_queues[PQ_ACTIVE+i].cnt_offset =
offsetof(struct vmstats, v_active_count);
for (i = 0; i < PQ_L2_SIZE; i++)
vm_page_queues[PQ_HOLD+i].cnt_offset =
offsetof(struct vmstats, v_active_count);
for (i = 0; i < PQ_COUNT; i++) {
struct vpgqueues *vpq;
vpq = &vm_page_queues[i];
vpq->lastq = -1;
TAILQ_INIT(&vpq->pl);
spin_init(&vpq->spin, "vm_page_queue_init");
}
}
vm_pindex_t first_page = 0;
vm_pindex_t vm_page_array_size = 0;
vm_page_t vm_page_array = NULL;
vm_paddr_t vm_low_phys_reserved;
void
vm_set_page_size(void)
{
if (vmstats.v_page_size == 0)
vmstats.v_page_size = PAGE_SIZE;
if (((vmstats.v_page_size - 1) & vmstats.v_page_size) != 0)
panic("vm_set_page_size: page size not a power of two");
}
static void
vm_add_new_page(vm_paddr_t pa, int *badcountp)
{
struct vpgqueues *vpq;
vm_page_t m;
m = PHYS_TO_VM_PAGE(pa);
if (m->queue) {
if (*badcountp < 10) {
kprintf("vm_add_new_page: duplicate pa %016jx\n",
(intmax_t)pa);
++*badcountp;
} else if (*badcountp == 10) {
kprintf("vm_add_new_page: duplicate pa (many more)\n");
++*badcountp;
}
return;
}
m->phys_addr = pa;
m->flags = 0;
m->pat_mode = PAT_WRITE_BACK;
m->pc = (pa >> PAGE_SHIFT);
m->pc ^= ((pa >> PAGE_SHIFT) / PQ_L2_SIZE);
m->pc ^= ((pa >> PAGE_SHIFT) / (PQ_L2_SIZE * PQ_L2_SIZE));
m->pc &= PQ_L2_MASK;
if (pa < vm_low_phys_reserved) {
atomic_add_long(&vmstats.v_page_count, 1);
atomic_add_long(&vmstats.v_dma_pages, 1);
m->flags |= PG_FICTITIOUS | PG_UNQUEUED;
m->queue = PQ_NONE;
m->wire_count = 1;
atomic_add_long(&vmstats.v_wire_count, 1);
alist_free(&vm_contig_alist, pa >> PAGE_SHIFT, 1);
return;
}
m->queue = m->pc + PQ_FREE;
KKASSERT(m->dirty == 0);
atomic_add_long(&vmstats.v_page_count, 1);
atomic_add_long(&vmstats.v_free_count, 1);
vpq = &vm_page_queues[m->queue];
TAILQ_INSERT_HEAD(&vpq->pl, m, pageq);
++vpq->lcnt;
}
void
vm_page_startup(void)
{
vm_offset_t vaddr = virtual2_start ? virtual2_start : virtual_start;
vm_offset_t mapped;
vm_pindex_t npages;
vm_paddr_t page_range;
vm_paddr_t new_end;
int i;
vm_paddr_t pa;
vm_paddr_t last_pa;
vm_paddr_t end;
vm_paddr_t biggestone, biggestsize;
vm_paddr_t total;
vm_page_t m;
int badcount;
total = 0;
badcount = 0;
biggestsize = 0;
biggestone = 0;
vaddr = round_page(vaddr);
for (i = 0; phys_avail[i].phys_end; ++i) {
phys_avail[i].phys_beg = round_page64(phys_avail[i].phys_beg);
phys_avail[i].phys_end = trunc_page64(phys_avail[i].phys_end);
if (phys_avail[i].phys_end < phys_avail[i].phys_beg)
phys_avail[i].phys_end = phys_avail[i].phys_beg;
}
for (i = 0; phys_avail[i].phys_end; ++i) {
vm_paddr_t size = phys_avail[i].phys_end -
phys_avail[i].phys_beg;
if (size > biggestsize) {
biggestone = i;
biggestsize = size;
}
total += size;
}
--i;
end = phys_avail[biggestone].phys_end;
end = trunc_page(end);
vm_page_queue_init();
#if !defined(_KERNEL_VIRTUAL)
page_range = phys_avail[i].phys_end / PAGE_SIZE;
vm_page_dump_size = round_page(roundup2(page_range, NBBY) / NBBY);
end -= vm_page_dump_size;
vm_page_dump = (void *)pmap_map(&vaddr, end, end + vm_page_dump_size,
VM_PROT_READ | VM_PROT_WRITE);
bzero((void *)vm_page_dump, vm_page_dump_size);
#endif
first_page = phys_avail[0].phys_beg / PAGE_SIZE;
page_range = phys_avail[i].phys_end / PAGE_SIZE - first_page;
npages = (total - (page_range * sizeof(struct vm_page))) / PAGE_SIZE;
#ifndef _KERNEL_VIRTUAL
vm_low_phys_reserved = (vm_paddr_t)65536 << PAGE_SHIFT;
if (vm_low_phys_reserved > total / 4)
vm_low_phys_reserved = total / 4;
if (vm_dma_reserved == 0) {
vm_dma_reserved = 128 * 1024 * 1024;
if (vm_dma_reserved > total / 16)
vm_dma_reserved = total / 16;
}
#endif
alist_init(&vm_contig_alist, 65536, vm_contig_ameta,
ALIST_RECORDS_65536);
if (bootverbose && ctob(physmem) >= 400LL*1024*1024*1024)
kprintf("initializing vm_page_array ");
new_end = trunc_page(end - page_range * sizeof(struct vm_page));
mapped = pmap_map(&vaddr, new_end, end, VM_PROT_READ | VM_PROT_WRITE);
vm_page_array = (vm_page_t)mapped;
#if defined(__x86_64__) && !defined(_KERNEL_VIRTUAL)
for (pa = new_end;
pa < phys_avail[biggestone].phys_end;
pa += PAGE_SIZE) {
dump_add_page(pa);
}
#endif
bzero((caddr_t) vm_page_array, page_range * sizeof(struct vm_page));
vm_page_array_size = page_range;
if (bootverbose && ctob(physmem) >= 400LL*1024*1024*1024)
kprintf("size = 0x%zx\n", vm_page_array_size);
m = &vm_page_array[0];
pa = ptoa(first_page);
for (i = 0; i < page_range; ++i) {
spin_init(&m->spin, "vm_page");
m->phys_addr = pa;
pa += PAGE_SIZE;
++m;
}
vmstats.v_page_count = 0;
vmstats.v_free_count = 0;
for (i = 0; phys_avail[i].phys_end && npages > 0; ++i) {
pa = phys_avail[i].phys_beg;
if (i == biggestone)
last_pa = new_end;
else
last_pa = phys_avail[i].phys_end;
while (pa < last_pa && npages-- > 0) {
vm_add_new_page(pa, &badcount);
pa += PAGE_SIZE;
}
}
if (virtual2_start)
virtual2_start = vaddr;
else
virtual_start = vaddr;
mycpu->gd_vmstats = vmstats;
}
void
vm_numa_organize(vm_paddr_t ran_beg, vm_paddr_t bytes, int physid)
{
vm_paddr_t scan_beg;
vm_paddr_t scan_end;
vm_paddr_t ran_end;
struct vpgqueues *vpq;
vm_page_t m;
vm_page_t mend;
int socket_mod;
int socket_value;
int i;
if (cpu_topology_phys_ids <= 1 ||
cpu_topology_core_ids == 0) {
return;
}
ran_end = ran_beg + bytes;
socket_mod = PQ_L2_SIZE / cpu_topology_phys_ids;
socket_value = (physid % cpu_topology_phys_ids) * socket_mod;
mend = &vm_page_array[vm_page_array_size];
crit_enter();
if (root_cpu_node)
vm_numa_add_topology_mem(root_cpu_node, physid, (long)bytes);
for (i = 0; phys_avail[i].phys_end; ++i) {
scan_beg = phys_avail[i].phys_beg;
scan_end = phys_avail[i].phys_end;
if (scan_end <= ran_beg)
continue;
if (scan_beg >= ran_end)
continue;
if (scan_beg < ran_beg)
scan_beg = ran_beg;
if (scan_end > ran_end)
scan_end = ran_end;
if (atop(scan_end) > first_page + vm_page_array_size)
scan_end = ptoa(first_page + vm_page_array_size);
m = PHYS_TO_VM_PAGE(scan_beg);
while (scan_beg < scan_end) {
KKASSERT(m < mend);
if (m->queue != PQ_NONE) {
vpq = &vm_page_queues[m->queue];
TAILQ_REMOVE(&vpq->pl, m, pageq);
--vpq->lcnt;
m->queue -= m->pc;
m->pc %= socket_mod;
m->pc += socket_value;
m->pc &= PQ_L2_MASK;
m->queue += m->pc;
vpq = &vm_page_queues[m->queue];
TAILQ_INSERT_HEAD(&vpq->pl, m, pageq);
++vpq->lcnt;
} else {
m->pc %= socket_mod;
m->pc += socket_value;
m->pc &= PQ_L2_MASK;
}
scan_beg += PAGE_SIZE;
++m;
}
}
crit_exit();
}
void
vm_numa_organize_finalize(void)
{
struct vpgqueues *vpq;
vm_page_t m;
long lcnt_lo;
long lcnt_hi;
int iter;
int i;
int scale_lim;
crit_enter();
if (cpu_topology_ht_ids) {
scale_lim = PQ_L2_SIZE / cpu_topology_phys_ids;
scale_lim = scale_lim / cpu_topology_core_ids;
scale_lim = scale_lim / cpu_topology_ht_ids;
scale_lim = scale_lim * cpu_topology_ht_ids;
scale_lim = scale_lim * cpu_topology_core_ids;
scale_lim = scale_lim * cpu_topology_phys_ids;
} else {
scale_lim = PQ_L2_SIZE;
}
lcnt_hi = 0;
for (i = 0; i < scale_lim; ++i) {
lcnt_hi += vm_page_queues[i].lcnt;
}
lcnt_hi /= scale_lim;
lcnt_lo = lcnt_hi - lcnt_hi / 10;
kprintf("vm_page: avg %ld pages per queue, %d queues\n",
lcnt_hi, scale_lim);
iter = 0;
for (i = 0; i < scale_lim; ++i) {
vpq = &vm_page_queues[PQ_FREE + i];
while (vpq->lcnt < lcnt_lo) {
struct vpgqueues *vptmp;
iter = (iter + 1) & PQ_L2_MASK;
vptmp = &vm_page_queues[PQ_FREE + iter];
if (vptmp->lcnt < lcnt_hi)
continue;
m = TAILQ_FIRST(&vptmp->pl);
KKASSERT(m->queue == PQ_FREE + iter);
TAILQ_REMOVE(&vptmp->pl, m, pageq);
--vptmp->lcnt;
m->queue -= m->pc;
m->pc = i;
m->queue += m->pc;
TAILQ_INSERT_HEAD(&vpq->pl, m, pageq);
++vpq->lcnt;
}
}
crit_exit();
}
static
void
vm_numa_add_topology_mem(cpu_node_t *cpup, int physid, long bytes)
{
int cpuid;
int i;
switch(cpup->type) {
case PACKAGE_LEVEL:
cpup->phys_mem += bytes;
break;
case CHIP_LEVEL:
if (CPUMASK_TESTNZERO(cpup->members)) {
cpuid = BSFCPUMASK(cpup->members);
if (physid ==
get_chip_ID_from_APICID(CPUID_TO_APICID(cpuid))) {
cpup->phys_mem += bytes;
}
}
break;
case CORE_LEVEL:
case THREAD_LEVEL:
cpup->phys_mem = cpup->parent_node->phys_mem;
break;
}
for (i = 0; i < MAXCPU && cpup->child_node[i]; ++i)
vm_numa_add_topology_mem(cpup->child_node[i], physid, bytes);
}
static void
vm_page_startup_finish(void *dummy __unused)
{
alist_blk_t blk;
alist_blk_t rblk;
alist_blk_t count;
alist_blk_t xcount;
alist_blk_t bfree;
vm_page_t m;
struct vm_page_hash_elm *mp;
int mask;
mask = PQ_L2_SIZE / ncpus_fit - 1;
if (mask < 7)
mask = 7;
if (mask < 15)
mask = 15;
cpu_ccfence();
set_assoc_mask = mask;
spin_lock(&vm_contig_spin);
for (;;) {
bfree = alist_free_info(&vm_contig_alist, &blk, &count);
if (bfree <= vm_dma_reserved / PAGE_SIZE)
break;
if (count == 0)
break;
bfree -= vm_dma_reserved / PAGE_SIZE;
if (count > bfree) {
blk += count - bfree;
count = bfree;
}
for (xcount = 1; xcount <= count; xcount <<= 1)
;
xcount >>= 1;
blk += count - xcount;
count = xcount;
rblk = alist_alloc(&vm_contig_alist, blk, count);
if (rblk != blk) {
kprintf("vm_page_startup_finish: Unable to return "
"dma space @0x%08x/%d -> 0x%08x\n",
blk, count, rblk);
break;
}
atomic_add_long(&vmstats.v_dma_pages, -(long)count);
spin_unlock(&vm_contig_spin);
m = PHYS_TO_VM_PAGE((vm_paddr_t)blk << PAGE_SHIFT);
vm_low_phys_reserved = VM_PAGE_TO_PHYS(m);
while (count) {
vm_page_flag_clear(m, PG_FICTITIOUS | PG_UNQUEUED);
vm_page_busy_wait(m, FALSE, "cpgfr");
vm_page_unwire(m, 0);
vm_page_free(m);
--count;
++m;
}
spin_lock(&vm_contig_spin);
}
spin_unlock(&vm_contig_spin);
kprintf("DMA space used: %jdk, remaining available: %jdk\n",
(intmax_t)(vmstats.v_dma_pages - vm_contig_alist.bl_free) *
(PAGE_SIZE / 1024),
(intmax_t)vm_contig_alist.bl_free * (PAGE_SIZE / 1024));
vm_page_hash_size = 4096;
while (vm_page_hash_size < (vm_page_array_size / 16))
vm_page_hash_size <<= 1;
if (vm_page_hash_size > VM_PAGE_HASH_MAX)
vm_page_hash_size = VM_PAGE_HASH_MAX;
mp = (void *)kmem_alloc3(kernel_map,
(vm_page_hash_size + VM_PAGE_HASH_SET) *
sizeof(*vm_page_hash),
VM_SUBSYS_VMPGHASH, KM_CPU(0));
bzero(mp, (vm_page_hash_size + VM_PAGE_HASH_SET) * sizeof(*mp));
cpu_sfence();
vm_page_hash = mp;
}
SYSINIT(vm_pgend, SI_SUB_PROC0_POST, SI_ORDER_ANY,
vm_page_startup_finish, NULL);
int
rb_vm_page_scancmp(struct vm_page *p, void *data)
{
struct rb_vm_page_scan_info *info = data;
if (p->pindex < info->start_pindex)
return(-1);
if (p->pindex > info->end_pindex)
return(1);
return(0);
}
int
rb_vm_page_compare(struct vm_page *p1, struct vm_page *p2)
{
if (p1->pindex < p2->pindex)
return(-1);
if (p1->pindex > p2->pindex)
return(1);
return(0);
}
void
vm_page_init(vm_page_t m)
{
}
static __inline
void
_vm_page_queue_spin_lock(vm_page_t m)
{
u_short queue;
queue = m->queue;
if (queue != PQ_NONE) {
spin_lock(&vm_page_queues[queue].spin);
KKASSERT(queue == m->queue);
}
}
static __inline
void
_vm_page_queue_spin_unlock(vm_page_t m)
{
u_short queue;
queue = m->queue;
cpu_ccfence();
if (queue != PQ_NONE)
spin_unlock(&vm_page_queues[queue].spin);
}
static __inline
void
_vm_page_queues_spin_lock(u_short queue)
{
cpu_ccfence();
if (queue != PQ_NONE)
spin_lock(&vm_page_queues[queue].spin);
}
static __inline
void
_vm_page_queues_spin_unlock(u_short queue)
{
cpu_ccfence();
if (queue != PQ_NONE)
spin_unlock(&vm_page_queues[queue].spin);
}
void
vm_page_queue_spin_lock(vm_page_t m)
{
_vm_page_queue_spin_lock(m);
}
void
vm_page_queues_spin_lock(u_short queue)
{
_vm_page_queues_spin_lock(queue);
}
void
vm_page_queue_spin_unlock(vm_page_t m)
{
_vm_page_queue_spin_unlock(m);
}
void
vm_page_queues_spin_unlock(u_short queue)
{
_vm_page_queues_spin_unlock(queue);
}
static __inline
void
_vm_page_and_queue_spin_lock(vm_page_t m)
{
vm_page_spin_lock(m);
_vm_page_queue_spin_lock(m);
}
static __inline
void
_vm_page_and_queue_spin_unlock(vm_page_t m)
{
_vm_page_queues_spin_unlock(m->queue);
vm_page_spin_unlock(m);
}
void
vm_page_and_queue_spin_unlock(vm_page_t m)
{
_vm_page_and_queue_spin_unlock(m);
}
void
vm_page_and_queue_spin_lock(vm_page_t m)
{
_vm_page_and_queue_spin_lock(m);
}
static __inline u_short
_vm_page_rem_queue_spinlocked(vm_page_t m)
{
struct vpgqueues *pq;
u_short queue;
u_short oqueue;
long *cnt_adj;
long *cnt_gd;
queue = m->queue;
if (queue != PQ_NONE) {
pq = &vm_page_queues[queue];
TAILQ_REMOVE(&pq->pl, m, pageq);
cnt_adj = (long *)((char *)&mycpu->gd_vmstats_adj +
pq->cnt_offset);
cnt_gd = (long *)((char *)&mycpu->gd_vmstats +
pq->cnt_offset);
atomic_add_long(cnt_adj, -1);
atomic_add_long(cnt_gd, -1);
if (*cnt_adj < -1024 && vm_paging_start(-1024 * ncpus)) {
u_long copy = atomic_swap_long(cnt_adj, 0);
cnt_adj = (long *)((char *)&vmstats + pq->cnt_offset);
atomic_add_long(cnt_adj, copy);
}
pq->lcnt--;
m->queue = PQ_NONE;
oqueue = queue;
queue -= m->pc;
vm_page_queues_spin_unlock(oqueue);
}
return queue;
}
static __inline void
_vm_page_add_queue_spinlocked(vm_page_t m, u_short queue, int athead)
{
struct vpgqueues *pq;
u_long *cnt_adj;
u_long *cnt_gd;
KKASSERT(m->queue == PQ_NONE &&
(m->flags & (PG_FICTITIOUS | PG_UNQUEUED)) == 0);
if (queue != PQ_NONE) {
vm_page_queues_spin_lock(queue);
pq = &vm_page_queues[queue];
++pq->lcnt;
cnt_adj = (long *)((char *)&mycpu->gd_vmstats_adj +
pq->cnt_offset);
cnt_gd = (long *)((char *)&mycpu->gd_vmstats +
pq->cnt_offset);
atomic_add_long(cnt_adj, 1);
atomic_add_long(cnt_gd, 1);
m->queue = queue;
if (queue - m->pc == PQ_FREE) {
TAILQ_INSERT_HEAD(&pq->pl, m, pageq);
} else if (athead) {
TAILQ_INSERT_HEAD(&pq->pl, m, pageq);
} else {
TAILQ_INSERT_TAIL(&pq->pl, m, pageq);
}
}
}
void
vm_page_sleep_busy(vm_page_t m, int also_m_busy, const char *msg)
{
u_int32_t busy_count;
for (;;) {
busy_count = m->busy_count;
cpu_ccfence();
if ((busy_count & PBUSY_LOCKED) == 0 &&
(also_m_busy == 0 || (busy_count & PBUSY_MASK) == 0)) {
break;
}
tsleep_interlock(m, 0);
if (atomic_cmpset_int(&m->busy_count, busy_count,
busy_count | PBUSY_WANTED)) {
atomic_set_int(&m->flags, PG_REFERENCED);
tsleep(m, PINTERLOCKED, msg, 0);
break;
}
}
}
u_short
vm_get_pg_color(int cpuid, vm_object_t object, vm_pindex_t pindex)
{
u_short pg_color;
int object_pg_color;
object_pg_color = object ? object->pg_color : 0;
if (cpu_topology_ht_ids) {
int phys_id;
int core_id;
int ht_id;
int physcale;
int grpscale;
int cpuscale;
phys_id = get_cpu_phys_id(cpuid);
core_id = get_cpu_core_id(cpuid);
ht_id = get_cpu_ht_id(cpuid);
physcale = PQ_L2_SIZE / cpu_topology_phys_ids;
grpscale = physcale / cpu_topology_core_ids;
cpuscale = grpscale / cpu_topology_ht_ids;
pg_color = phys_id * physcale;
pg_color += core_id * grpscale;
pg_color += ht_id * cpuscale;
pg_color += (pindex + object_pg_color) % cpuscale;
#if 0
if (grpsize >= 8) {
pg_color += (pindex + object_pg_color) % grpsize;
} else {
if (grpsize <= 2) {
grpsize = 8;
} else {
grpsize += grpsize;
if (grpsize < 8)
grpsize += grpsize;
}
pg_color += (pindex + object_pg_color) % grpsize;
}
#endif
} else {
int cpuscale;
cpuscale = PQ_L2_SIZE / ncpus;
pg_color = cpuid * cpuscale;
pg_color += (pindex + object_pg_color) % cpuscale;
}
return (pg_color & PQ_L2_MASK);
}
void
VM_PAGE_DEBUG_EXT(vm_page_busy_wait)(vm_page_t m,
int also_m_busy, const char *msg
VM_PAGE_DEBUG_ARGS)
{
u_int32_t busy_count;
for (;;) {
busy_count = m->busy_count;
cpu_ccfence();
if (busy_count & PBUSY_LOCKED) {
tsleep_interlock(m, 0);
if (atomic_cmpset_int(&m->busy_count, busy_count,
busy_count | PBUSY_WANTED)) {
atomic_set_int(&m->flags, PG_REFERENCED);
tsleep(m, PINTERLOCKED, msg, 0);
}
} else if (also_m_busy && busy_count) {
tsleep_interlock(m, 0);
if (atomic_cmpset_int(&m->busy_count, busy_count,
busy_count | PBUSY_WANTED)) {
atomic_set_int(&m->flags, PG_REFERENCED);
tsleep(m, PINTERLOCKED, msg, 0);
}
} else {
if (atomic_cmpset_int(&m->busy_count, busy_count,
busy_count | PBUSY_LOCKED)) {
#ifdef VM_PAGE_DEBUG
m->busy_func = func;
m->busy_line = lineno;
#endif
break;
}
}
}
}
int
VM_PAGE_DEBUG_EXT(vm_page_busy_try)(vm_page_t m, int also_m_busy
VM_PAGE_DEBUG_ARGS)
{
u_int32_t busy_count;
for (;;) {
busy_count = m->busy_count;
cpu_ccfence();
if (busy_count & PBUSY_LOCKED)
return TRUE;
if (also_m_busy && (busy_count & PBUSY_MASK) != 0)
return TRUE;
if (atomic_cmpset_int(&m->busy_count, busy_count,
busy_count | PBUSY_LOCKED)) {
#ifdef VM_PAGE_DEBUG
m->busy_func = func;
m->busy_line = lineno;
#endif
return FALSE;
}
}
}
static __inline
int
_vm_page_wakeup(vm_page_t m)
{
u_int32_t busy_count;
busy_count = m->busy_count;
cpu_ccfence();
for (;;) {
if (atomic_fcmpset_int(&m->busy_count, &busy_count,
busy_count &
~(PBUSY_LOCKED | PBUSY_WANTED))) {
return((int)(busy_count & PBUSY_WANTED));
}
}
}
void
vm_page_wakeup(vm_page_t m)
{
KASSERT(m->busy_count & PBUSY_LOCKED,
("vm_page_wakeup: page not busy!!!"));
if (_vm_page_wakeup(m))
wakeup(m);
}
void
vm_page_hold(vm_page_t m)
{
atomic_add_int(&m->hold_count, 1);
KKASSERT(m->queue - m->pc != PQ_FREE);
}
void
vm_page_unhold(vm_page_t m)
{
KASSERT(m->hold_count > 0 && m->queue - m->pc != PQ_FREE,
("vm_page_unhold: pg %p illegal hold_count (%d) or "
"on FREE queue (%d)",
m, m->hold_count, m->queue - m->pc));
if (atomic_fetchadd_int(&m->hold_count, -1) == 1 &&
m->queue - m->pc == PQ_HOLD) {
vm_page_spin_lock(m);
if (m->hold_count == 0 && m->queue - m->pc == PQ_HOLD) {
_vm_page_queue_spin_lock(m);
_vm_page_rem_queue_spinlocked(m);
_vm_page_add_queue_spinlocked(m, PQ_FREE + m->pc, 1);
_vm_page_queue_spin_unlock(m);
}
vm_page_spin_unlock(m);
}
}
void
vm_page_initfake(vm_page_t m, vm_paddr_t paddr, vm_memattr_t memattr)
{
if ((m->flags & PG_FICTITIOUS) != 0)
goto memattr;
m->phys_addr = paddr;
m->queue = PQ_NONE;
m->flags = PG_FICTITIOUS | PG_UNQUEUED;
m->busy_count = PBUSY_LOCKED;
m->wire_count = 1;
spin_init(&m->spin, "fake_page");
pmap_page_init(m);
memattr:
pmap_page_set_memattr(m, memattr);
}
int
vm_page_insert(vm_page_t m, vm_object_t object, vm_pindex_t pindex)
{
ASSERT_LWKT_TOKEN_HELD_EXCL(vm_object_token(object));
if (m->object != NULL)
panic("vm_page_insert: already inserted");
atomic_add_int(&object->generation, 1);
vm_page_spin_lock(m);
m->object = object;
m->pindex = pindex;
if (vm_page_rb_tree_RB_INSERT(&object->rb_memq, m)) {
m->object = NULL;
m->pindex = 0;
vm_page_spin_unlock(m);
return FALSE;
}
++object->resident_page_count;
++mycpu->gd_vmtotal.t_rm;
vm_page_spin_unlock(m);
if ((m->valid & m->dirty) ||
(m->flags & (PG_WRITEABLE | PG_NEED_COMMIT)))
vm_object_set_writeable_dirty(object);
swap_pager_page_inserted(m);
return TRUE;
}
void
vm_page_remove(vm_page_t m)
{
vm_object_t object;
if (m->object == NULL) {
return;
}
if ((m->busy_count & PBUSY_LOCKED) == 0)
panic("vm_page_remove: page not busy");
object = m->object;
vm_object_hold(object);
vm_page_spin_lock(m);
vm_page_rb_tree_RB_REMOVE(&object->rb_memq, m);
--object->resident_page_count;
--mycpu->gd_vmtotal.t_rm;
m->object = NULL;
atomic_add_int(&object->generation, 1);
vm_page_spin_unlock(m);
vm_object_drop(object);
}
static __inline
struct vm_page_hash_elm *
vm_page_hash_hash(vm_object_t object, vm_pindex_t pindex)
{
size_t hi;
hi = iscsi_crc32(&object, sizeof(object)) << 2;
hi ^= hi >> (23 - 2);
hi += pindex * VM_PAGE_HASH_SET;
#if 0
hi = (intptr_t)object ^ object->pg_color ^ pindex;
hi += object->pg_color * pindex;
hi = hi ^ (hi >> 20);
#endif
hi &= vm_page_hash_size - 1;
return (&vm_page_hash[hi]);
}
vm_page_t
vm_page_hash_get(vm_object_t object, vm_pindex_t pindex)
{
struct vm_page_hash_elm *mp;
vm_page_t m;
int i;
if (__predict_false(vm_page_hash == NULL))
return NULL;
mp = vm_page_hash_hash(object, pindex);
for (i = 0; i < VM_PAGE_HASH_SET; ++i, ++mp) {
if (mp->object != object ||
mp->pindex != pindex) {
continue;
}
m = mp->m;
cpu_ccfence();
if (m == NULL)
continue;
if (m->object != object || m->pindex != pindex)
continue;
if (vm_page_sbusy_try(m))
continue;
if (m->object == object && m->pindex == pindex) {
if (mp->ticks != ticks)
mp->ticks = ticks;
return m;
}
vm_page_sbusy_drop(m);
}
return NULL;
}
static __inline
void
vm_page_hash_enter(vm_page_t m)
{
struct vm_page_hash_elm *mp;
struct vm_page_hash_elm *best;
vm_object_t object;
vm_pindex_t pindex;
int best_delta;
int delta;
int i;
if ((m->flags & PG_MAPPEDMULTI) == 0)
return;
if (__predict_false(vm_page_hash == NULL ||
m < &vm_page_array[0] ||
m >= &vm_page_array[vm_page_array_size])) {
return;
}
if (__predict_false(m->object == NULL))
return;
#if 0
if (m->object->ref_count <= 2 || (m->object->flags & OBJ_ONEMAPPING))
return;
#endif
if (vm_page_hash_vnode_only && m->object->type != OBJT_VNODE)
return;
object = m->object;
pindex = m->pindex;
mp = vm_page_hash_hash(object, pindex);
best = mp;
best_delta = ticks - best->ticks;
for (i = 0; i < VM_PAGE_HASH_SET; ++i, ++mp) {
if (mp->m == m &&
mp->object == object &&
mp->pindex == pindex) {
if (mp->ticks != ticks)
mp->ticks = ticks;
return;
}
delta = ticks - mp->ticks;
if (delta < -1)
best = mp;
if (best_delta < delta)
best = mp;
}
best->m = m;
best->object = object;
best->pindex = pindex;
best->ticks = ticks;
}
vm_page_t
vm_page_lookup(vm_object_t object, vm_pindex_t pindex)
{
vm_page_t m;
ASSERT_LWKT_TOKEN_HELD(vm_object_token(object));
m = vm_page_rb_tree_RB_LOOKUP(&object->rb_memq, pindex);
if (m) {
KKASSERT(m->object == object && m->pindex == pindex);
vm_page_hash_enter(m);
}
return(m);
}
vm_page_t
VM_PAGE_DEBUG_EXT(vm_page_lookup_busy_wait)(struct vm_object *object,
vm_pindex_t pindex,
int also_m_busy, const char *msg
VM_PAGE_DEBUG_ARGS)
{
u_int32_t busy_count;
vm_page_t m;
ASSERT_LWKT_TOKEN_HELD(vm_object_token(object));
m = vm_page_rb_tree_RB_LOOKUP(&object->rb_memq, pindex);
while (m) {
KKASSERT(m->object == object && m->pindex == pindex);
busy_count = m->busy_count;
cpu_ccfence();
if (busy_count & PBUSY_LOCKED) {
tsleep_interlock(m, 0);
if (atomic_cmpset_int(&m->busy_count, busy_count,
busy_count | PBUSY_WANTED)) {
atomic_set_int(&m->flags, PG_REFERENCED);
tsleep(m, PINTERLOCKED, msg, 0);
m = vm_page_rb_tree_RB_LOOKUP(&object->rb_memq,
pindex);
}
} else if (also_m_busy && busy_count) {
tsleep_interlock(m, 0);
if (atomic_cmpset_int(&m->busy_count, busy_count,
busy_count | PBUSY_WANTED)) {
atomic_set_int(&m->flags, PG_REFERENCED);
tsleep(m, PINTERLOCKED, msg, 0);
m = vm_page_rb_tree_RB_LOOKUP(&object->rb_memq,
pindex);
}
} else if (atomic_cmpset_int(&m->busy_count, busy_count,
busy_count | PBUSY_LOCKED)) {
#ifdef VM_PAGE_DEBUG
m->busy_func = func;
m->busy_line = lineno;
#endif
vm_page_hash_enter(m);
break;
}
}
return m;
}
vm_page_t
VM_PAGE_DEBUG_EXT(vm_page_lookup_busy_try)(struct vm_object *object,
vm_pindex_t pindex,
int also_m_busy, int *errorp
VM_PAGE_DEBUG_ARGS)
{
u_int32_t busy_count;
vm_page_t m;
ASSERT_LWKT_TOKEN_HELD(vm_object_token(object));
m = vm_page_rb_tree_RB_LOOKUP(&object->rb_memq, pindex);
*errorp = FALSE;
while (m) {
KKASSERT(m->object == object && m->pindex == pindex);
busy_count = m->busy_count;
cpu_ccfence();
if (busy_count & PBUSY_LOCKED) {
*errorp = TRUE;
break;
}
if (also_m_busy && busy_count) {
*errorp = TRUE;
break;
}
if (atomic_cmpset_int(&m->busy_count, busy_count,
busy_count | PBUSY_LOCKED)) {
#ifdef VM_PAGE_DEBUG
m->busy_func = func;
m->busy_line = lineno;
#endif
vm_page_hash_enter(m);
break;
}
}
return m;
}
vm_page_t
vm_page_lookup_sbusy_try(struct vm_object *object, vm_pindex_t pindex,
int pgoff, int pgbytes)
{
vm_page_t m;
ASSERT_LWKT_TOKEN_HELD(vm_object_token(object));
m = vm_page_rb_tree_RB_LOOKUP(&object->rb_memq, pindex);
if (m) {
if ((m->valid != VM_PAGE_BITS_ALL &&
!vm_page_is_valid(m, pgoff, pgbytes)) ||
(m->flags & PG_FICTITIOUS)) {
m = NULL;
} else if (vm_page_sbusy_try(m)) {
m = NULL;
} else if ((m->valid != VM_PAGE_BITS_ALL &&
!vm_page_is_valid(m, pgoff, pgbytes)) ||
(m->flags & PG_FICTITIOUS)) {
vm_page_sbusy_drop(m);
m = NULL;
} else {
vm_page_hash_enter(m);
}
}
return m;
}
vm_page_t
vm_page_next(vm_page_t m)
{
vm_page_t next;
next = vm_page_rb_tree_RB_NEXT(m);
if (next && next->pindex != m->pindex + 1)
next = NULL;
return (next);
}
void
vm_page_rename(vm_page_t m, vm_object_t new_object, vm_pindex_t new_pindex)
{
KKASSERT(m->busy_count & PBUSY_LOCKED);
ASSERT_LWKT_TOKEN_HELD_EXCL(vm_object_token(new_object));
if (m->object) {
ASSERT_LWKT_TOKEN_HELD_EXCL(vm_object_token(m->object));
vm_page_remove(m);
}
if (vm_page_insert(m, new_object, new_pindex) == FALSE) {
panic("vm_page_rename: target exists (%p,%"PRIu64")",
new_object, new_pindex);
}
if (m->queue - m->pc == PQ_CACHE)
vm_page_deactivate(m);
vm_page_dirty(m);
}
void
vm_page_unqueue_nowakeup(vm_page_t m)
{
vm_page_and_queue_spin_lock(m);
(void)_vm_page_rem_queue_spinlocked(m);
vm_page_spin_unlock(m);
}
void
vm_page_unqueue(vm_page_t m)
{
u_short queue;
vm_page_and_queue_spin_lock(m);
queue = _vm_page_rem_queue_spinlocked(m);
if (queue == PQ_FREE || queue == PQ_CACHE) {
vm_page_spin_unlock(m);
pagedaemon_wakeup();
} else {
vm_page_spin_unlock(m);
}
}
static __inline
vm_page_t
_vm_page_list_find(int basequeue, int index)
{
struct vpgqueues *pq;
vm_page_t m;
index &= PQ_L2_MASK;
pq = &vm_page_queues[basequeue + index];
if (TAILQ_FIRST(&pq->pl)) {
spin_lock(&pq->spin);
TAILQ_FOREACH(m, &pq->pl, pageq) {
if (spin_trylock(&m->spin) == 0)
continue;
KKASSERT(m->queue == basequeue + index);
pq->lastq = -1;
return(m);
}
spin_unlock(&pq->spin);
}
m = _vm_page_list_find_wide(basequeue, index, &pq->lastq);
return(m);
}
static vm_page_t
_vm_page_list_find_wide(int basequeue, int index, int *lastp)
{
struct vpgqueues *pq;
vm_page_t m = NULL;
int pqmask = set_assoc_mask >> 1;
int pqi;
int range;
int skip_start;
int skip_next;
int count;
if (*lastp >= 0)
index = *lastp;
index &= PQ_L2_MASK;
pq = &vm_page_queues[basequeue];
count = 0;
skip_start = -1;
skip_next = -1;
do {
pqmask = (pqmask << 1) | 1;
pqi = index;
range = pqmask + 1;
while (range > 0) {
if (pqi >= skip_start && pqi < skip_next) {
range -= skip_next - pqi;
pqi = (pqi & ~pqmask) | (skip_next & pqmask);
}
if (range > 0 && TAILQ_FIRST(&pq[pqi].pl)) {
spin_lock(&pq[pqi].spin);
TAILQ_FOREACH(m, &pq[pqi].pl, pageq) {
if (spin_trylock(&m->spin) == 0)
continue;
KKASSERT(m->queue == basequeue + pqi);
if (count >= 8)
*lastp = pqi & PQ_L2_MASK;
return(m);
}
spin_unlock(&pq[pqi].spin);
}
--range;
++count;
pqi = (pqi & ~pqmask) | ((pqi + 1) & pqmask);
}
skip_start = pqi & ~pqmask;
skip_next = (pqi | pqmask) + 1;
} while (pqmask != PQ_L2_MASK);
return(m);
}
static __inline
vm_page_t
_vm_page_list_find2(int bq1, int bq2, int index)
{
struct vpgqueues *pq1;
struct vpgqueues *pq2;
vm_page_t m;
index &= PQ_L2_MASK;
pq1 = &vm_page_queues[bq1 + index];
pq2 = &vm_page_queues[bq2 + index];
if (TAILQ_FIRST(&pq1->pl)) {
spin_lock(&pq1->spin);
TAILQ_FOREACH(m, &pq1->pl, pageq) {
if (spin_trylock(&m->spin) == 0)
continue;
KKASSERT(m->queue == bq1 + index);
pq1->lastq = -1;
pq2->lastq = -1;
return(m);
}
spin_unlock(&pq1->spin);
}
m = _vm_page_list_find2_wide(bq1, bq2, index, &pq1->lastq, &pq2->lastq);
return(m);
}
static vm_page_t
_vm_page_list_find2_wide(int basequeue1, int basequeue2, int index,
int *lastp1, int *lastp2)
{
struct vpgqueues *pq1;
struct vpgqueues *pq2;
vm_page_t m = NULL;
int pqmask1, pqmask2;
int pqi;
int range;
int skip_start1, skip_start2;
int skip_next1, skip_next2;
int count1, count2;
if (*lastp1 >= 0)
index = *lastp1;
index &= PQ_L2_MASK;
pqmask1 = set_assoc_mask >> 1;
pq1 = &vm_page_queues[basequeue1];
count1 = 0;
skip_start1 = -1;
skip_next1 = -1;
pqmask2 = set_assoc_mask >> 1;
pq2 = &vm_page_queues[basequeue2];
count2 = 0;
skip_start2 = -1;
skip_next2 = -1;
do {
if (pqmask1 == PQ_L2_MASK)
goto skip2;
pqmask1 = (pqmask1 << 1) | 1;
pqi = index;
range = pqmask1 + 1;
while (range > 0) {
if (pqi >= skip_start1 && pqi < skip_next1) {
range -= skip_next1 - pqi;
pqi = (pqi & ~pqmask1) | (skip_next1 & pqmask1);
}
if (range > 0 && TAILQ_FIRST(&pq1[pqi].pl)) {
spin_lock(&pq1[pqi].spin);
TAILQ_FOREACH(m, &pq1[pqi].pl, pageq) {
if (spin_trylock(&m->spin) == 0)
continue;
KKASSERT(m->queue == basequeue1 + pqi);
if (count1 >= 8)
*lastp1 = pqi & PQ_L2_MASK;
return(m);
}
spin_unlock(&pq1[pqi].spin);
}
--range;
++count1;
pqi = (pqi & ~pqmask1) | ((pqi + 1) & pqmask1);
}
skip_start1 = pqi & ~pqmask1;
skip_next1 = (pqi | pqmask1) + 1;
skip2:
if (pqmask1 < ((set_assoc_mask << 1) | 1))
continue;
pqmask2 = (pqmask2 << 1) | 1;
pqi = index;
range = pqmask2 + 1;
while (range > 0) {
if (pqi >= skip_start2 && pqi < skip_next2) {
range -= skip_next2 - pqi;
pqi = (pqi & ~pqmask2) | (skip_next2 & pqmask2);
}
if (range > 0 && TAILQ_FIRST(&pq2[pqi].pl)) {
spin_lock(&pq2[pqi].spin);
TAILQ_FOREACH(m, &pq2[pqi].pl, pageq) {
if (spin_trylock(&m->spin) == 0)
continue;
KKASSERT(m->queue == basequeue2 + pqi);
if (count2 >= 8)
*lastp2 = pqi & PQ_L2_MASK;
return(m);
}
spin_unlock(&pq2[pqi].spin);
}
--range;
++count2;
pqi = (pqi & ~pqmask2) | ((pqi + 1) & pqmask2);
}
skip_start2 = pqi & ~pqmask2;
skip_next2 = (pqi | pqmask2) + 1;
} while (pqmask1 != PQ_L2_MASK && pqmask2 != PQ_L2_MASK);
return(m);
}
vm_page_t
vm_page_list_find(int basequeue, int index)
{
vm_page_t m;
m = _vm_page_list_find(basequeue, index);
if (m)
_vm_page_rem_queue_spinlocked(m);
return m;
}
static vm_page_t
vm_page_select_cache(u_short pg_color)
{
vm_page_t m;
for (;;) {
m = _vm_page_list_find(PQ_CACHE, pg_color);
if (m == NULL)
break;
_vm_page_rem_queue_spinlocked(m);
if (vm_page_busy_try(m, TRUE)) {
_vm_page_deactivate_locked(m, 0);
vm_page_spin_unlock(m);
} else {
if ((m->flags & (PG_NEED_COMMIT | PG_MAPPED)) == 0 &&
m->hold_count == 0 &&
m->wire_count == 0 &&
(m->dirty & m->valid) == 0) {
vm_page_spin_unlock(m);
KKASSERT((m->flags & PG_UNQUEUED) == 0);
pagedaemon_wakeup();
return(m);
}
_vm_page_deactivate_locked(m, 0);
if (_vm_page_wakeup(m)) {
vm_page_spin_unlock(m);
wakeup(m);
} else {
vm_page_spin_unlock(m);
}
}
}
return (m);
}
static __inline vm_page_t
vm_page_select_free(u_short pg_color)
{
vm_page_t m;
for (;;) {
m = _vm_page_list_find(PQ_FREE, pg_color);
if (m == NULL)
break;
_vm_page_rem_queue_spinlocked(m);
if (vm_page_busy_try(m, TRUE)) {
_vm_page_deactivate_locked(m, 0);
vm_page_spin_unlock(m);
} else {
KKASSERT((m->flags & (PG_UNQUEUED |
PG_NEED_COMMIT)) == 0);
KASSERT(m->hold_count == 0,
("m->hold_count is not zero "
"pg %p q=%d flags=%08x hold=%d wire=%d",
m, m->queue, m->flags,
m->hold_count, m->wire_count));
KKASSERT(m->wire_count == 0);
vm_page_spin_unlock(m);
pagedaemon_wakeup();
return(m);
}
}
return(m);
}
static __inline vm_page_t
vm_page_select_free_or_cache(u_short pg_color, int *fromcachep)
{
vm_page_t m;
*fromcachep = 0;
for (;;) {
m = _vm_page_list_find2(PQ_FREE, PQ_CACHE, pg_color);
if (m == NULL)
break;
if (vm_page_busy_try(m, TRUE)) {
_vm_page_rem_queue_spinlocked(m);
_vm_page_deactivate_locked(m, 0);
vm_page_spin_unlock(m);
} else if (m->queue - m->pc == PQ_FREE) {
_vm_page_rem_queue_spinlocked(m);
KKASSERT((m->flags & (PG_UNQUEUED |
PG_NEED_COMMIT)) == 0);
KASSERT(m->hold_count == 0,
("m->hold_count is not zero "
"pg %p q=%d flags=%08x hold=%d wire=%d",
m, m->queue, m->flags,
m->hold_count, m->wire_count));
KKASSERT(m->wire_count == 0);
vm_page_spin_unlock(m);
pagedaemon_wakeup();
return(m);
} else {
_vm_page_rem_queue_spinlocked(m);
if ((m->flags & (PG_NEED_COMMIT | PG_MAPPED)) == 0 &&
m->hold_count == 0 &&
m->wire_count == 0 &&
(m->dirty & m->valid) == 0) {
vm_page_spin_unlock(m);
KKASSERT((m->flags & PG_UNQUEUED) == 0);
pagedaemon_wakeup();
*fromcachep = 1;
return(m);
}
_vm_page_deactivate_locked(m, 0);
if (_vm_page_wakeup(m)) {
vm_page_spin_unlock(m);
wakeup(m);
} else {
vm_page_spin_unlock(m);
}
}
}
return(m);
}
vm_page_t
vm_page_alloc(vm_object_t object, vm_pindex_t pindex, int page_req)
{
globaldata_t gd;
vm_object_t obj;
vm_page_t m;
u_short pg_color;
int cpuid_local;
int fromcache;
#if 0
if (gd->gd_vmpg_count && (page_req & VM_ALLOC_USE_GD)) {
crit_enter_gd(gd);
if (gd->gd_vmpg_count) {
m = gd->gd_vmpg_array[--gd->gd_vmpg_count];
crit_exit_gd(gd);
goto done;
}
crit_exit_gd(gd);
}
#endif
m = NULL;
if (page_req & VM_ALLOC_CPU_SPEC)
cpuid_local = VM_ALLOC_GETCPU(page_req);
else
cpuid_local = mycpu->gd_cpuid;
pg_color = vm_get_pg_color(cpuid_local, object, pindex);
KKASSERT(page_req & (VM_ALLOC_NORMAL | VM_ALLOC_QUICK |
VM_ALLOC_INTERRUPT | VM_ALLOC_SYSTEM));
if (curthread->td_flags & TDF_SYSTHREAD)
page_req |= VM_ALLOC_SYSTEM;
loop:
gd = mycpu;
if (gd->gd_vmstats.v_free_count >= gd->gd_vmstats.v_free_reserved ||
((page_req & VM_ALLOC_INTERRUPT) &&
gd->gd_vmstats.v_free_count > 0) ||
((page_req & VM_ALLOC_SYSTEM) &&
gd->gd_vmstats.v_cache_count == 0 &&
gd->gd_vmstats.v_free_count >
gd->gd_vmstats.v_interrupt_free_min)
) {
if (page_req & VM_ALLOC_NORMAL) {
m = vm_page_select_free_or_cache(pg_color, &fromcache);
if (m && fromcache)
goto found_cache;
} else {
m = vm_page_select_free(pg_color);
}
} else if (page_req & VM_ALLOC_NORMAL) {
#ifdef INVARIANTS
if (curthread->td_preempted) {
kprintf("vm_page_alloc(): warning, attempt to allocate"
" cache page from preempting interrupt\n");
m = NULL;
} else {
m = vm_page_select_cache(pg_color);
}
#else
m = vm_page_select_cache(pg_color);
#endif
if (m != NULL) {
found_cache:
KASSERT(m->dirty == 0,
("Found dirty cache page %p", m));
if ((obj = m->object) != NULL) {
if (vm_object_hold_try(obj)) {
if (__predict_false((m->flags & (PG_MAPPED|PG_WRITEABLE)) != 0))
vm_page_protect(m, VM_PROT_NONE);
vm_page_free(m);
vm_object_drop(obj);
} else {
vm_page_deactivate(m);
vm_page_wakeup(m);
}
} else {
if (__predict_false((m->flags & (PG_MAPPED|PG_WRITEABLE)) != 0))
vm_page_protect(m, VM_PROT_NONE);
vm_page_free(m);
}
goto loop;
}
atomic_add_int(&vm_pageout_deficit, 1);
pagedaemon_wakeup();
return (NULL);
} else {
atomic_add_int(&vm_pageout_deficit, 1);
pagedaemon_wakeup();
return (NULL);
}
if (m == NULL) {
vmstats_rollup();
goto loop;
}
KASSERT(m->dirty == 0,
("vm_page_alloc: free/cache page %p was dirty", m));
KKASSERT(m->queue == PQ_NONE);
#if 0
done:
#endif
vm_page_flag_clear(m, ~PG_KEEP_NEWPAGE_MASK);
KKASSERT(m->wire_count == 0);
KKASSERT((m->busy_count & PBUSY_MASK) == 0);
m->act_count = 0;
m->valid = 0;
if (object) {
if (vm_page_insert(m, object, pindex) == FALSE) {
vm_page_free(m);
if ((page_req & VM_ALLOC_NULL_OK) == 0)
panic("PAGE RACE %p[%ld]/%p",
object, (long)pindex, m);
m = NULL;
}
} else {
m->pindex = pindex;
}
pagedaemon_wakeup();
return (m);
}
vm_size_t
vm_contig_avail_pages(void)
{
alist_blk_t blk;
alist_blk_t count;
alist_blk_t bfree;
spin_lock(&vm_contig_spin);
bfree = alist_free_info(&vm_contig_alist, &blk, &count);
spin_unlock(&vm_contig_spin);
return bfree;
}
vm_page_t
vm_page_alloc_contig(vm_paddr_t low, vm_paddr_t high,
unsigned long alignment, unsigned long boundary,
unsigned long size, vm_memattr_t memattr)
{
alist_blk_t blk;
vm_page_t m;
vm_pindex_t i;
#if 0
static vm_pindex_t contig_rover;
#endif
alignment >>= PAGE_SHIFT;
if (alignment == 0)
alignment = 1;
boundary >>= PAGE_SHIFT;
if (boundary == 0)
boundary = 1;
size = (size + PAGE_MASK) >> PAGE_SHIFT;
#if 0
if (high == BUS_SPACE_MAXADDR && alignment <= PAGE_SIZE &&
boundary <= PAGE_SIZE && size == 1 &&
memattr == VM_MEMATTR_DEFAULT) {
m = vm_page_alloc(NULL, (contig_rover++) & 0x7FFFFFFF,
VM_ALLOC_NORMAL | VM_ALLOC_SYSTEM |
VM_ALLOC_INTERRUPT);
m->valid = VM_PAGE_BITS_ALL;
vm_page_wire(m);
vm_page_wakeup(m);
} else
#endif
{
spin_lock(&vm_contig_spin);
blk = alist_alloc(&vm_contig_alist, 0, size);
if (blk == ALIST_BLOCK_NONE) {
spin_unlock(&vm_contig_spin);
if (bootverbose) {
kprintf("vm_page_alloc_contig: %ldk nospace\n",
(size << PAGE_SHIFT) / 1024);
print_backtrace(5);
}
return(NULL);
}
if (high && ((vm_paddr_t)(blk + size) << PAGE_SHIFT) > high) {
alist_free(&vm_contig_alist, blk, size);
spin_unlock(&vm_contig_spin);
if (bootverbose) {
kprintf("vm_page_alloc_contig: %ldk high "
"%016jx failed\n",
(size << PAGE_SHIFT) / 1024,
(intmax_t)high);
}
return(NULL);
}
spin_unlock(&vm_contig_spin);
m = PHYS_TO_VM_PAGE((vm_paddr_t)blk << PAGE_SHIFT);
}
if (vm_contig_verbose) {
kprintf("vm_page_alloc_contig: %016jx/%ldk "
"(%016jx-%016jx al=%lu bo=%lu pgs=%lu attr=%d\n",
(intmax_t)m->phys_addr,
(size << PAGE_SHIFT) / 1024,
low, high, alignment, boundary, size, memattr);
}
if (memattr != VM_MEMATTR_DEFAULT) {
for (i = 0; i < size; ++i) {
KKASSERT(m[i].flags & PG_FICTITIOUS);
pmap_page_set_memattr(&m[i], memattr);
}
}
return m;
}
void
vm_page_free_contig(vm_page_t m, unsigned long size)
{
vm_paddr_t pa = VM_PAGE_TO_PHYS(m);
vm_pindex_t start = pa >> PAGE_SHIFT;
vm_pindex_t pages = (size + PAGE_MASK) >> PAGE_SHIFT;
if (vm_contig_verbose) {
kprintf("vm_page_free_contig: %016jx/%ldk\n",
(intmax_t)pa, size / 1024);
}
if (pa < vm_low_phys_reserved) {
KKASSERT(m->wire_count == 1);
KKASSERT(m->flags & PG_FICTITIOUS);
KKASSERT(pa + size <= vm_low_phys_reserved);
spin_lock(&vm_contig_spin);
alist_free(&vm_contig_alist, start, pages);
spin_unlock(&vm_contig_spin);
} else {
while (pages) {
vm_page_busy_wait(m, FALSE, "cpgfr");
vm_page_unwire(m, 0);
vm_page_free(m);
--pages;
++m;
}
}
}
void
vm_wait_nominal(void)
{
while (vm_paging_min())
vm_wait(0);
}
int
vm_test_nominal(void)
{
if (vm_paging_min())
return(1);
return(0);
}
void
vm_wait(int timo)
{
if (timo == 0)
timo = hz;
lwkt_gettoken(&vm_token);
if (curthread == pagethread ||
curthread == emergpager) {
if (vm_paging_min()) {
vm_pageout_pages_needed = 1;
tsleep(&vm_pageout_pages_needed, 0, "VMWait", timo);
}
} else {
int nice;
nice = curthread->td_proc ? curthread->td_proc->p_nice : 0;
if (vm_paging_min_nice(nice + 1))
{
if (vm_pages_needed <= 1) {
++vm_pages_needed;
wakeup(&vm_pages_needed);
}
++vm_pages_waiting;
tsleep(&vmstats.v_free_count, 0, "vmwait", timo);
}
}
lwkt_reltoken(&vm_token);
}
void
vm_wait_pfault(void)
{
int nice;
nice = curthread->td_proc ? curthread->td_proc->p_nice : 0;
if (vm_paging_min_nice(nice)) {
lwkt_gettoken(&vm_token);
do {
thread_t td;
if (vm_pages_needed <= 1) {
++vm_pages_needed;
wakeup(&vm_pages_needed);
}
++vm_pages_waiting;
tsleep(&vmstats.v_free_count, 0, "pfault",
hz / 10 + 1);
td = curthread;
if (td->td_proc &&
(td->td_proc->p_flags & P_LOWMEMKILL))
{
break;
}
} while (vm_paging_severe());
lwkt_reltoken(&vm_token);
}
}
void
vm_page_activate(vm_page_t m)
{
u_short oqueue;
if (m->queue - m->pc == PQ_ACTIVE ||
(m->flags & (PG_FICTITIOUS | PG_UNQUEUED))) {
if (m->act_count < ACT_INIT)
m->act_count = ACT_INIT;
return;
}
vm_page_spin_lock(m);
if (m->queue - m->pc != PQ_ACTIVE &&
(m->flags & (PG_FICTITIOUS | PG_UNQUEUED)) == 0) {
_vm_page_queue_spin_lock(m);
oqueue = _vm_page_rem_queue_spinlocked(m);
if (oqueue == PQ_CACHE)
mycpu->gd_cnt.v_reactivated++;
if (m->act_count < ACT_INIT)
m->act_count = ACT_INIT;
_vm_page_add_queue_spinlocked(m, PQ_ACTIVE + m->pc, 0);
_vm_page_and_queue_spin_unlock(m);
if (oqueue == PQ_CACHE || oqueue == PQ_FREE)
pagedaemon_wakeup();
} else {
if (m->act_count < ACT_INIT)
m->act_count = ACT_INIT;
vm_page_spin_unlock(m);
}
}
void
vm_page_soft_activate(vm_page_t m)
{
if (m->queue - m->pc == PQ_ACTIVE ||
(m->flags & (PG_FICTITIOUS | PG_UNQUEUED))) {
if (m->act_count < ACT_INIT)
m->act_count = ACT_INIT;
} else {
vm_page_activate(m);
}
}
static __inline void
vm_page_free_wakeup(void)
{
globaldata_t gd = mycpu;
if (vm_pageout_pages_needed &&
gd->gd_vmstats.v_cache_count + gd->gd_vmstats.v_free_count >=
gd->gd_vmstats.v_pageout_free_min
) {
vm_pageout_pages_needed = 0;
wakeup(&vm_pageout_pages_needed);
}
if (vm_pages_waiting) {
if (!vm_paging_min_dnc(vm_page_free_hysteresis) ||
!vm_paging_target1())
{
vm_pages_waiting = 0;
wakeup(&vmstats.v_free_count);
++mycpu->gd_cnt.v_ppwakeups;
}
}
}
void
vm_page_free_toq(vm_page_t m)
{
mycpu->gd_cnt.v_tfree++;
if (m->flags & (PG_MAPPED | PG_WRITEABLE))
pmap_mapped_sync(m);
KKASSERT((m->flags & PG_MAPPED) == 0);
KKASSERT(m->busy_count & PBUSY_LOCKED);
if ((m->busy_count & PBUSY_MASK) || ((m->queue - m->pc) == PQ_FREE)) {
kprintf("vm_page_free: pindex(%lu), busy %08x, "
"hold(%d)\n",
(u_long)m->pindex, m->busy_count, m->hold_count);
if ((m->queue - m->pc) == PQ_FREE)
panic("vm_page_free: freeing free page");
else
panic("vm_page_free: freeing busy page");
}
vm_page_remove(m);
if ((m->flags & PG_FICTITIOUS) != 0) {
KKASSERT(m->queue == PQ_NONE);
vm_page_wakeup(m);
return;
}
vm_page_and_queue_spin_lock(m);
_vm_page_rem_queue_spinlocked(m);
m->valid = 0;
vm_page_undirty(m);
if (m->wire_count != 0) {
if (m->wire_count > 1) {
panic(
"vm_page_free: invalid wire count (%d), pindex: 0x%lx",
m->wire_count, (long)m->pindex);
}
panic("vm_page_free: freeing wired page");
}
if (!MD_PAGE_FREEABLE(m))
panic("vm_page_free: page %p is still mapped!", m);
vm_page_flag_clear(m, PG_NEED_COMMIT | PG_UNQUEUED);
if (m->hold_count != 0) {
_vm_page_add_queue_spinlocked(m, PQ_HOLD + m->pc, 0);
} else {
_vm_page_add_queue_spinlocked(m, PQ_FREE + m->pc, 1);
}
_vm_page_queue_spin_unlock(m);
if (_vm_page_wakeup(m)) {
vm_page_spin_unlock(m);
wakeup(m);
} else {
vm_page_spin_unlock(m);
}
vm_page_free_wakeup();
}
void
vm_page_wire(vm_page_t m)
{
KKASSERT(m->busy_count & PBUSY_LOCKED);
if ((m->flags & PG_FICTITIOUS) == 0) {
if (atomic_fetchadd_int(&m->wire_count, 1) == 0) {
atomic_add_long(&mycpu->gd_vmstats_adj.v_wire_count, 1);
}
KASSERT(m->wire_count != 0,
("vm_page_wire: wire_count overflow m=%p", m));
}
}
void
vm_page_unwire(vm_page_t m, int activate)
{
KKASSERT(activate < 0 || (m->busy_count & PBUSY_LOCKED));
if (m->flags & PG_FICTITIOUS) {
} else if ((int)m->wire_count <= 0) {
panic("vm_page_unwire: invalid wire count: %d", m->wire_count);
} else {
if (atomic_fetchadd_int(&m->wire_count, -1) == 1) {
atomic_add_long(&mycpu->gd_vmstats_adj.v_wire_count,-1);
if (activate < 0 || (m->flags & PG_UNQUEUED)) {
;
} else if (activate || (m->flags & PG_NEED_COMMIT)) {
vm_page_activate(m);
} else {
vm_page_deactivate(m);
}
}
}
}
static void
_vm_page_deactivate_locked(vm_page_t m, int athead)
{
u_short oqueue;
if (m->queue - m->pc == PQ_INACTIVE ||
(m->flags & (PG_FICTITIOUS | PG_UNQUEUED))) {
return;
}
_vm_page_queue_spin_lock(m);
oqueue = _vm_page_rem_queue_spinlocked(m);
if ((m->flags & (PG_FICTITIOUS | PG_UNQUEUED)) == 0) {
if (oqueue == PQ_CACHE)
mycpu->gd_cnt.v_reactivated++;
vm_page_flag_clear(m, PG_WINATCFLS);
_vm_page_add_queue_spinlocked(m, PQ_INACTIVE + m->pc, athead);
if (athead == 0) {
atomic_add_long(
&vm_page_queues[PQ_INACTIVE + m->pc].adds, 1);
}
}
_vm_page_queue_spin_unlock(m);
}
void
vm_page_deactivate(vm_page_t m)
{
if (m->queue - m->pc != PQ_INACTIVE &&
(m->flags & (PG_FICTITIOUS | PG_UNQUEUED)) == 0) {
vm_page_spin_lock(m);
_vm_page_deactivate_locked(m, 0);
vm_page_spin_unlock(m);
}
}
void
vm_page_deactivate_locked(vm_page_t m)
{
_vm_page_deactivate_locked(m, 0);
}
int
vm_page_try_to_cache(vm_page_t m)
{
if (m->dirty || m->hold_count || m->wire_count ||
m->queue - m->pc == PQ_CACHE ||
(m->flags & (PG_UNQUEUED | PG_NEED_COMMIT | PG_FICTITIOUS))) {
vm_page_wakeup(m);
return(0);
}
vm_page_test_dirty(m);
if (m->dirty || (m->flags & PG_NEED_COMMIT)) {
if (m->queue - m->pc == PQ_ACTIVE)
vm_page_deactivate(m);
vm_page_wakeup(m);
return(0);
}
vm_page_cache(m);
return(1);
}
int
vm_page_try_to_free(vm_page_t m)
{
if (vm_page_busy_try(m, TRUE))
return(0);
if (m->dirty ||
m->hold_count ||
m->wire_count ||
(m->flags & (PG_UNQUEUED |
PG_NEED_COMMIT |
PG_FICTITIOUS)) ||
m->queue - m->pc == PQ_FREE ||
m->queue - m->pc == PQ_HOLD) {
vm_page_wakeup(m);
return(0);
}
vm_page_test_dirty(m);
if (m->dirty || (m->flags & PG_NEED_COMMIT)) {
vm_page_wakeup(m);
return(0);
}
vm_page_protect(m, VM_PROT_NONE);
if (m->dirty || (m->flags & PG_NEED_COMMIT)) {
vm_page_wakeup(m);
return(0);
}
vm_page_free(m);
return(1);
}
void
vm_page_cache(vm_page_t m)
{
if ((m->flags & (PG_UNQUEUED | PG_NEED_COMMIT | PG_FICTITIOUS)) ||
(m->busy_count & PBUSY_MASK) ||
m->wire_count || m->hold_count) {
vm_page_wakeup(m);
return;
}
if ((m->queue - m->pc) == PQ_CACHE) {
KKASSERT((m->flags & PG_MAPPED) == 0);
vm_page_wakeup(m);
return;
}
#if 0
if (m->dirty) {
panic("vm_page_cache: caching a dirty page, pindex: %ld",
(long)m->pindex);
}
#endif
if (m->flags & (PG_MAPPED | PG_WRITEABLE)) {
vm_page_protect(m, VM_PROT_NONE);
pmap_mapped_sync(m);
}
if ((m->flags & (PG_UNQUEUED | PG_MAPPED)) ||
(m->busy_count & PBUSY_MASK) ||
m->wire_count || m->hold_count) {
vm_page_wakeup(m);
} else if (m->dirty || (m->flags & PG_NEED_COMMIT)) {
vm_page_deactivate(m);
vm_page_wakeup(m);
} else {
_vm_page_and_queue_spin_lock(m);
_vm_page_rem_queue_spinlocked(m);
_vm_page_add_queue_spinlocked(m, PQ_CACHE + m->pc, 0);
_vm_page_and_queue_spin_unlock(m);
vm_page_wakeup(m);
vm_page_free_wakeup();
}
}
void
vm_page_dontneed(vm_page_t m)
{
static int dnweight;
int dnw;
int head;
dnw = ++dnweight;
if ((dnw & 0x01F0) == 0 ||
m->queue - m->pc == PQ_INACTIVE ||
m->queue - m->pc == PQ_CACHE
) {
if (m->act_count >= ACT_INIT)
--m->act_count;
return;
}
vm_page_flag_clear(m, PG_REFERENCED);
pmap_clear_reference(m);
if (m->dirty == 0)
vm_page_test_dirty(m);
if (m->dirty || (dnw & 0x0070) == 0) {
head = 0;
} else {
head = 1;
}
vm_page_spin_lock(m);
_vm_page_deactivate_locked(m, head);
vm_page_spin_unlock(m);
}
void
vm_page_io_start(vm_page_t m)
{
uint32_t ocount;
ocount = atomic_fetchadd_int(&m->busy_count, 1);
KKASSERT(ocount & PBUSY_LOCKED);
}
void
vm_page_io_finish(vm_page_t m)
{
uint32_t ocount;
ocount = atomic_fetchadd_int(&m->busy_count, -1);
KKASSERT(ocount & PBUSY_MASK);
#if 0
if (((ocount - 1) & (PBUSY_LOCKED | PBUSY_MASK)) == 0)
wakeup(m);
#endif
}
int
vm_page_sbusy_try(vm_page_t m)
{
uint32_t ocount;
for (;;) {
ocount = m->busy_count;
cpu_ccfence();
if (ocount & PBUSY_LOCKED)
return 1;
if (atomic_cmpset_int(&m->busy_count, ocount, ocount + 1))
break;
}
return 0;
#if 0
if (m->busy_count & PBUSY_LOCKED)
return 1;
ocount = atomic_fetchadd_int(&m->busy_count, 1);
if (ocount & PBUSY_LOCKED) {
vm_page_sbusy_drop(m);
return 1;
}
return 0;
#endif
}
void
vm_page_need_commit(vm_page_t m)
{
vm_page_flag_set(m, PG_NEED_COMMIT);
vm_object_set_writeable_dirty(m->object);
}
void
vm_page_clear_commit(vm_page_t m)
{
vm_page_flag_clear(m, PG_NEED_COMMIT);
}
vm_page_t
vm_page_alloczwq(vm_pindex_t pindex, int flags)
{
vm_page_t m;
KKASSERT(flags & (VM_ALLOC_NORMAL | VM_ALLOC_QUICK |
VM_ALLOC_INTERRUPT | VM_ALLOC_SYSTEM));
for (;;) {
m = vm_page_alloc(NULL, pindex, flags & ~VM_ALLOC_RETRY);
if (m)
break;
vm_wait(0);
if ((flags & VM_ALLOC_RETRY) == 0)
return NULL;
}
if (flags & (VM_ALLOC_ZERO | VM_ALLOC_FORCE_ZERO)) {
pmap_zero_page(VM_PAGE_TO_PHYS(m));
m->valid = VM_PAGE_BITS_ALL;
}
vm_page_wire(m);
vm_page_wakeup(m);
return(m);
}
void
vm_page_freezwq(vm_page_t m)
{
vm_page_busy_wait(m, FALSE, "pgzwq");
vm_page_unwire(m, 0);
vm_page_free(m);
}
vm_page_t
vm_page_grab(vm_object_t object, vm_pindex_t pindex, int flags)
{
vm_page_t m;
int error;
int shared = 1;
KKASSERT(flags & (VM_ALLOC_NORMAL | VM_ALLOC_QUICK |
VM_ALLOC_INTERRUPT | VM_ALLOC_SYSTEM));
vm_object_hold_shared(object);
for (;;) {
m = vm_page_lookup_busy_try(object, pindex, TRUE, &error);
if (error) {
vm_page_sleep_busy(m, TRUE, "pgrbwt");
if ((flags & VM_ALLOC_RETRY) == 0) {
m = NULL;
break;
}
} else if (m == NULL) {
if (shared) {
vm_object_upgrade(object);
shared = 0;
}
if (flags & VM_ALLOC_RETRY)
flags |= VM_ALLOC_NULL_OK;
m = vm_page_alloc(object, pindex,
flags & ~VM_ALLOC_RETRY);
if (m)
break;
vm_wait(0);
if ((flags & VM_ALLOC_RETRY) == 0)
goto failed;
} else {
break;
}
}
if (m->valid == 0) {
if (flags & (VM_ALLOC_ZERO | VM_ALLOC_FORCE_ZERO)) {
pmap_zero_page(VM_PAGE_TO_PHYS(m));
m->valid = VM_PAGE_BITS_ALL;
}
} else if (flags & VM_ALLOC_FORCE_ZERO) {
pmap_zero_page(VM_PAGE_TO_PHYS(m));
m->valid = VM_PAGE_BITS_ALL;
}
failed:
vm_object_drop(object);
return(m);
}
int
vm_page_bits(int base, int size)
{
int first_bit;
int last_bit;
KASSERT(
base + size <= PAGE_SIZE,
("vm_page_bits: illegal base/size %d/%d", base, size)
);
if (size == 0)
return(0);
first_bit = base >> DEV_BSHIFT;
last_bit = (base + size - 1) >> DEV_BSHIFT;
return ((2 << last_bit) - (1 << first_bit));
}
static void
_vm_page_zero_valid(vm_page_t m, int base, int size)
{
int frag;
int endoff;
if (size == 0)
return;
if ((frag = rounddown2(base, DEV_BSIZE)) != base &&
(m->valid & (1 << (base >> DEV_BSHIFT))) == 0
) {
pmap_zero_page_area(
VM_PAGE_TO_PHYS(m),
frag,
base - frag
);
}
endoff = base + size;
if ((frag = rounddown2(endoff, DEV_BSIZE)) != endoff &&
(m->valid & (1 << (endoff >> DEV_BSHIFT))) == 0
) {
pmap_zero_page_area(
VM_PAGE_TO_PHYS(m),
endoff,
DEV_BSIZE - (endoff & (DEV_BSIZE - 1))
);
}
}
void
vm_page_set_valid(vm_page_t m, int base, int size)
{
_vm_page_zero_valid(m, base, size);
m->valid |= vm_page_bits(base, size);
}
void
vm_page_set_validclean(vm_page_t m, int base, int size)
{
int pagebits;
_vm_page_zero_valid(m, base, size);
pagebits = vm_page_bits(base, size);
m->valid |= pagebits;
m->dirty &= ~pagebits;
if (base == 0 && size == PAGE_SIZE) {
vm_page_flag_clear(m, PG_NOSYNC);
}
}
void
vm_page_set_validdirty(vm_page_t m, int base, int size)
{
int pagebits;
pagebits = vm_page_bits(base, size);
m->valid |= pagebits;
m->dirty |= pagebits;
if (m->object)
vm_object_set_writeable_dirty(m->object);
}
void
vm_page_clear_dirty(vm_page_t m, int base, int size)
{
m->dirty &= ~vm_page_bits(base, size);
if (base == 0 && size == PAGE_SIZE) {
vm_page_flag_clear(m, PG_NOSYNC);
}
}
void
vm_page_dirty(vm_page_t m)
{
#ifdef INVARIANTS
int pqtype = m->queue - m->pc;
#endif
KASSERT(pqtype != PQ_CACHE && pqtype != PQ_FREE,
("vm_page_dirty: page in free/cache queue!"));
if (m->dirty != VM_PAGE_BITS_ALL) {
m->dirty = VM_PAGE_BITS_ALL;
if (m->object)
vm_object_set_writeable_dirty(m->object);
}
}
void
vm_page_set_invalid(vm_page_t m, int base, int size)
{
int bits;
bits = vm_page_bits(base, size);
m->valid &= ~bits;
m->dirty &= ~bits;
atomic_add_int(&m->object->generation, 1);
}
void
vm_page_zero_invalid(vm_page_t m, boolean_t setvalid)
{
int b;
int i;
for (b = i = 0; i <= PAGE_SIZE / DEV_BSIZE; ++i) {
if (i == (PAGE_SIZE / DEV_BSIZE) ||
(m->valid & (1 << i))
) {
if (i > b) {
pmap_zero_page_area(
VM_PAGE_TO_PHYS(m),
b << DEV_BSHIFT,
(i - b) << DEV_BSHIFT
);
}
b = i + 1;
}
}
if (setvalid)
m->valid = VM_PAGE_BITS_ALL;
}
int
vm_page_is_valid(vm_page_t m, int base, int size)
{
int bits = vm_page_bits(base, size);
if (m->valid && ((m->valid & bits) == bits))
return 1;
else
return 0;
}
void
vm_page_test_dirty(vm_page_t m)
{
if (m->dirty != VM_PAGE_BITS_ALL && pmap_is_modified(m)) {
vm_page_dirty(m);
}
}
#include "opt_ddb.h"
#ifdef DDB
#include <ddb/ddb.h>
DB_SHOW_COMMAND(page, vm_page_print_page_info)
{
db_printf("vmstats.v_free_count: %ld\n", vmstats.v_free_count);
db_printf("vmstats.v_cache_count: %ld\n", vmstats.v_cache_count);
db_printf("vmstats.v_inactive_count: %ld\n", vmstats.v_inactive_count);
db_printf("vmstats.v_active_count: %ld\n", vmstats.v_active_count);
db_printf("vmstats.v_wire_count: %ld\n", vmstats.v_wire_count);
db_printf("vmstats.v_free_reserved: %ld\n", vmstats.v_free_reserved);
db_printf("vmstats.v_free_min: %ld\n", vmstats.v_free_min);
db_printf("vmstats.v_free_target: %ld\n", vmstats.v_free_target);
db_printf("vmstats.v_inactive_target: %ld\n",
vmstats.v_inactive_target);
db_printf("vmstats.v_paging_wait: %ld\n", vmstats.v_paging_wait);
db_printf("vmstats.v_paging_start: %ld\n", vmstats.v_paging_start);
db_printf("vmstats.v_paging_target1: %ld\n", vmstats.v_paging_target1);
db_printf("vmstats.v_paging_target2: %ld\n", vmstats.v_paging_target2);
}
DB_SHOW_COMMAND(pageq, vm_page_print_pageq_info)
{
int i;
db_printf("PQ_FREE:");
for (i = 0; i < PQ_L2_SIZE; i++) {
db_printf(" %ld", vm_page_queues[PQ_FREE + i].lcnt);
}
db_printf("\n");
db_printf("PQ_CACHE:");
for(i = 0; i < PQ_L2_SIZE; i++) {
db_printf(" %ld", vm_page_queues[PQ_CACHE + i].lcnt);
}
db_printf("\n");
db_printf("PQ_ACTIVE:");
for(i = 0; i < PQ_L2_SIZE; i++) {
db_printf(" %ld", vm_page_queues[PQ_ACTIVE + i].lcnt);
}
db_printf("\n");
db_printf("PQ_INACTIVE:");
for(i = 0; i < PQ_L2_SIZE; i++) {
db_printf(" %ld", vm_page_queues[PQ_INACTIVE + i].lcnt);
}
db_printf("\n");
}
#endif