#include <sys/types.h>
#include <sys/debug.h>
#include <sys/cmn_err.h>
#include <sys/systm.h>
#include <sys/atomic.h>
#include <sys/sysmacros.h>
#include <vm/as.h>
#include <vm/page.h>
#include <vm/seg_kmem.h>
#include <vm/seg_vn.h>
#include <sys/vmsystm.h>
#include <sys/memnode.h>
#include <vm/vm_dep.h>
#include <sys/lgrp.h>
#include <sys/mem_config.h>
#include <sys/callb.h>
#include <sys/mem_cage.h>
#include <sys/sdt.h>
#include <sys/dumphdr.h>
#include <sys/swap.h>
extern uint_t vac_colors;
#define MAX_PRAGMA_ALIGN 128
#if L2CACHE_ALIGN_MAX <= MAX_PRAGMA_ALIGN
#pragma align L2CACHE_ALIGN_MAX(vm_cpu_data0)
#else
#pragma align MAX_PRAGMA_ALIGN(vm_cpu_data0)
#endif
char vm_cpu_data0[VM_CPU_DATA_PADSIZE];
uint_t colorequiv;
uchar_t colorequivszc[MMU_PAGE_SIZES];
int ptcpthreshold;
pgcnt_t pgcpfailcnt[MMU_PAGE_SIZES];
int pgcplimitsearch = 1;
#define PGCPFAILMAX (1 << (highbit(physinstalled) - 1))
#define SETPGCPFAILCNT(szc) \
if (++pgcpfailcnt[szc] >= PGCPFAILMAX) \
pgcpfailcnt[szc] = PGCPFAILMAX / 2;
#ifdef VM_STATS
struct vmm_vmstats_str vmm_vmstats;
#endif
#if defined(__sparc)
#define LPGCREATE 0
#else
#define LPGCREATE 1
#endif
int pg_contig_disable;
int pg_lpgcreate_nocage = LPGCREATE;
#define PFNNULL 0
#define PC_FREE 0x1
#define PC_ALLOC 0x2
#define PC_NO_COLOR (-1)
#define PC_MTYPE_ANY (-1)
typedef struct pcc_info {
pgcnt_t pcc_pages_free;
pgcnt_t *pcc_color_free;
uint_t pad[12];
} pcc_info_t;
pcc_info_t **page_ctrs_cands[NPC_MUTEX][MMU_PAGE_SIZES];
#define PGCTRS_CANDS_GETVALUE(m, g, r, val) { \
int i; \
val = 0; \
for (i = 0; i < NPC_MUTEX; i++) { \
val += page_ctrs_cands[i][(r)][(m)][(g)].pcc_pages_free; \
} \
}
#define PGCTRS_CANDS_GETVALUECOLOR(m, g, r, c, val) { \
int i; \
val = 0; \
ASSERT((c) < PAGE_GET_PAGECOLORS(r)); \
for (i = 0; i < NPC_MUTEX; i++) { \
val += \
page_ctrs_cands[i][(r)][(m)][(g)].pcc_color_free[(c)]; \
} \
}
static kmutex_t *ctr_mutex[NPC_MUTEX];
#define PP_CTR_LOCK_INDX(pp) \
(((pp)->p_pagenum >> \
(PAGE_BSZS_SHIFT(mmu_page_sizes - 1))) & (NPC_MUTEX - 1))
#define INVALID_COLOR 0xffffffff
#define INVALID_MASK 0xffffffff
void page_ctr_add(int, int, page_t *, int);
void page_ctr_add_internal(int, int, page_t *, int);
void page_ctr_sub(int, int, page_t *, int);
void page_ctr_sub_internal(int, int, page_t *, int);
void page_freelist_lock(int);
void page_freelist_unlock(int);
page_t *page_promote(int, pfn_t, uchar_t, int, int);
page_t *page_demote(int, pfn_t, pfn_t, uchar_t, uchar_t, int, int);
page_t *page_freelist_split(uchar_t,
uint_t, int, int, pfn_t, pfn_t, page_list_walker_t *);
page_t *page_get_mnode_cachelist(uint_t, uint_t, int, int);
static int page_trylock_cons(page_t *pp, se_t se);
typedef struct hw_page_map {
hpmctr_t *hpm_counters;
size_t hpm_entries;
int hpm_shift;
pfn_t hpm_base;
size_t *hpm_color_current[MAX_MNODE_MRANGES];
#if defined(__sparc)
uint_t pad[4];
#endif
} hw_page_map_t;
static hw_page_map_t *page_counters[MMU_PAGE_SIZES];
static int mnode_nranges[MAX_MEM_NODES];
static int mnode_maxmrange[MAX_MEM_NODES];
#define PAGE_COUNTERS(mnode, rg_szc, idx) \
(page_counters[(rg_szc)][(mnode)].hpm_counters[(idx)])
#define PAGE_COUNTERS_COUNTERS(mnode, rg_szc) \
(page_counters[(rg_szc)][(mnode)].hpm_counters)
#define PAGE_COUNTERS_SHIFT(mnode, rg_szc) \
(page_counters[(rg_szc)][(mnode)].hpm_shift)
#define PAGE_COUNTERS_ENTRIES(mnode, rg_szc) \
(page_counters[(rg_szc)][(mnode)].hpm_entries)
#define PAGE_COUNTERS_BASE(mnode, rg_szc) \
(page_counters[(rg_szc)][(mnode)].hpm_base)
#define PAGE_COUNTERS_CURRENT_COLOR_ARRAY(mnode, rg_szc, g) \
(page_counters[(rg_szc)][(mnode)].hpm_color_current[(g)])
#define PAGE_COUNTERS_CURRENT_COLOR(mnode, rg_szc, color, mrange) \
(page_counters[(rg_szc)][(mnode)]. \
hpm_color_current[(mrange)][(color)])
#define PNUM_TO_IDX(mnode, rg_szc, pnum) \
(((pnum) - PAGE_COUNTERS_BASE((mnode), (rg_szc))) >> \
PAGE_COUNTERS_SHIFT((mnode), (rg_szc)))
#define IDX_TO_PNUM(mnode, rg_szc, index) \
(PAGE_COUNTERS_BASE((mnode), (rg_szc)) + \
((index) << PAGE_COUNTERS_SHIFT((mnode), (rg_szc))))
krwlock_t page_ctrs_rwlock[MAX_MEM_NODES];
void
cpu_vm_data_init(struct cpu *cp)
{
if (cp == CPU0) {
cp->cpu_vm_data = (void *)&vm_cpu_data0;
} else {
void *kmptr;
int align;
size_t sz;
align = (L2CACHE_ALIGN) ? L2CACHE_ALIGN : L2CACHE_ALIGN_MAX;
sz = P2ROUNDUP(sizeof (vm_cpu_data_t), align) + align;
kmptr = kmem_zalloc(sz, KM_SLEEP);
cp->cpu_vm_data = (void *) P2ROUNDUP((uintptr_t)kmptr, align);
((vm_cpu_data_t *)cp->cpu_vm_data)->vc_kmptr = kmptr;
((vm_cpu_data_t *)cp->cpu_vm_data)->vc_kmsize = sz;
}
}
void
cpu_vm_data_destroy(struct cpu *cp)
{
if (cp->cpu_seqid && cp->cpu_vm_data) {
ASSERT(cp != CPU0);
kmem_free(((vm_cpu_data_t *)cp->cpu_vm_data)->vc_kmptr,
((vm_cpu_data_t *)cp->cpu_vm_data)->vc_kmsize);
}
cp->cpu_vm_data = NULL;
}
int
page_szc(size_t pagesize)
{
int i = 0;
while (hw_page_array[i].hp_size) {
if (pagesize == hw_page_array[i].hp_size)
return (i);
i++;
}
return (-1);
}
int
page_szc_user_filtered(size_t pagesize)
{
int szc = page_szc(pagesize);
if ((szc != -1) && (SZC_2_USERSZC(szc) != -1)) {
return (szc);
}
return (-1);
}
uint_t
page_num_user_pagesizes(int legacy)
{
if (legacy)
return (mmu_legacy_page_sizes);
return (mmu_exported_page_sizes);
}
uint_t
page_num_pagesizes(void)
{
return (mmu_page_sizes);
}
pgcnt_t
page_get_pagecnt(uint_t szc)
{
if (szc >= mmu_page_sizes)
panic("page_get_pagecnt: out of range %d", szc);
return (hw_page_array[szc].hp_pgcnt);
}
size_t
page_get_pagesize(uint_t szc)
{
if (szc >= mmu_page_sizes)
panic("page_get_pagesize: out of range %d", szc);
return (hw_page_array[szc].hp_size);
}
size_t
page_get_user_pagesize(uint_t userszc)
{
uint_t szc = USERSZC_2_SZC(userszc);
if (szc >= mmu_page_sizes)
panic("page_get_user_pagesize: out of range %d", szc);
return (hw_page_array[szc].hp_size);
}
uint_t
page_get_shift(uint_t szc)
{
if (szc >= mmu_page_sizes)
panic("page_get_shift: out of range %d", szc);
return (PAGE_GET_SHIFT(szc));
}
uint_t
page_get_pagecolors(uint_t szc)
{
if (szc >= mmu_page_sizes)
panic("page_get_pagecolors: out of range %d", szc);
return (PAGE_GET_PAGECOLORS(szc));
}
uint_t
page_correct_color(uchar_t szc, uchar_t nszc, uint_t color,
uint_t ncolor, uint_t ceq_mask)
{
ASSERT(nszc > szc);
ASSERT(szc < mmu_page_sizes);
ASSERT(color < PAGE_GET_PAGECOLORS(szc));
ASSERT(ncolor < PAGE_GET_PAGECOLORS(nszc));
color &= ceq_mask;
ncolor = PAGE_CONVERT_COLOR(ncolor, szc, nszc);
return (color | (ncolor & ~ceq_mask));
}
int interleaved_mnodes = 0;
size_t
page_ctrs_sz(void)
{
int r;
int mnode;
int firstmn;
int nranges;
pfn_t physbase;
pfn_t physmax;
uint_t ctrs_sz = 0;
int i;
pgcnt_t colors_per_szc[MMU_PAGE_SIZES];
for (i = 0; i < mmu_page_sizes; i++) {
colors_per_szc[i] = PAGE_GET_PAGECOLORS(i);
}
for (firstmn = -1, mnode = 0; mnode < max_mem_nodes; mnode++) {
pgcnt_t r_pgcnt;
pfn_t r_base;
pgcnt_t r_align;
if (mem_node_config[mnode].exists == 0)
continue;
HPM_COUNTERS_LIMITS(mnode, physbase, physmax, firstmn);
nranges = MNODE_RANGE_CNT(mnode);
mnode_nranges[mnode] = nranges;
mnode_maxmrange[mnode] = MNODE_MAX_MRANGE(mnode);
for (r = 1; r < mmu_page_sizes; r++) {
ctrs_sz += sizeof (size_t) *
colors_per_szc[r] * nranges;
if (firstmn != mnode)
continue;
r_align = page_get_pagecnt(r);
r_base = physbase;
r_base &= ~(r_align - 1);
r_pgcnt = howmany(physmax - r_base + 1, r_align);
ctrs_sz += P2ROUNDUP((r_pgcnt * sizeof (hpmctr_t)),
sizeof (hpmctr_t *));
}
}
for (r = 1; r < mmu_page_sizes; r++) {
ctrs_sz += (max_mem_nodes * sizeof (hw_page_map_t));
}
ctrs_sz += sizeof (pcc_info_t *) * max_mem_nodes *
mmu_page_sizes * NPC_MUTEX;
for (mnode = 0; mnode < max_mem_nodes; mnode++) {
if (mem_node_config[mnode].exists == 0)
continue;
nranges = mnode_nranges[mnode];
ctrs_sz += sizeof (pcc_info_t) * nranges *
mmu_page_sizes * NPC_MUTEX;
for (r = 1; r < mmu_page_sizes; r++) {
ctrs_sz += sizeof (pgcnt_t) * nranges *
colors_per_szc[r] * NPC_MUTEX;
}
}
ctrs_sz += (max_mem_nodes * NPC_MUTEX * sizeof (kmutex_t));
PLCNT_SZ(ctrs_sz);
return (ctrs_sz + max_mem_nodes * L2CACHE_ALIGN);
}
caddr_t
page_ctrs_alloc(caddr_t alloc_base)
{
int mnode;
int mrange, nranges;
int r;
int i;
int firstmn;
pfn_t physbase;
pfn_t physmax;
pgcnt_t colors_per_szc[MMU_PAGE_SIZES];
for (i = 0; i < mmu_page_sizes; i++) {
colors_per_szc[i] = PAGE_GET_PAGECOLORS(i);
}
for (r = 1; r < mmu_page_sizes; r++) {
page_counters[r] = (hw_page_map_t *)alloc_base;
alloc_base += (max_mem_nodes * sizeof (hw_page_map_t));
}
for (i = 0; i < NPC_MUTEX; i++) {
for (r = 1; r < mmu_page_sizes; r++) {
page_ctrs_cands[i][r] = (pcc_info_t **)alloc_base;
alloc_base += sizeof (pcc_info_t *) * max_mem_nodes;
for (mnode = 0; mnode < max_mem_nodes; mnode++) {
pcc_info_t *pi;
if (mem_node_config[mnode].exists == 0)
continue;
nranges = mnode_nranges[mnode];
pi = (pcc_info_t *)alloc_base;
alloc_base += sizeof (pcc_info_t) * nranges;
page_ctrs_cands[i][r][mnode] = pi;
for (mrange = 0; mrange < nranges; mrange++) {
pi->pcc_color_free =
(pgcnt_t *)alloc_base;
alloc_base += sizeof (pgcnt_t) *
colors_per_szc[r];
pi++;
}
}
}
}
for (i = 0; i < NPC_MUTEX; i++) {
ctr_mutex[i] = (kmutex_t *)alloc_base;
alloc_base += (max_mem_nodes * sizeof (kmutex_t));
}
PLCNT_INIT(alloc_base);
for (firstmn = -1, mnode = 0; mnode < max_mem_nodes; mnode++) {
pgcnt_t r_pgcnt;
pfn_t r_base;
pgcnt_t r_align;
int r_shift;
int nranges = mnode_nranges[mnode];
if (mem_node_config[mnode].exists == 0)
continue;
HPM_COUNTERS_LIMITS(mnode, physbase, physmax, firstmn);
for (r = 1; r < mmu_page_sizes; r++) {
r_align = page_get_pagecnt(r);
r_base = physbase;
r_base &= ~(r_align - 1);
r_pgcnt = howmany(physmax - r_base + 1, r_align);
r_shift = PAGE_BSZS_SHIFT(r);
PAGE_COUNTERS_SHIFT(mnode, r) = r_shift;
PAGE_COUNTERS_ENTRIES(mnode, r) = r_pgcnt;
PAGE_COUNTERS_BASE(mnode, r) = r_base;
for (mrange = 0; mrange < nranges; mrange++) {
PAGE_COUNTERS_CURRENT_COLOR_ARRAY(mnode,
r, mrange) = (size_t *)alloc_base;
alloc_base += sizeof (size_t) *
colors_per_szc[r];
}
for (i = 0; i < colors_per_szc[r]; i++) {
uint_t color_mask = colors_per_szc[r] - 1;
pfn_t pfnum = r_base;
size_t idx;
int mrange;
MEM_NODE_ITERATOR_DECL(it);
MEM_NODE_ITERATOR_INIT(pfnum, mnode, r, &it);
if (pfnum == (pfn_t)-1) {
idx = 0;
} else {
PAGE_NEXT_PFN_FOR_COLOR(pfnum, r, i,
color_mask, color_mask, &it);
idx = PNUM_TO_IDX(mnode, r, pfnum);
idx = (idx >= r_pgcnt) ? 0 : idx;
}
for (mrange = 0; mrange < nranges; mrange++) {
PAGE_COUNTERS_CURRENT_COLOR(mnode,
r, i, mrange) = idx;
}
}
if (firstmn == mnode) {
PAGE_COUNTERS_COUNTERS(mnode, r) =
(hpmctr_t *)alloc_base;
alloc_base +=
P2ROUNDUP((sizeof (hpmctr_t) * r_pgcnt),
sizeof (hpmctr_t *));
} else {
PAGE_COUNTERS_COUNTERS(mnode, r) =
PAGE_COUNTERS_COUNTERS(firstmn, r);
}
ASSERT(PNUM_TO_IDX(mnode, r,
(IDX_TO_PNUM(mnode, r, 0))) == 0);
ASSERT(IDX_TO_PNUM(mnode, r,
(PNUM_TO_IDX(mnode, r, r_base))) == r_base);
}
alloc_base = (caddr_t)P2ROUNDUP((uintptr_t)alloc_base,
L2CACHE_ALIGN);
}
for (mnode = 0; mnode < MAX_MEM_NODES; mnode++) {
rw_init(&page_ctrs_rwlock[mnode], NULL, RW_DEFAULT, NULL);
}
return (alloc_base);
}
void
page_ctr_add_internal(int mnode, int mtype, page_t *pp, int flags)
{
ssize_t r;
ssize_t idx;
pfn_t pfnum;
int lckidx;
ASSERT(mnode == PP_2_MEM_NODE(pp));
ASSERT(mtype == PP_2_MTYPE(pp));
ASSERT(pp->p_szc < mmu_page_sizes);
PLCNT_INCR(pp, mnode, mtype, pp->p_szc, flags);
if (pp->p_szc >= mmu_page_sizes - 1) {
return;
}
r = pp->p_szc + 1;
pfnum = pp->p_pagenum;
lckidx = PP_CTR_LOCK_INDX(pp);
while (r < mmu_page_sizes) {
idx = PNUM_TO_IDX(mnode, r, pfnum);
ASSERT(idx < PAGE_COUNTERS_ENTRIES(mnode, r));
ASSERT(PAGE_COUNTERS(mnode, r, idx) < FULL_REGION_CNT(r));
if (++PAGE_COUNTERS(mnode, r, idx) != FULL_REGION_CNT(r)) {
break;
} else {
int root_mtype = PP_2_MTYPE(PP_GROUPLEADER(pp, r));
pcc_info_t *cand = &page_ctrs_cands[lckidx][r][mnode]
[MTYPE_2_MRANGE(mnode, root_mtype)];
cand->pcc_pages_free++;
cand->pcc_color_free[PP_2_BIN_SZC(pp, r)]++;
}
r++;
}
}
void
page_ctr_add(int mnode, int mtype, page_t *pp, int flags)
{
int lckidx = PP_CTR_LOCK_INDX(pp);
kmutex_t *lock = &ctr_mutex[lckidx][mnode];
mutex_enter(lock);
page_ctr_add_internal(mnode, mtype, pp, flags);
mutex_exit(lock);
}
void
page_ctr_sub_internal(int mnode, int mtype, page_t *pp, int flags)
{
int lckidx;
ssize_t r;
ssize_t idx;
pfn_t pfnum;
ASSERT(mnode == PP_2_MEM_NODE(pp));
ASSERT(mtype == PP_2_MTYPE(pp));
ASSERT(pp->p_szc < mmu_page_sizes);
PLCNT_DECR(pp, mnode, mtype, pp->p_szc, flags);
if (pp->p_szc >= mmu_page_sizes - 1) {
return;
}
r = pp->p_szc + 1;
pfnum = pp->p_pagenum;
lckidx = PP_CTR_LOCK_INDX(pp);
while (r < mmu_page_sizes) {
idx = PNUM_TO_IDX(mnode, r, pfnum);
ASSERT(idx < PAGE_COUNTERS_ENTRIES(mnode, r));
ASSERT(PAGE_COUNTERS(mnode, r, idx) > 0);
if (--PAGE_COUNTERS(mnode, r, idx) != FULL_REGION_CNT(r) - 1) {
break;
} else {
int root_mtype = PP_2_MTYPE(PP_GROUPLEADER(pp, r));
pcc_info_t *cand = &page_ctrs_cands[lckidx][r][mnode]
[MTYPE_2_MRANGE(mnode, root_mtype)];
ASSERT(cand->pcc_pages_free != 0);
ASSERT(cand->pcc_color_free[PP_2_BIN_SZC(pp, r)] != 0);
cand->pcc_pages_free--;
cand->pcc_color_free[PP_2_BIN_SZC(pp, r)]--;
}
r++;
}
}
void
page_ctr_sub(int mnode, int mtype, page_t *pp, int flags)
{
int lckidx = PP_CTR_LOCK_INDX(pp);
kmutex_t *lock = &ctr_mutex[lckidx][mnode];
mutex_enter(lock);
page_ctr_sub_internal(mnode, mtype, pp, flags);
mutex_exit(lock);
}
uint_t
page_ctrs_adjust(int mnode)
{
pgcnt_t npgs;
int r;
int i;
size_t pcsz, old_csz;
hpmctr_t *new_ctr, *old_ctr;
pfn_t oldbase, newbase;
pfn_t physbase, physmax;
size_t old_npgs;
hpmctr_t *ctr_cache[MMU_PAGE_SIZES];
size_t size_cache[MMU_PAGE_SIZES];
size_t *color_cache[MMU_PAGE_SIZES][MAX_MNODE_MRANGES];
size_t *old_color_array[MAX_MNODE_MRANGES];
pgcnt_t colors_per_szc[MMU_PAGE_SIZES];
pcc_info_t **cands_cache;
pcc_info_t *old_pi, *pi;
pgcnt_t *pgcntp;
int nr, old_nranges, mrange, nranges = MNODE_RANGE_CNT(mnode);
int cands_cache_nranges;
int old_maxmrange, new_maxmrange;
int rc = 0;
int oldmnode;
cands_cache = kmem_zalloc(sizeof (pcc_info_t *) * NPC_MUTEX *
MMU_PAGE_SIZES, KM_NOSLEEP);
if (cands_cache == NULL)
return (ENOMEM);
i = -1;
HPM_COUNTERS_LIMITS(mnode, physbase, physmax, i);
newbase = physbase & ~PC_BASE_ALIGN_MASK;
npgs = roundup(physmax, PC_BASE_ALIGN) - newbase;
cands_cache_nranges = nranges;
bzero(ctr_cache, sizeof (ctr_cache));
bzero(color_cache, sizeof (color_cache));
for (r = 0; r < mmu_page_sizes; r++) {
colors_per_szc[r] = PAGE_GET_PAGECOLORS(r);
}
for (r = 1; r < mmu_page_sizes; r++) {
pcsz = npgs >> PAGE_BSZS_SHIFT(r);
size_cache[r] = pcsz;
ctr_cache[r] = kmem_zalloc(pcsz *
sizeof (hpmctr_t), KM_NOSLEEP);
if (ctr_cache[r] == NULL) {
rc = ENOMEM;
goto cleanup;
}
}
for (r = 1; r < mmu_page_sizes; r++) {
for (mrange = 0; mrange < nranges; mrange++) {
color_cache[r][mrange] = kmem_zalloc(sizeof (size_t) *
colors_per_szc[r], KM_NOSLEEP);
if (color_cache[r][mrange] == NULL) {
rc = ENOMEM;
goto cleanup;
}
}
}
for (r = 1; r < mmu_page_sizes; r++) {
for (i = 0; i < NPC_MUTEX; i++) {
pi = kmem_zalloc(nranges * sizeof (pcc_info_t),
KM_NOSLEEP);
if (pi == NULL) {
rc = ENOMEM;
goto cleanup;
}
cands_cache[i * MMU_PAGE_SIZES + r] = pi;
for (mrange = 0; mrange < nranges; mrange++, pi++) {
pgcntp = kmem_zalloc(colors_per_szc[r] *
sizeof (pgcnt_t), KM_NOSLEEP);
if (pgcntp == NULL) {
rc = ENOMEM;
goto cleanup;
}
pi->pcc_color_free = pgcntp;
}
}
}
PAGE_CTRS_WRITE_LOCK(mnode);
if (interleaved_mnodes) {
for (i = 0; i < max_mem_nodes; i++)
if (PAGE_COUNTERS_COUNTERS(i, 1) != NULL)
break;
ASSERT(i < max_mem_nodes);
oldmnode = i;
} else
oldmnode = mnode;
old_nranges = mnode_nranges[mnode];
cands_cache_nranges = old_nranges;
mnode_nranges[mnode] = nranges;
old_maxmrange = mnode_maxmrange[mnode];
mnode_maxmrange[mnode] = MNODE_MAX_MRANGE(mnode);
new_maxmrange = mnode_maxmrange[mnode];
for (r = 1; r < mmu_page_sizes; r++) {
PAGE_COUNTERS_SHIFT(mnode, r) = PAGE_BSZS_SHIFT(r);
old_ctr = PAGE_COUNTERS_COUNTERS(oldmnode, r);
old_csz = PAGE_COUNTERS_ENTRIES(oldmnode, r);
oldbase = PAGE_COUNTERS_BASE(oldmnode, r);
old_npgs = old_csz << PAGE_COUNTERS_SHIFT(oldmnode, r);
for (mrange = 0; mrange < MAX_MNODE_MRANGES; mrange++) {
old_color_array[mrange] =
PAGE_COUNTERS_CURRENT_COLOR_ARRAY(mnode,
r, mrange);
}
pcsz = npgs >> PAGE_COUNTERS_SHIFT(mnode, r);
new_ctr = ctr_cache[r];
ctr_cache[r] = NULL;
if (old_ctr != NULL &&
(oldbase + old_npgs > newbase) &&
(newbase + npgs > oldbase)) {
size_t offset;
if (newbase > oldbase) {
offset = (newbase - oldbase) >>
PAGE_COUNTERS_SHIFT(mnode, r);
bcopy(old_ctr + offset, new_ctr,
MIN(pcsz, (old_csz - offset)) *
sizeof (hpmctr_t));
} else {
offset = (oldbase - newbase) >>
PAGE_COUNTERS_SHIFT(mnode, r);
bcopy(old_ctr, new_ctr + offset,
MIN(pcsz - offset, old_csz) *
sizeof (hpmctr_t));
}
}
PAGE_COUNTERS_COUNTERS(mnode, r) = new_ctr;
PAGE_COUNTERS_ENTRIES(mnode, r) = pcsz;
PAGE_COUNTERS_BASE(mnode, r) = newbase;
if (interleaved_mnodes) {
for (i = 0; i < max_mem_nodes; i++) {
if ((i == mnode) ||
(mem_node_config[i].exists == 0))
continue;
ASSERT(
PAGE_COUNTERS_COUNTERS(i, r) == old_ctr ||
PAGE_COUNTERS_COUNTERS(i, r) == NULL);
PAGE_COUNTERS_COUNTERS(i, r) = new_ctr;
PAGE_COUNTERS_ENTRIES(i, r) = pcsz;
PAGE_COUNTERS_BASE(i, r) = newbase;
}
}
for (mrange = 0; mrange < MAX_MNODE_MRANGES; mrange++) {
PAGE_COUNTERS_CURRENT_COLOR_ARRAY(mnode, r, mrange) =
color_cache[r][mrange];
color_cache[r][mrange] = NULL;
}
for (i = 0; i < colors_per_szc[r]; i++) {
uint_t color_mask = colors_per_szc[r] - 1;
int mlo = interleaved_mnodes ? 0 : mnode;
int mhi = interleaved_mnodes ? max_mem_nodes :
(mnode + 1);
int m;
pfn_t pfnum;
size_t idx;
MEM_NODE_ITERATOR_DECL(it);
for (m = mlo; m < mhi; m++) {
if (mem_node_config[m].exists == 0)
continue;
pfnum = newbase;
MEM_NODE_ITERATOR_INIT(pfnum, m, r, &it);
if (pfnum == (pfn_t)-1) {
idx = 0;
} else {
PAGE_NEXT_PFN_FOR_COLOR(pfnum, r, i,
color_mask, color_mask, &it);
idx = PNUM_TO_IDX(m, r, pfnum);
idx = (idx < pcsz) ? idx : 0;
}
for (mrange = 0; mrange < nranges; mrange++) {
if (PAGE_COUNTERS_CURRENT_COLOR_ARRAY(m,
r, mrange) != NULL)
PAGE_COUNTERS_CURRENT_COLOR(m,
r, i, mrange) = idx;
}
}
}
if ((caddr_t)old_ctr >= kernelheap &&
(caddr_t)old_ctr < ekernelheap) {
ctr_cache[r] = old_ctr;
size_cache[r] = old_csz;
}
for (mrange = 0; mrange < MAX_MNODE_MRANGES; mrange++) {
size_t *tmp = old_color_array[mrange];
if ((caddr_t)tmp >= kernelheap &&
(caddr_t)tmp < ekernelheap) {
color_cache[r][mrange] = tmp;
}
}
ASSERT(PNUM_TO_IDX(mnode, r,
(IDX_TO_PNUM(mnode, r, 0))) == 0);
ASSERT(IDX_TO_PNUM(mnode, r,
(PNUM_TO_IDX(mnode, r, newbase))) == newbase);
for (i = 0; i < NPC_MUTEX; i++) {
pcc_info_t *epi;
pcc_info_t *eold_pi;
pi = cands_cache[i * MMU_PAGE_SIZES + r];
old_pi = page_ctrs_cands[i][r][mnode];
page_ctrs_cands[i][r][mnode] = pi;
cands_cache[i * MMU_PAGE_SIZES + r] = old_pi;
if (old_pi == NULL)
continue;
epi = &pi[nranges];
eold_pi = &old_pi[old_nranges];
if (new_maxmrange > old_maxmrange) {
pi += new_maxmrange - old_maxmrange;
} else if (new_maxmrange < old_maxmrange) {
old_pi += old_maxmrange - new_maxmrange;
}
for (; pi < epi && old_pi < eold_pi; pi++, old_pi++) {
pcc_info_t tmp = *pi;
*pi = *old_pi;
*old_pi = tmp;
}
}
}
PAGE_CTRS_WRITE_UNLOCK(mnode);
cleanup:
for (r = 1; r < mmu_page_sizes; r++) {
if (ctr_cache[r] != NULL) {
kmem_free(ctr_cache[r],
size_cache[r] * sizeof (hpmctr_t));
}
for (mrange = 0; mrange < MAX_MNODE_MRANGES; mrange++) {
if (color_cache[r][mrange] != NULL) {
kmem_free(color_cache[r][mrange],
colors_per_szc[r] * sizeof (size_t));
}
}
for (i = 0; i < NPC_MUTEX; i++) {
pi = cands_cache[i * MMU_PAGE_SIZES + r];
if (pi == NULL)
continue;
nr = cands_cache_nranges;
for (mrange = 0; mrange < nr; mrange++, pi++) {
pgcntp = pi->pcc_color_free;
if (pgcntp == NULL)
continue;
if ((caddr_t)pgcntp >= kernelheap &&
(caddr_t)pgcntp < ekernelheap) {
kmem_free(pgcntp,
colors_per_szc[r] *
sizeof (pgcnt_t));
}
}
pi = cands_cache[i * MMU_PAGE_SIZES + r];
if ((caddr_t)pi >= kernelheap &&
(caddr_t)pi < ekernelheap) {
kmem_free(pi, nr * sizeof (pcc_info_t));
}
}
}
kmem_free(cands_cache,
sizeof (pcc_info_t *) * NPC_MUTEX * MMU_PAGE_SIZES);
return (rc);
}
void
page_ctrs_cleanup(void)
{
int r;
int i;
for (i = 0; i < max_mem_nodes; i++) {
PAGE_CTRS_WRITE_LOCK(i);
if (mem_node_config[i].exists) {
PAGE_CTRS_WRITE_UNLOCK(i);
continue;
}
for (r = 1; r < mmu_page_sizes; r++) {
PAGE_COUNTERS_COUNTERS(i, r) = NULL;
}
PAGE_CTRS_WRITE_UNLOCK(i);
}
}
#ifdef DEBUG
void
chk_lpg(page_t *pp, uchar_t szc)
{
spgcnt_t npgs = page_get_pagecnt(pp->p_szc);
uint_t noreloc;
if (npgs == 1) {
ASSERT(pp->p_szc == 0);
ASSERT(pp->p_next == pp);
ASSERT(pp->p_prev == pp);
return;
}
ASSERT(pp->p_vpnext == pp || pp->p_vpnext == NULL);
ASSERT(pp->p_vpprev == pp || pp->p_vpprev == NULL);
ASSERT(IS_P2ALIGNED(pp->p_pagenum, npgs));
ASSERT(pp->p_pagenum == (pp->p_next->p_pagenum - 1));
ASSERT(pp->p_prev->p_pagenum == (pp->p_pagenum + (npgs - 1)));
ASSERT(pp->p_prev == (pp + (npgs - 1)));
noreloc = PP_ISNORELOC(pp);
while (npgs--) {
if (npgs != 0) {
ASSERT(pp->p_pagenum == pp->p_next->p_pagenum - 1);
ASSERT(pp->p_next == (pp + 1));
}
ASSERT(pp->p_szc == szc);
ASSERT(PP_ISFREE(pp));
ASSERT(PP_ISAGED(pp));
ASSERT(pp->p_vpnext == pp || pp->p_vpnext == NULL);
ASSERT(pp->p_vpprev == pp || pp->p_vpprev == NULL);
ASSERT(pp->p_vnode == NULL);
ASSERT(PP_ISNORELOC(pp) == noreloc);
pp = pp->p_next;
}
}
#endif
void
page_freelist_lock(int mnode)
{
int i;
for (i = 0; i < NPC_MUTEX; i++) {
mutex_enter(FPC_MUTEX(mnode, i));
mutex_enter(CPC_MUTEX(mnode, i));
}
}
void
page_freelist_unlock(int mnode)
{
int i;
for (i = 0; i < NPC_MUTEX; i++) {
mutex_exit(FPC_MUTEX(mnode, i));
mutex_exit(CPC_MUTEX(mnode, i));
}
}
void
page_list_add(page_t *pp, int flags)
{
page_t **ppp;
kmutex_t *pcm;
uint_t bin, mtype;
int mnode;
ASSERT(PAGE_EXCL(pp) || (flags & PG_LIST_ISINIT));
ASSERT(PP_ISFREE(pp));
ASSERT(!hat_page_is_mapped(pp));
ASSERT(hat_page_getshare(pp) == 0);
ASSERT(pp->p_szc == 0);
bin = PP_2_BIN(pp);
mnode = PP_2_MEM_NODE(pp);
mtype = PP_2_MTYPE(pp);
if (flags & PG_LIST_ISINIT) {
ppp = &PAGE_FREELISTS(mnode, 0, bin, mtype);
if (*ppp != NULL) {
pp->p_next = *ppp;
pp->p_prev = (*ppp)->p_prev;
(*ppp)->p_prev = pp;
pp->p_prev->p_next = pp;
} else
*ppp = pp;
page_ctr_add_internal(mnode, mtype, pp, flags);
VM_STAT_ADD(vmm_vmstats.pladd_free[0]);
} else {
pcm = PC_BIN_MUTEX(mnode, bin, flags);
if (flags & PG_FREE_LIST) {
VM_STAT_ADD(vmm_vmstats.pladd_free[0]);
ASSERT(PP_ISAGED(pp));
ppp = &PAGE_FREELISTS(mnode, 0, bin, mtype);
} else {
VM_STAT_ADD(vmm_vmstats.pladd_cache);
ASSERT(pp->p_vnode);
ASSERT((pp->p_offset & PAGEOFFSET) == 0);
ppp = &PAGE_CACHELISTS(mnode, bin, mtype);
}
mutex_enter(pcm);
page_add(ppp, pp);
if (flags & PG_LIST_TAIL)
*ppp = (*ppp)->p_next;
page_ctr_add(mnode, mtype, pp, flags);
mutex_exit(pcm);
}
#if defined(__sparc)
if (PP_ISNORELOC(pp)) {
kcage_freemem_add(1);
}
#endif
ASSERT(PAGE_EXCL(pp) || (flags & PG_LIST_ISINIT));
}
#ifdef __sparc
void
page_list_noreloc_startup(page_t *pp)
{
page_t **ppp;
uint_t bin;
int mnode;
int mtype;
int flags = 0;
if (pp->p_szc != 0)
page_boot_demote(pp);
bin = PP_2_BIN(pp);
mnode = PP_2_MEM_NODE(pp);
mtype = PP_2_MTYPE(pp);
ASSERT(mtype == MTYPE_RELOC);
ASSERT(pp->p_szc == 0);
if (PP_ISAGED(pp)) {
ppp = &PAGE_FREELISTS(mnode, 0, bin, mtype);
flags |= PG_FREE_LIST;
} else {
ppp = &PAGE_CACHELISTS(mnode, bin, mtype);
flags |= PG_CACHE_LIST;
}
ASSERT(*ppp != NULL);
if (*ppp == pp)
*ppp = pp->p_next;
if (*ppp == pp) {
*ppp = NULL;
} else {
pp->p_prev->p_next = pp->p_next;
pp->p_next->p_prev = pp->p_prev;
}
page_ctr_sub_internal(mnode, mtype, pp, flags);
PP_SETNORELOC(pp);
mtype = PP_2_MTYPE(pp);
ASSERT(mtype == MTYPE_NORELOC);
if (PP_ISAGED(pp)) {
ppp = &PAGE_FREELISTS(mnode, 0, bin, mtype);
} else {
ppp = &PAGE_CACHELISTS(mnode, bin, mtype);
}
if (*ppp == NULL) {
*ppp = pp;
pp->p_next = pp->p_prev = pp;
} else {
pp->p_next = *ppp;
pp->p_prev = (*ppp)->p_prev;
(*ppp)->p_prev = pp;
pp->p_prev->p_next = pp;
}
page_ctr_add_internal(mnode, mtype, pp, flags);
atomic_inc_ulong(&kcage_freemem);
}
#else
void
page_list_noreloc_startup(page_t *pp)
{
panic("page_list_noreloc_startup: should be here only for sparc");
}
#endif
void
page_list_add_pages(page_t *pp, int flags)
{
kmutex_t *pcm;
pgcnt_t pgcnt;
uint_t bin, mtype, i;
int mnode;
ASSERT((flags & (PG_CACHE_LIST | PG_LIST_TAIL)) == 0);
CHK_LPG(pp, pp->p_szc);
VM_STAT_ADD(vmm_vmstats.pladd_free[pp->p_szc]);
bin = PP_2_BIN(pp);
mnode = PP_2_MEM_NODE(pp);
mtype = PP_2_MTYPE(pp);
if (flags & PG_LIST_ISINIT) {
ASSERT(pp->p_szc == mmu_page_sizes - 1);
page_vpadd(&PAGE_FREELISTS(mnode, pp->p_szc, bin, mtype), pp);
ASSERT(!PP_ISNORELOC(pp));
PLCNT_INCR(pp, mnode, mtype, pp->p_szc, flags);
} else {
ASSERT(pp->p_szc != 0 && pp->p_szc < mmu_page_sizes);
pcm = PC_BIN_MUTEX(mnode, bin, PG_FREE_LIST);
mutex_enter(pcm);
page_vpadd(&PAGE_FREELISTS(mnode, pp->p_szc, bin, mtype), pp);
page_ctr_add(mnode, mtype, pp, PG_FREE_LIST);
mutex_exit(pcm);
pgcnt = page_get_pagecnt(pp->p_szc);
#if defined(__sparc)
if (PP_ISNORELOC(pp))
kcage_freemem_add(pgcnt);
#endif
for (i = 0; i < pgcnt; i++, pp++)
page_unlock_nocapture(pp);
}
}
void
page_boot_demote(page_t *pp)
{
ASSERT(pp->p_szc != 0);
ASSERT(PP_ISFREE(pp));
ASSERT(PP_ISAGED(pp));
(void) page_demote(PP_2_MEM_NODE(pp),
PFN_BASE(pp->p_pagenum, pp->p_szc), 0, pp->p_szc, 0, PC_NO_COLOR,
PC_FREE);
ASSERT(PP_ISFREE(pp));
ASSERT(PP_ISAGED(pp));
ASSERT(pp->p_szc == 0);
}
void
page_list_sub(page_t *pp, int flags)
{
int bin;
uint_t mtype;
int mnode;
kmutex_t *pcm;
page_t **ppp;
ASSERT(PAGE_EXCL(pp));
ASSERT(PP_ISFREE(pp));
try_again:
bin = PP_2_BIN(pp);
mnode = PP_2_MEM_NODE(pp);
pcm = PC_BIN_MUTEX(mnode, bin, flags);
mutex_enter(pcm);
if (PP_2_BIN(pp) != bin) {
mutex_exit(pcm);
goto try_again;
}
mtype = PP_2_MTYPE(pp);
if (flags & PG_FREE_LIST) {
VM_STAT_ADD(vmm_vmstats.plsub_free[0]);
ASSERT(PP_ISAGED(pp));
ppp = &PAGE_FREELISTS(mnode, pp->p_szc, bin, mtype);
} else {
VM_STAT_ADD(vmm_vmstats.plsub_cache);
ASSERT(!PP_ISAGED(pp));
ppp = &PAGE_CACHELISTS(mnode, bin, mtype);
}
if (pp->p_szc == 0) {
page_sub(ppp, pp);
page_ctr_sub(mnode, mtype, pp, flags);
mutex_exit(pcm);
#if defined(__sparc)
if (PP_ISNORELOC(pp)) {
kcage_freemem_sub(1);
}
#endif
return;
}
if (flags & PG_CACHE_LIST)
panic("page_list_sub: large page on cachelist");
mutex_exit(pcm);
page_freelist_lock(mnode);
if (pp->p_szc != 0) {
(void) page_demote(mnode, PFN_BASE(pp->p_pagenum, pp->p_szc),
0, pp->p_szc, 0, PC_NO_COLOR, PC_FREE);
}
ASSERT(PP_ISFREE(pp));
ASSERT(PP_ISAGED(pp));
ASSERT(pp->p_szc == 0);
bin = PP_2_BIN(pp);
mtype = PP_2_MTYPE(pp);
ppp = &PAGE_FREELISTS(mnode, pp->p_szc, bin, mtype);
page_sub(ppp, pp);
page_ctr_sub(mnode, mtype, pp, flags);
page_freelist_unlock(mnode);
#if defined(__sparc)
if (PP_ISNORELOC(pp)) {
kcage_freemem_sub(1);
}
#endif
}
void
page_list_sub_pages(page_t *pp, uint_t szc)
{
kmutex_t *pcm;
uint_t bin, mtype;
int mnode;
ASSERT(PAGE_EXCL(pp));
ASSERT(PP_ISFREE(pp));
ASSERT(PP_ISAGED(pp));
try_again:
bin = PP_2_BIN(pp);
mnode = PP_2_MEM_NODE(pp);
pcm = PC_BIN_MUTEX(mnode, bin, PG_FREE_LIST);
mutex_enter(pcm);
if (PP_2_BIN(pp) != bin) {
mutex_exit(pcm);
goto try_again;
}
if (pp->p_szc > szc) {
mutex_exit(pcm);
pcm = NULL;
page_freelist_lock(mnode);
if (pp->p_szc > szc) {
VM_STAT_ADD(vmm_vmstats.plsubpages_szcbig);
(void) page_demote(mnode,
PFN_BASE(pp->p_pagenum, pp->p_szc), 0,
pp->p_szc, szc, PC_NO_COLOR, PC_FREE);
}
bin = PP_2_BIN(pp);
}
ASSERT(PP_ISFREE(pp));
ASSERT(PP_ISAGED(pp));
ASSERT(pp->p_szc <= szc);
ASSERT(pp == PP_PAGEROOT(pp));
VM_STAT_ADD(vmm_vmstats.plsub_free[pp->p_szc]);
mtype = PP_2_MTYPE(pp);
if (pp->p_szc != 0) {
page_vpsub(&PAGE_FREELISTS(mnode, pp->p_szc, bin, mtype), pp);
CHK_LPG(pp, pp->p_szc);
} else {
VM_STAT_ADD(vmm_vmstats.plsubpages_szc0);
page_sub(&PAGE_FREELISTS(mnode, pp->p_szc, bin, mtype), pp);
}
page_ctr_sub(mnode, mtype, pp, PG_FREE_LIST);
if (pcm != NULL) {
mutex_exit(pcm);
} else {
page_freelist_unlock(mnode);
}
#if defined(__sparc)
if (PP_ISNORELOC(pp)) {
pgcnt_t pgcnt;
pgcnt = page_get_pagecnt(pp->p_szc);
kcage_freemem_sub(pgcnt);
}
#endif
}
void
mach_page_add(page_t **ppp, page_t *pp)
{
if (*ppp == NULL) {
pp->p_next = pp->p_prev = pp;
} else {
pp->p_next = *ppp;
pp->p_prev = (*ppp)->p_prev;
(*ppp)->p_prev = pp;
pp->p_prev->p_next = pp;
}
*ppp = pp;
}
void
mach_page_sub(page_t **ppp, page_t *pp)
{
ASSERT(pp != NULL && PP_ISFREE(pp));
if (*ppp == NULL || pp == NULL)
panic("mach_page_sub");
if (*ppp == pp)
*ppp = pp->p_next;
if (*ppp == pp)
*ppp = NULL;
else {
pp->p_prev->p_next = pp->p_next;
pp->p_next->p_prev = pp->p_prev;
}
pp->p_prev = pp->p_next = pp;
}
void
page_promote_size(page_t *pp, uint_t cur_szc)
{
pfn_t pfn;
int mnode;
int idx;
int new_szc = cur_szc + 1;
int full = FULL_REGION_CNT(new_szc);
pfn = page_pptonum(pp);
mnode = PFN_2_MEM_NODE(pfn);
page_freelist_lock(mnode);
idx = PNUM_TO_IDX(mnode, new_szc, pfn);
if (PAGE_COUNTERS(mnode, new_szc, idx) == full)
(void) page_promote(mnode, pfn, new_szc, PC_FREE, PC_MTYPE_ANY);
page_freelist_unlock(mnode);
}
static uint_t page_promote_err;
static uint_t page_promote_noreloc_err;
page_t *
page_promote(int mnode, pfn_t pfnum, uchar_t new_szc, int flags, int mtype)
{
page_t *pp, *pplist, *tpp, *start_pp;
pgcnt_t new_npgs, npgs;
uint_t bin;
pgcnt_t tmpnpgs, pages_left;
uint_t noreloc;
int which_list;
ulong_t index;
kmutex_t *phm;
start_pp = page_numtopp_nolock(pfnum);
ASSERT(start_pp && (start_pp->p_pagenum == pfnum));
new_npgs = page_get_pagecnt(new_szc);
ASSERT(IS_P2ALIGNED(pfnum, new_npgs));
if (mtype != PC_MTYPE_ANY && mtype != PP_2_MTYPE(start_pp))
return (NULL);
noreloc = PP_ISNORELOC(start_pp);
for (pp = start_pp + new_npgs; --pp > start_pp; ) {
if (noreloc != PP_ISNORELOC(pp)) {
page_promote_noreloc_err++;
page_promote_err++;
return (NULL);
}
}
pages_left = new_npgs;
pplist = NULL;
pp = start_pp;
while (pages_left) {
ASSERT(PP_ISFREE(pp));
bin = PP_2_BIN(pp);
ASSERT(mnode == PP_2_MEM_NODE(pp));
mtype = PP_2_MTYPE(pp);
if (PP_ISAGED(pp)) {
if (pp->p_szc) {
page_vpsub(&PAGE_FREELISTS(mnode,
pp->p_szc, bin, mtype), pp);
} else {
mach_page_sub(&PAGE_FREELISTS(mnode, 0,
bin, mtype), pp);
}
which_list = PG_FREE_LIST;
} else {
ASSERT(pp->p_szc == 0);
if (!page_trylock(pp, SE_EXCL)) {
goto fail_promote;
}
index = PAGE_HASH_FUNC(pp->p_vnode, pp->p_offset);
phm = PAGE_HASH_MUTEX(index);
if (!mutex_tryenter(phm)) {
page_unlock_nocapture(pp);
goto fail_promote;
}
mach_page_sub(&PAGE_CACHELISTS(mnode, bin, mtype), pp);
page_hashout(pp, phm);
mutex_exit(phm);
PP_SETAGED(pp);
page_unlock_nocapture(pp);
which_list = PG_CACHE_LIST;
}
page_ctr_sub(mnode, mtype, pp, which_list);
tmpnpgs = npgs = page_get_pagecnt(pp->p_szc);
pages_left -= npgs;
tpp = pp;
while (npgs--) {
tpp->p_szc = new_szc;
tpp = tpp->p_next;
}
page_list_concat(&pplist, &pp);
pp += tmpnpgs;
}
CHK_LPG(pplist, new_szc);
if (flags == PC_ALLOC && (page_trylock_cons(pplist, SE_EXCL))) {
return (pplist);
}
bin = PP_2_BIN(pplist);
mnode = PP_2_MEM_NODE(pplist);
mtype = PP_2_MTYPE(pplist);
page_vpadd(&PAGE_FREELISTS(mnode, new_szc, bin, mtype), pplist);
page_ctr_add(mnode, mtype, pplist, PG_FREE_LIST);
return (NULL);
fail_promote:
page_promote_err++;
while (pplist) {
pp = pplist;
mach_page_sub(&pplist, pp);
pp->p_szc = 0;
bin = PP_2_BIN(pp);
mtype = PP_2_MTYPE(pp);
mach_page_add(&PAGE_FREELISTS(mnode, 0, bin, mtype), pp);
page_ctr_add(mnode, mtype, pp, PG_FREE_LIST);
}
return (NULL);
}
page_t *
page_demote(int mnode, pfn_t pfnum, pfn_t pfnmax, uchar_t cur_szc,
uchar_t new_szc, int color, int flags)
{
page_t *pp, *pplist, *npplist;
pgcnt_t npgs, n;
uint_t bin;
uint_t mtype;
page_t *ret_pp = NULL;
ASSERT(cur_szc != 0);
ASSERT(new_szc < cur_szc);
pplist = page_numtopp_nolock(pfnum);
ASSERT(pplist != NULL);
ASSERT(pplist->p_szc == cur_szc);
bin = PP_2_BIN(pplist);
ASSERT(mnode == PP_2_MEM_NODE(pplist));
mtype = PP_2_MTYPE(pplist);
page_vpsub(&PAGE_FREELISTS(mnode, cur_szc, bin, mtype), pplist);
CHK_LPG(pplist, cur_szc);
page_ctr_sub(mnode, mtype, pplist, PG_FREE_LIST);
npgs = page_get_pagecnt(new_szc);
while (pplist) {
pp = pplist;
ASSERT(pp->p_szc == cur_szc);
if (npgs == 1) {
mach_page_sub(&pplist, pp);
ASSERT(pp->p_szc == cur_szc);
ASSERT(new_szc == 0);
ASSERT(mnode == PP_2_MEM_NODE(pp));
pp->p_szc = new_szc;
bin = PP_2_BIN(pp);
if ((bin == color) && (flags == PC_ALLOC) &&
(ret_pp == NULL) && (pfnmax == 0 ||
pp->p_pagenum < pfnmax) &&
page_trylock_cons(pp, SE_EXCL)) {
ret_pp = pp;
} else {
mtype = PP_2_MTYPE(pp);
mach_page_add(&PAGE_FREELISTS(mnode, 0, bin,
mtype), pp);
page_ctr_add(mnode, mtype, pp, PG_FREE_LIST);
}
} else {
page_t *try_to_return_this_page = NULL;
int count = 0;
page_list_break(&pplist, &npplist, npgs);
pp = pplist;
n = npgs;
while (n--) {
ASSERT(pp->p_szc == cur_szc);
if (pfnmax == 0 || pp->p_pagenum < pfnmax) {
count++;
}
pp->p_szc = new_szc;
pp = pp->p_next;
}
if (count == npgs &&
(pfnmax == 0 || pp->p_pagenum < pfnmax)) {
try_to_return_this_page = pp;
}
CHK_LPG(pplist, new_szc);
bin = PP_2_BIN(pplist);
if (try_to_return_this_page)
ASSERT(mnode ==
PP_2_MEM_NODE(try_to_return_this_page));
if ((bin == color) && (flags == PC_ALLOC) &&
(ret_pp == NULL) && try_to_return_this_page &&
page_trylock_cons(try_to_return_this_page,
SE_EXCL)) {
ret_pp = try_to_return_this_page;
} else {
mtype = PP_2_MTYPE(pp);
page_vpadd(&PAGE_FREELISTS(mnode, new_szc,
bin, mtype), pplist);
page_ctr_add(mnode, mtype, pplist,
PG_FREE_LIST);
}
pplist = npplist;
}
}
return (ret_pp);
}
int mpss_coalesce_disable = 0;
page_t *
page_freelist_coalesce(int mnode, uchar_t szc, uint_t color, uint_t ceq_mask,
int mtype, pfn_t pfnhi)
{
int r = szc;
int mrange;
uint_t full, bin, color_mask, wrap = 0;
pfn_t pfnum, lo, hi;
size_t len, idx, idx0;
pgcnt_t cands = 0, szcpgcnt = page_get_pagecnt(szc);
page_t *ret_pp;
MEM_NODE_ITERATOR_DECL(it);
if (mpss_coalesce_disable) {
ASSERT(szc < MMU_PAGE_SIZES);
VM_STAT_ADD(vmm_vmstats.page_ctrs_coalesce[szc][0]);
return (NULL);
}
ASSERT(szc < mmu_page_sizes);
color_mask = PAGE_GET_PAGECOLORS(szc) - 1;
ASSERT(ceq_mask <= color_mask);
ASSERT(color <= color_mask);
color &= ceq_mask;
rw_enter(&page_ctrs_rwlock[mnode], RW_READER);
mrange = MTYPE_2_MRANGE(mnode, mtype);
ASSERT(mrange < mnode_nranges[mnode]);
VM_STAT_ADD(vmm_vmstats.page_ctrs_coalesce[r][mrange]);
len = PAGE_COUNTERS_ENTRIES(mnode, r);
MNODETYPE_2_PFN(mnode, mtype, lo, hi);
hi++;
if (pfnhi != PFNNULL && pfnhi < hi)
hi = pfnhi;
lo = P2ROUNDUP(lo, szcpgcnt);
MEM_NODE_ITERATOR_INIT(lo, mnode, szc, &it);
if (lo == (pfn_t)-1) {
rw_exit(&page_ctrs_rwlock[mnode]);
return (NULL);
}
hi = hi & ~(szcpgcnt - 1);
if (((PFN_2_COLOR(lo, szc, &it) ^ color) & ceq_mask) ||
(interleaved_mnodes && PFN_2_MEM_NODE(lo) != mnode)) {
PAGE_NEXT_PFN_FOR_COLOR(lo, szc, color, ceq_mask, color_mask,
&it);
}
if (hi <= lo) {
rw_exit(&page_ctrs_rwlock[mnode]);
return (NULL);
}
full = FULL_REGION_CNT(r);
bin = color;
idx0 = (size_t)(-1);
do {
pgcnt_t acand;
PGCTRS_CANDS_GETVALUECOLOR(mnode, mrange, r, bin, acand);
if (acand) {
idx = PAGE_COUNTERS_CURRENT_COLOR(mnode,
r, bin, mrange);
idx0 = MIN(idx0, idx);
cands += acand;
}
bin = ADD_MASKED(bin, 1, ceq_mask, color_mask);
} while (bin != color);
if (cands == 0) {
VM_STAT_ADD(vmm_vmstats.page_ctrs_cands_skip[r][mrange]);
rw_exit(&page_ctrs_rwlock[mnode]);
return (NULL);
}
pfnum = IDX_TO_PNUM(mnode, r, idx0);
if (pfnum < lo || pfnum >= hi) {
pfnum = lo;
} else {
MEM_NODE_ITERATOR_INIT(pfnum, mnode, szc, &it);
if (pfnum == (pfn_t)-1) {
pfnum = lo;
MEM_NODE_ITERATOR_INIT(pfnum, mnode, szc, &it);
ASSERT(pfnum != (pfn_t)-1);
} else if ((PFN_2_COLOR(pfnum, szc, &it) ^ color) & ceq_mask ||
(interleaved_mnodes && PFN_2_MEM_NODE(pfnum) != mnode)) {
PAGE_NEXT_PFN_FOR_COLOR(pfnum, szc, color, ceq_mask,
color_mask, &it);
if (pfnum >= hi) {
pfnum = lo;
MEM_NODE_ITERATOR_INIT(pfnum, mnode, szc, &it);
}
}
}
idx0 = PNUM_TO_IDX(mnode, r, pfnum);
ASSERT(idx0 < len);
for (idx = idx0; wrap == 0 || (idx < idx0 && wrap < 2); ) {
if (PAGE_COUNTERS(mnode, r, idx) != full)
goto next;
page_freelist_lock(mnode);
if (PAGE_COUNTERS(mnode, r, idx) == full) {
ret_pp =
page_promote(mnode, pfnum, r, PC_ALLOC, mtype);
if (ret_pp != NULL) {
VM_STAT_ADD(vmm_vmstats.pfc_coalok[r][mrange]);
PAGE_COUNTERS_CURRENT_COLOR(mnode, r,
PFN_2_COLOR(pfnum, szc, &it), mrange) = idx;
page_freelist_unlock(mnode);
rw_exit(&page_ctrs_rwlock[mnode]);
return (ret_pp);
}
} else {
VM_STAT_ADD(vmm_vmstats.page_ctrs_changed[r][mrange]);
}
page_freelist_unlock(mnode);
if (--cands == 0) {
PAGE_COUNTERS_CURRENT_COLOR(mnode, r, color, mrange) =
idx;
break;
}
next:
PAGE_NEXT_PFN_FOR_COLOR(pfnum, szc, color, ceq_mask,
color_mask, &it);
idx = PNUM_TO_IDX(mnode, r, pfnum);
if (idx >= len || pfnum >= hi) {
pfnum = lo;
MEM_NODE_ITERATOR_INIT(pfnum, mnode, szc, &it);
idx = PNUM_TO_IDX(mnode, r, pfnum);
wrap++;
}
}
rw_exit(&page_ctrs_rwlock[mnode]);
VM_STAT_ADD(vmm_vmstats.page_ctrs_failed[r][mrange]);
return (NULL);
}
void
page_freelist_coalesce_all(int mnode)
{
int r;
int idx, full;
size_t len;
int doall = interleaved_mnodes || mnode < 0;
int mlo = doall ? 0 : mnode;
int mhi = doall ? max_mem_nodes : (mnode + 1);
VM_STAT_ADD(vmm_vmstats.page_ctrs_coalesce_all);
if (mpss_coalesce_disable) {
return;
}
for (mnode = mlo; mnode < mhi; mnode++) {
rw_enter(&page_ctrs_rwlock[mnode], RW_READER);
page_freelist_lock(mnode);
}
for (r = mmu_page_sizes - 1; r > 0; r--) {
for (mnode = mlo; mnode < mhi; mnode++) {
pgcnt_t cands = 0;
int mrange, nranges = mnode_nranges[mnode];
for (mrange = 0; mrange < nranges; mrange++) {
PGCTRS_CANDS_GETVALUE(mnode, mrange, r, cands);
if (cands != 0)
break;
}
if (cands == 0) {
VM_STAT_ADD(vmm_vmstats.
page_ctrs_cands_skip_all);
continue;
}
full = FULL_REGION_CNT(r);
len = PAGE_COUNTERS_ENTRIES(mnode, r);
for (idx = 0; idx < len; idx++) {
if (PAGE_COUNTERS(mnode, r, idx) == full) {
pfn_t pfnum =
IDX_TO_PNUM(mnode, r, idx);
int tmnode = interleaved_mnodes ?
PFN_2_MEM_NODE(pfnum) : mnode;
ASSERT(pfnum >=
mem_node_config[tmnode].physbase &&
pfnum <
mem_node_config[tmnode].physmax);
(void) page_promote(tmnode,
pfnum, r, PC_FREE, PC_MTYPE_ANY);
}
}
if (interleaved_mnodes)
break;
}
}
for (mnode = mlo; mnode < mhi; mnode++) {
page_freelist_unlock(mnode);
rw_exit(&page_ctrs_rwlock[mnode]);
}
}
page_t *
page_freelist_split(uchar_t szc, uint_t color, int mnode, int mtype,
pfn_t pfnlo, pfn_t pfnhi, page_list_walker_t *plw)
{
uchar_t nszc = szc + 1;
uint_t bin, sbin, bin_prev;
page_t *pp, *firstpp;
page_t *ret_pp = NULL;
uint_t color_mask;
if (nszc == mmu_page_sizes)
return (NULL);
ASSERT(nszc < mmu_page_sizes);
color_mask = PAGE_GET_PAGECOLORS(nszc) - 1;
bin = sbin = PAGE_GET_NSZ_COLOR(szc, color);
bin_prev = (plw->plw_bin_split_prev == color) ? INVALID_COLOR :
PAGE_GET_NSZ_COLOR(szc, plw->plw_bin_split_prev);
VM_STAT_ADD(vmm_vmstats.pfs_req[szc]);
while (plw->plw_bins[nszc] != 0) {
ASSERT(nszc < mmu_page_sizes);
if (PAGE_FREELISTS(mnode, nszc, bin, mtype)) {
page_freelist_lock(mnode);
firstpp = pp = PAGE_FREELISTS(mnode, nszc, bin, mtype);
if (pp &&
((pfnhi != PFNNULL && pp->p_pagenum >= pfnhi) ||
(pfnlo != PFNNULL && pp->p_pagenum < pfnlo))) {
do {
pp = pp->p_vpnext;
if (pp == firstpp) {
pp = NULL;
break;
}
} while ((pfnhi != PFNNULL &&
pp->p_pagenum >= pfnhi) ||
(pfnlo != PFNNULL &&
pp->p_pagenum < pfnlo));
if (pfnhi != PFNNULL && pp != NULL)
ASSERT(pp->p_pagenum < pfnhi);
if (pfnlo != PFNNULL && pp != NULL)
ASSERT(pp->p_pagenum >= pfnlo);
}
if (pp) {
uint_t ccolor = page_correct_color(szc, nszc,
color, bin, plw->plw_ceq_mask[szc]);
ASSERT(pp->p_szc == nszc);
VM_STAT_ADD(vmm_vmstats.pfs_demote[nszc]);
ret_pp = page_demote(mnode, pp->p_pagenum,
pfnhi, pp->p_szc, szc, ccolor, PC_ALLOC);
if (ret_pp) {
page_freelist_unlock(mnode);
#if defined(__sparc)
if (PP_ISNORELOC(ret_pp)) {
pgcnt_t npgs;
npgs = page_get_pagecnt(
ret_pp->p_szc);
kcage_freemem_sub(npgs);
}
#endif
return (ret_pp);
}
}
page_freelist_unlock(mnode);
}
bin = ADD_MASKED(bin, 1, plw->plw_ceq_mask[nszc], color_mask);
plw->plw_bins[nszc]--;
if (bin == sbin) {
uchar_t nnszc = nszc + 1;
if (plw->plw_bins[nnszc] == 0)
break;
bin = sbin = PAGE_GET_NSZ_COLOR(nszc, bin);
if (bin_prev != INVALID_COLOR) {
bin_prev = PAGE_GET_NSZ_COLOR(nszc, bin_prev);
if (!((bin ^ bin_prev) &
plw->plw_ceq_mask[nnszc]))
break;
}
ASSERT(nnszc < mmu_page_sizes);
color_mask = PAGE_GET_PAGECOLORS(nnszc) - 1;
nszc = nnszc;
ASSERT(nszc < mmu_page_sizes);
}
}
return (ret_pp);
}
static int
page_trylock_cons(page_t *pp, se_t se)
{
page_t *tpp, *first_pp = pp;
if (!page_trylock(pp, se)) {
return (0);
}
if (pp->p_szc == 0) {
return (1);
}
tpp = pp->p_next;
while (tpp != pp) {
if (!page_trylock(tpp, se)) {
while (first_pp != tpp) {
page_unlock_nocapture(first_pp);
first_pp = first_pp->p_next;
}
return (0);
}
tpp = tpp->p_next;
}
return (1);
}
void
page_list_walk_init(uchar_t szc, uint_t flags, uint_t bin, int can_split,
int use_ceq, page_list_walker_t *plw)
{
uint_t nszc, ceq_mask, colors;
uchar_t ceq = use_ceq ? colorequivszc[szc] : 0;
ASSERT(szc < mmu_page_sizes);
colors = PAGE_GET_PAGECOLORS(szc);
plw->plw_colors = colors;
plw->plw_color_mask = colors - 1;
plw->plw_bin_marker = plw->plw_bin0 = bin;
plw->plw_bin_split_prev = bin;
plw->plw_bin_step = (szc == 0) ? vac_colors : 1;
if (vac_colors > 1)
ceq &= 0xf0;
plw->plw_ceq_dif = colors >> ((ceq >> 4) + (ceq & 0xf));
ASSERT(szc > 0 || plw->plw_ceq_dif >= vac_colors);
ASSERT(plw->plw_ceq_dif > 0);
plw->plw_ceq_mask[szc] = (plw->plw_ceq_dif - 1) << (ceq & 0xf);
if (flags & PG_MATCH_COLOR) {
if (cpu_page_colors < 0) {
uint_t cpucolors = CPUSETSIZE() >> PAGE_GET_SHIFT(szc);
cpucolors = MAX(cpucolors, 1);
ceq_mask = plw->plw_color_mask & (cpucolors - 1);
plw->plw_ceq_mask[szc] =
MIN(ceq_mask, plw->plw_ceq_mask[szc]);
}
plw->plw_ceq_dif = 1;
}
if (can_split) {
plw->plw_do_split = (szc + 1 < mmu_page_sizes) ? 1 : 0;
for (nszc = szc + 1; nszc < mmu_page_sizes; nszc++, szc++) {
plw->plw_ceq_mask[nszc] = PAGE_GET_NSZ_MASK(szc,
plw->plw_ceq_mask[szc]);
plw->plw_bins[nszc] = PAGE_GET_PAGECOLORS(nszc);
}
plw->plw_ceq_mask[nszc] = INVALID_MASK;
plw->plw_bins[nszc] = 0;
} else {
ASSERT(szc == 0);
plw->plw_do_split = 0;
plw->plw_bins[1] = 0;
plw->plw_ceq_mask[1] = INVALID_MASK;
}
}
#define PAGE_SET_NEXT_SPLIT_MARKER(szc, nszc, bin, plw) { \
uint_t bin_nsz = PAGE_GET_NSZ_COLOR(szc, bin); \
uint_t bin0_nsz = PAGE_GET_NSZ_COLOR(szc, plw->plw_bin0); \
uint_t neq_mask = ~plw->plw_ceq_mask[nszc] & plw->plw_color_mask; \
plw->plw_split_next = \
INC_MASKED(bin_nsz, neq_mask, plw->plw_color_mask); \
if (!((plw->plw_split_next ^ bin0_nsz) & plw->plw_ceq_mask[nszc])) { \
plw->plw_split_next = \
INC_MASKED(plw->plw_split_next, \
neq_mask, plw->plw_color_mask); \
} \
}
uint_t
page_list_walk_next_bin(uchar_t szc, uint_t bin, page_list_walker_t *plw)
{
uint_t neq_mask = ~plw->plw_ceq_mask[szc] & plw->plw_color_mask;
uint_t bin0_nsz, nbin_nsz, nbin0, nbin;
uchar_t nszc = szc + 1;
nbin = ADD_MASKED(bin,
plw->plw_bin_step, neq_mask, plw->plw_color_mask);
if (plw->plw_do_split) {
plw->plw_bin_split_prev = bin;
PAGE_SET_NEXT_SPLIT_MARKER(szc, nszc, bin, plw);
plw->plw_do_split = 0;
}
if (szc == 0) {
if (plw->plw_count != 0 || plw->plw_ceq_dif == vac_colors) {
if (nbin == plw->plw_bin0 &&
(vac_colors == 1 || nbin != plw->plw_bin_marker)) {
nbin = ADD_MASKED(nbin, plw->plw_bin_step,
neq_mask, plw->plw_color_mask);
plw->plw_bin_split_prev = plw->plw_bin0;
}
if (vac_colors > 1 && nbin == plw->plw_bin_marker) {
plw->plw_bin_marker =
nbin = INC_MASKED(nbin, neq_mask,
plw->plw_color_mask);
plw->plw_bin_split_prev = plw->plw_bin0;
ASSERT(plw->plw_bins[1] == 0);
plw->plw_do_split = 0;
return (nbin);
}
} else {
uint_t bin_jump = (vac_colors == 1) ?
(BIN_STEP & ~3) - (plw->plw_bin0 & 3) : BIN_STEP;
bin_jump &= ~(vac_colors - 1);
nbin0 = ADD_MASKED(plw->plw_bin0, bin_jump, neq_mask,
plw->plw_color_mask);
if ((nbin0 ^ plw->plw_bin0) & plw->plw_ceq_mask[szc]) {
plw->plw_bin_marker = nbin = nbin0;
if (plw->plw_bins[nszc] != 0) {
nbin_nsz = PAGE_GET_NSZ_COLOR(szc,
nbin);
bin0_nsz = PAGE_GET_NSZ_COLOR(szc,
plw->plw_bin0);
if ((bin0_nsz ^ nbin_nsz) &
plw->plw_ceq_mask[nszc])
plw->plw_do_split = 1;
}
return (nbin);
}
}
}
if (plw->plw_bins[nszc] != 0) {
nbin_nsz = PAGE_GET_NSZ_COLOR(szc, nbin);
if (!((plw->plw_split_next ^ nbin_nsz) &
plw->plw_ceq_mask[nszc]))
plw->plw_do_split = 1;
}
return (nbin);
}
page_t *
page_get_mnode_freelist(int mnode, uint_t bin, int mtype, uchar_t szc,
uint_t flags)
{
kmutex_t *pcm;
page_t *pp, *first_pp;
uint_t sbin;
int plw_initialized;
page_list_walker_t plw;
ASSERT(szc < mmu_page_sizes);
VM_STAT_ADD(vmm_vmstats.pgmf_alloc[szc]);
MTYPE_START(mnode, mtype, flags);
if (mtype < 0) {
VM_STAT_ADD(vmm_vmstats.pgmf_allocempty[szc]);
return (NULL);
}
try_again:
plw_initialized = 0;
plw.plw_ceq_dif = 1;
for (plw.plw_count = 0;
plw.plw_count < plw.plw_ceq_dif; plw.plw_count++) {
sbin = bin;
do {
if (!PAGE_FREELISTS(mnode, szc, bin, mtype))
goto bin_empty_1;
pcm = PC_BIN_MUTEX(mnode, bin, PG_FREE_LIST);
mutex_enter(pcm);
pp = PAGE_FREELISTS(mnode, szc, bin, mtype);
if (pp == NULL)
goto bin_empty_0;
ASSERT(PP_ISFREE(pp));
ASSERT(PP_ISAGED(pp));
ASSERT(pp->p_vnode == NULL);
ASSERT(pp->p_hash == NULL);
ASSERT(pp->p_offset == (u_offset_t)-1);
ASSERT(pp->p_szc == szc);
ASSERT(PFN_2_MEM_NODE(pp->p_pagenum) == mnode);
first_pp = pp;
while (IS_DUMP_PAGE(pp) || !page_trylock_cons(pp,
SE_EXCL)) {
if (szc == 0) {
pp = pp->p_next;
} else {
pp = pp->p_vpnext;
}
ASSERT(PP_ISFREE(pp));
ASSERT(PP_ISAGED(pp));
ASSERT(pp->p_vnode == NULL);
ASSERT(pp->p_hash == NULL);
ASSERT(pp->p_offset == (u_offset_t)-1);
ASSERT(pp->p_szc == szc);
ASSERT(PFN_2_MEM_NODE(pp->p_pagenum) == mnode);
if (pp == first_pp)
goto bin_empty_0;
}
ASSERT(pp != NULL);
ASSERT(mtype == PP_2_MTYPE(pp));
ASSERT(pp->p_szc == szc);
if (szc == 0) {
page_sub(&PAGE_FREELISTS(mnode,
szc, bin, mtype), pp);
} else {
page_vpsub(&PAGE_FREELISTS(mnode,
szc, bin, mtype), pp);
CHK_LPG(pp, szc);
}
page_ctr_sub(mnode, mtype, pp, PG_FREE_LIST);
if ((PP_ISFREE(pp) == 0) || (PP_ISAGED(pp) == 0))
panic("free page is not. pp %p", (void *)pp);
mutex_exit(pcm);
#if defined(__sparc)
ASSERT(!kcage_on || PP_ISNORELOC(pp) ||
(flags & PG_NORELOC) == 0);
if (PP_ISNORELOC(pp))
kcage_freemem_sub(page_get_pagecnt(szc));
#endif
VM_STAT_ADD(vmm_vmstats.pgmf_allocok[szc]);
return (pp);
bin_empty_0:
mutex_exit(pcm);
bin_empty_1:
if (plw_initialized == 0) {
page_list_walk_init(szc, flags, bin, 1, 1,
&plw);
plw_initialized = 1;
ASSERT(plw.plw_colors <=
PAGE_GET_PAGECOLORS(szc));
ASSERT(plw.plw_colors > 0);
ASSERT((plw.plw_colors &
(plw.plw_colors - 1)) == 0);
ASSERT(bin < plw.plw_colors);
ASSERT(plw.plw_ceq_mask[szc] < plw.plw_colors);
}
bin = ADD_MASKED(bin, plw.plw_bin_step,
plw.plw_ceq_mask[szc], plw.plw_color_mask);
} while (sbin != bin);
if (plw.plw_do_split &&
(pp = page_freelist_split(szc, bin, mnode,
mtype, PFNNULL, PFNNULL, &plw)) != NULL)
return (pp);
if (szc > 0 && (pp = page_freelist_coalesce(mnode, szc,
bin, plw.plw_ceq_mask[szc], mtype, PFNNULL)) != NULL)
return (pp);
if (plw.plw_ceq_dif > 1)
bin = page_list_walk_next_bin(szc, bin, &plw);
}
MTYPE_NEXT(mnode, mtype, flags);
if (mtype >= 0)
goto try_again;
VM_STAT_ADD(vmm_vmstats.pgmf_allocfailed[szc]);
return (NULL);
}
static int
page_freecnt(int mnode, page_t *pp, uchar_t szc)
{
pgcnt_t pgfree;
pgcnt_t cnt;
ssize_t r = szc;
ssize_t idx;
int i;
int full, range;
ASSERT((pp->p_pagenum & (PNUM_SIZE(szc) - 1)) == 0);
ASSERT(szc > 0);
rw_enter(&page_ctrs_rwlock[mnode], RW_READER);
idx = PNUM_TO_IDX(mnode, r, pp->p_pagenum);
cnt = PAGE_COUNTERS(mnode, r, idx);
pgfree = cnt << PNUM_SHIFT(r - 1);
range = FULL_REGION_CNT(szc);
if (cnt == range) {
rw_exit(&page_ctrs_rwlock[mnode]);
return (pgfree);
}
while (--r > 0) {
idx = PNUM_TO_IDX(mnode, r, pp->p_pagenum);
full = FULL_REGION_CNT(r);
for (i = 0; i < range; i++, idx++) {
cnt = PAGE_COUNTERS(mnode, r, idx);
if (cnt != full) {
pgfree += (cnt << PNUM_SHIFT(r - 1));
}
}
range *= full;
}
rw_exit(&page_ctrs_rwlock[mnode]);
return (pgfree);
}
static int
page_trylock_contig_pages(int mnode, page_t *spp, uchar_t szc, int flags)
{
pgcnt_t pgcnt = PNUM_SIZE(szc);
pgcnt_t pgfree, i;
page_t *pp;
VM_STAT_ADD(vmm_vmstats.ptcp[szc]);
if ((ptcpthreshold == 0) || (flags & PGI_PGCPHIPRI))
goto skipptcpcheck;
pgfree = page_freecnt(mnode, spp, szc);
if (pgfree < pgcnt/ptcpthreshold) {
VM_STAT_ADD(vmm_vmstats.ptcpfreethresh[szc]);
return (0);
}
skipptcpcheck:
for (i = 0; i < pgcnt; i++) {
pp = &spp[i];
if (!page_trylock(pp, SE_EXCL)) {
VM_STAT_ADD(vmm_vmstats.ptcpfailexcl[szc]);
while (--i != (pgcnt_t)-1) {
pp = &spp[i];
ASSERT(PAGE_EXCL(pp));
page_unlock_nocapture(pp);
}
return (0);
}
ASSERT(spp[i].p_pagenum == spp->p_pagenum + i);
if ((pp->p_szc > szc || (szc && pp->p_szc == szc)) &&
!PP_ISFREE(pp)) {
VM_STAT_ADD(vmm_vmstats.ptcpfailszc[szc]);
ASSERT(i == 0);
page_unlock_nocapture(pp);
return (0);
}
if (PP_ISNORELOC(pp) ||
pp->p_lckcnt != 0 || pp->p_cowcnt != 0) {
VM_STAT_ADD(vmm_vmstats.ptcpfailcage[szc]);
while (i != (pgcnt_t)-1) {
pp = &spp[i];
ASSERT(PAGE_EXCL(pp));
page_unlock_nocapture(pp);
i--;
}
return (0);
}
}
VM_STAT_ADD(vmm_vmstats.ptcpok[szc]);
return (1);
}
static page_t *
page_claim_contig_pages(page_t *pp, uchar_t szc, int flags)
{
spgcnt_t pgcnt, npgs, i;
page_t *targpp, *rpp, *hpp;
page_t *replpp = NULL;
page_t *pplist = NULL;
ASSERT(pp != NULL);
pgcnt = page_get_pagecnt(szc);
while (pgcnt) {
ASSERT(PAGE_EXCL(pp));
ASSERT(!PP_ISNORELOC(pp));
if (PP_ISFREE(pp)) {
if (PP_ISAGED(pp)) {
page_list_sub_pages(pp, szc);
if (pp->p_szc == szc) {
return (pp);
}
ASSERT(pp->p_szc < szc);
npgs = page_get_pagecnt(pp->p_szc);
hpp = pp;
for (i = 0; i < npgs; i++, pp++) {
pp->p_szc = szc;
}
page_list_concat(&pplist, &hpp);
pgcnt -= npgs;
continue;
}
ASSERT(!PP_ISAGED(pp));
ASSERT(pp->p_szc == 0);
page_list_sub(pp, PG_CACHE_LIST);
page_hashout(pp, NULL);
PP_SETAGED(pp);
pp->p_szc = szc;
page_list_concat(&pplist, &pp);
pp++;
pgcnt--;
continue;
}
npgs = page_get_pagecnt(pp->p_szc);
if (pp->p_szc < szc || (szc == 0 && (flags & PGI_PGCPSZC0))) {
replpp = page_get_replacement_page(pp, NULL, 0);
if (replpp) {
npgs = page_get_pagecnt(pp->p_szc);
ASSERT(npgs <= pgcnt);
targpp = pp;
}
}
if (replpp == NULL || (do_page_relocate(&targpp, &replpp, 0,
&npgs, NULL) != 0)) {
while (pgcnt--) {
ASSERT(PAGE_EXCL(pp));
page_unlock_nocapture(pp);
pp++;
}
while (pplist) {
pp = pplist;
page_sub(&pplist, pp);
ASSERT(PAGE_EXCL(pp));
ASSERT(pp->p_szc == szc);
ASSERT(PP_ISFREE(pp));
ASSERT(PP_ISAGED(pp));
pp->p_szc = 0;
page_list_add(pp, PG_FREE_LIST | PG_LIST_TAIL);
page_unlock_nocapture(pp);
}
if (replpp != NULL)
page_free_replacement_page(replpp);
return (NULL);
}
ASSERT(pp == targpp);
ASSERT(hpp = pp);
pp += npgs;
pgcnt -= npgs;
while (npgs--) {
ASSERT(PAGE_EXCL(targpp));
ASSERT(!PP_ISFREE(targpp));
ASSERT(!PP_ISNORELOC(targpp));
PP_SETFREE(targpp);
ASSERT(PP_ISAGED(targpp));
ASSERT(targpp->p_szc < szc || (szc == 0 &&
(flags & PGI_PGCPSZC0)));
targpp->p_szc = szc;
targpp = targpp->p_next;
rpp = replpp;
ASSERT(rpp != NULL);
page_sub(&replpp, rpp);
ASSERT(PAGE_EXCL(rpp));
ASSERT(!PP_ISFREE(rpp));
page_unlock_nocapture(rpp);
}
ASSERT(targpp == hpp);
ASSERT(replpp == NULL);
page_list_concat(&pplist, &targpp);
}
CHK_LPG(pplist, szc);
return (pplist);
}
int
trimkcage(struct memseg *mseg, pfn_t *lo, pfn_t *hi, pfn_t pfnlo, pfn_t pfnhi)
{
pfn_t kcagepfn;
int decr;
int rc = 0;
if (PP_ISNORELOC(mseg->pages)) {
if (PP_ISNORELOC(mseg->epages - 1) == 0) {
decr = kcage_current_pfn(&kcagepfn);
if (kcagepfn >= mseg->pages_base &&
kcagepfn < mseg->pages_end) {
ASSERT(decr == 0);
*lo = MAX(kcagepfn, pfnlo);
*hi = MIN(pfnhi, (mseg->pages_end - 1));
rc = 1;
}
}
} else {
if (PP_ISNORELOC(mseg->epages - 1)) {
decr = kcage_current_pfn(&kcagepfn);
if (kcagepfn >= mseg->pages_base &&
kcagepfn < mseg->pages_end) {
ASSERT(decr);
*hi = MIN(kcagepfn, pfnhi);
*lo = MAX(pfnlo, mseg->pages_base);
rc = 1;
}
} else {
*lo = MAX(pfnlo, mseg->pages_base);
*hi = MIN(pfnhi, (mseg->pages_end - 1));
rc = 1;
}
}
return (rc);
}
static page_t *
page_geti_contig_pages(int mnode, uint_t bin, uchar_t szc, int flags,
pfn_t pfnlo, pfn_t pfnhi, pgcnt_t pfnflag)
{
struct memseg *mseg;
pgcnt_t szcpgcnt = page_get_pagecnt(szc);
pgcnt_t szcpgmask = szcpgcnt - 1;
pfn_t randpfn;
page_t *pp, *randpp, *endpp;
uint_t colors, ceq_mask;
uint_t color_mask __unused;
pfn_t hi, lo;
uint_t skip;
MEM_NODE_ITERATOR_DECL(it);
ASSERT(szc != 0 || (flags & PGI_PGCPSZC0));
pfnlo = P2ROUNDUP(pfnlo, szcpgcnt);
if ((pfnhi - pfnlo) + 1 < szcpgcnt || pfnlo >= pfnhi)
return (NULL);
ASSERT(szc < mmu_page_sizes);
colors = PAGE_GET_PAGECOLORS(szc);
color_mask = colors - 1;
if ((colors > 1) && (flags & PG_MATCH_COLOR)) {
uchar_t ceq = colorequivszc[szc];
uint_t ceq_dif = colors >> ((ceq >> 4) + (ceq & 0xf));
ASSERT(ceq_dif > 0);
ceq_mask = (ceq_dif - 1) << (ceq & 0xf);
} else {
ceq_mask = 0;
}
ASSERT(bin < colors);
bin &= ceq_mask;
if (pfnflag > 1) {
int slots = 1 << (highbit(pfnflag) - 1);
int slotid = pfnflag & (slots - 1);
pgcnt_t szcpages;
int slotlen;
pfnhi = P2ALIGN((pfnhi + 1), szcpgcnt) - 1;
szcpages = ((pfnhi - pfnlo) + 1) / szcpgcnt;
slotlen = howmany(szcpages, slots);
if (slotid * slotlen >= szcpages)
return (NULL);
pfnlo = pfnlo + (((slotid * slotlen) % szcpages) * szcpgcnt);
ASSERT(pfnlo < pfnhi);
if (pfnhi > pfnlo + (slotlen * szcpgcnt))
pfnhi = pfnlo + (slotlen * szcpgcnt) - 1;
}
if (!memsegs_trylock(0))
return (NULL);
for (mseg = memsegs; mseg != NULL; mseg = mseg->next) {
if (pfnhi < mseg->pages_base || pfnlo >= mseg->pages_end) {
continue;
}
if (mseg->pages_end - mseg->pages_base < szcpgcnt)
continue;
if (kcage_on) {
if (trimkcage(mseg, &lo, &hi, pfnlo, pfnhi) == 0 ||
lo >= hi || ((hi - lo) + 1) < szcpgcnt)
continue;
} else {
lo = MAX(pfnlo, mseg->pages_base);
hi = MIN(pfnhi, (mseg->pages_end - 1));
}
lo = P2ROUNDUP(lo, szcpgcnt);
MEM_NODE_ITERATOR_INIT(lo, mnode, szc, &it);
hi = P2ALIGN((hi + 1), szcpgcnt) - 1;
if (hi <= lo)
continue;
skip = szcpgcnt;
if (ceq_mask > 0 || interleaved_mnodes) {
if (((PFN_2_COLOR(lo, szc, &it) ^ bin) & ceq_mask) ||
(interleaved_mnodes &&
PFN_2_MEM_NODE(lo) != mnode)) {
PAGE_NEXT_PFN_FOR_COLOR(lo, szc, bin, ceq_mask,
color_mask, &it);
}
if (hi <= lo)
continue;
}
randpfn = (pfn_t)GETTICK();
randpfn = ((randpfn % (hi - lo)) + lo) & ~(skip - 1);
MEM_NODE_ITERATOR_INIT(randpfn, mnode, szc, &it);
if (ceq_mask || interleaved_mnodes || randpfn == (pfn_t)-1) {
if (randpfn != (pfn_t)-1) {
PAGE_NEXT_PFN_FOR_COLOR(randpfn, szc, bin,
ceq_mask, color_mask, &it);
}
if (randpfn >= hi) {
randpfn = lo;
MEM_NODE_ITERATOR_INIT(randpfn, mnode, szc,
&it);
}
}
randpp = mseg->pages + (randpfn - mseg->pages_base);
ASSERT(randpp->p_pagenum == randpfn);
pp = randpp;
endpp = mseg->pages + (hi - mseg->pages_base) + 1;
ASSERT(randpp + szcpgcnt <= endpp);
do {
ASSERT(!(pp->p_pagenum & szcpgmask));
ASSERT(((PP_2_BIN(pp) ^ bin) & ceq_mask) == 0);
if (page_trylock_contig_pages(mnode, pp, szc, flags)) {
if (page_claim_contig_pages(pp, szc, flags)) {
memsegs_unlock(0);
return (pp);
}
}
if (ceq_mask == 0 && !interleaved_mnodes) {
pp += skip;
} else {
pfn_t pfn = pp->p_pagenum;
PAGE_NEXT_PFN_FOR_COLOR(pfn, szc, bin,
ceq_mask, color_mask, &it);
if (pfn == (pfn_t)-1) {
pp = endpp;
} else {
pp = mseg->pages +
(pfn - mseg->pages_base);
}
}
if (pp >= endpp) {
MEM_NODE_ITERATOR_INIT(lo, mnode, szc, &it);
pp = mseg->pages + (lo - mseg->pages_base);
ASSERT(pp->p_pagenum == lo);
ASSERT(pp + szcpgcnt <= endpp);
}
} while (pp != randpp);
}
memsegs_unlock(0);
return (NULL);
}
page_t *
page_get_contig_pages(int mnode, uint_t bin, int mtype, uchar_t szc,
uint_t flags)
{
pfn_t pfnlo, pfnhi;
page_t *pp;
pgcnt_t pfnflag = 0;
VM_STAT_ADD(vmm_vmstats.pgcp_alloc[szc]);
flags |= PGI_NOCAGE;
MTYPE_START(mnode, mtype, flags);
if (mtype < 0) {
VM_STAT_ADD(vmm_vmstats.pgcp_allocempty[szc]);
return (NULL);
}
ASSERT(szc > 0 || (flags & PGI_PGCPSZC0));
if (pgcplimitsearch && ((flags & PGI_PGCPHIPRI) == 0))
pfnflag = pgcpfailcnt[szc];
if (flags & PGI_PGCPHIPRI || pfnflag)
flags &= ~PG_MATCH_COLOR;
do {
MNODETYPE_2_PFN(mnode, mtype, pfnlo, pfnhi);
ASSERT(pfnhi >= pfnlo);
pp = page_geti_contig_pages(mnode, bin, szc, flags,
pfnlo, pfnhi, pfnflag);
if (pp != NULL) {
pfnflag = pgcpfailcnt[szc];
if (pfnflag) {
pgcpfailcnt[szc] = pfnflag >> 1;
}
VM_STAT_ADD(vmm_vmstats.pgcp_allocok[szc]);
return (pp);
}
MTYPE_NEXT(mnode, mtype, flags);
} while (mtype >= 0);
VM_STAT_ADD(vmm_vmstats.pgcp_allocfailed[szc]);
return (NULL);
}
#if defined(__x86)
int
page_chk_freelist(uint_t szc)
{
pgcnt_t pgcnt;
if (szc <= 1)
return (1);
pgcnt = page_get_pagecnt(szc);
if (pgcpfailcnt[szc] && pgcnt + throttlefree >= freemem) {
VM_STAT_ADD(vmm_vmstats.pcf_deny[szc]);
return (0);
}
VM_STAT_ADD(vmm_vmstats.pcf_allow[szc]);
return (1);
}
#endif
page_t *
page_get_freelist(struct vnode *vp, u_offset_t off, struct seg *seg,
caddr_t vaddr, size_t size, uint_t flags, struct lgrp *lgrp)
{
struct as *as = seg->s_as;
page_t *pp = NULL;
ulong_t bin;
uchar_t szc;
int mnode;
int mtype;
page_t *(*page_get_func)(int, uint_t, int, uchar_t, uint_t);
lgrp_mnode_cookie_t lgrp_cookie;
page_get_func = page_get_mnode_freelist;
if (!LGRP_EXISTS(lgrp))
lgrp = lgrp_home_lgrp();
if (kcage_on) {
if ((flags & (PG_NORELOC | PG_PANIC)) == PG_NORELOC &&
kcage_freemem < kcage_throttlefree + btop(size) &&
curthread != kcage_cageout_thread) {
return (NULL);
}
} else {
flags &= ~PG_NORELOC;
flags |= PGI_NOCAGE;
}
MTYPE_INIT(mtype, vp, vaddr, flags, size);
if ((szc = page_szc(size)) == (uchar_t)-1)
panic("page_get_freelist: illegal page size request");
ASSERT(szc < mmu_page_sizes);
VM_STAT_ADD(vmm_vmstats.pgf_alloc[szc]);
AS_2_BIN(as, seg, vp, vaddr, bin, szc);
ASSERT(bin < PAGE_GET_PAGECOLORS(szc));
pgretry:
LGRP_MNODE_COOKIE_INIT(lgrp_cookie, lgrp, LGRP_SRCH_LOCAL);
while ((mnode = lgrp_memnode_choose(&lgrp_cookie)) >= 0) {
pp = page_get_func(mnode, bin, mtype, szc, flags);
if (pp != NULL) {
VM_STAT_ADD(vmm_vmstats.pgf_allocok[szc]);
DTRACE_PROBE4(page__get,
lgrp_t *, lgrp,
int, mnode,
ulong_t, bin,
uint_t, flags);
return (pp);
}
}
ASSERT(pp == NULL);
if (szc == 0 && ((flags & PGI_PGCPSZC0) == 0)) {
VM_STAT_ADD(vmm_vmstats.pgf_allocdeferred);
return (NULL);
}
ASSERT(szc > 0 || (flags & PGI_PGCPSZC0));
lgrp_stat_add(lgrp->lgrp_id, LGRP_NUM_ALLOC_FAIL, 1);
if (!(flags & PG_LOCAL)) {
LGRP_MNODE_COOKIE_UPGRADE(lgrp_cookie);
while ((mnode = lgrp_memnode_choose(&lgrp_cookie)) >= 0) {
pp = page_get_func(mnode, bin, mtype, szc, flags);
if (pp != NULL) {
DTRACE_PROBE4(page__get,
lgrp_t *, lgrp,
int, mnode,
ulong_t, bin,
uint_t, flags);
VM_STAT_ADD(vmm_vmstats.pgf_allocokrem[szc]);
return (pp);
}
}
ASSERT(pp == NULL);
}
if (!(flags & PG_NORELOC) && (pg_contig_disable == 0) &&
(kcage_on || pg_lpgcreate_nocage || szc == 0) &&
(page_get_func != page_get_contig_pages)) {
VM_STAT_ADD(vmm_vmstats.pgf_allocretry[szc]);
page_get_func = page_get_contig_pages;
goto pgretry;
}
if (!(flags & PG_LOCAL) && pgcplimitsearch &&
page_get_func == page_get_contig_pages)
SETPGCPFAILCNT(szc);
VM_STAT_ADD(vmm_vmstats.pgf_allocfailed[szc]);
return (NULL);
}
page_t *
page_get_cachelist(struct vnode *vp, u_offset_t off, struct seg *seg,
caddr_t vaddr, uint_t flags, struct lgrp *lgrp)
{
page_t *pp;
struct as *as = seg->s_as;
ulong_t bin;
int mnode;
int mtype;
lgrp_mnode_cookie_t lgrp_cookie;
if (!LGRP_EXISTS(lgrp))
lgrp = lgrp_home_lgrp();
if (!kcage_on) {
flags &= ~PG_NORELOC;
flags |= PGI_NOCAGE;
}
if ((flags & (PG_NORELOC | PG_PANIC | PG_PUSHPAGE)) == PG_NORELOC &&
kcage_freemem <= kcage_throttlefree) {
return (NULL);
}
AS_2_BIN(as, seg, vp, vaddr, bin, 0);
ASSERT(bin < PAGE_GET_PAGECOLORS(0));
MTYPE_INIT(mtype, vp, vaddr, flags, MMU_PAGESIZE);
VM_STAT_ADD(vmm_vmstats.pgc_alloc);
LGRP_MNODE_COOKIE_INIT(lgrp_cookie, lgrp, LGRP_SRCH_LOCAL);
while ((mnode = lgrp_memnode_choose(&lgrp_cookie)) >= 0) {
pp = page_get_mnode_cachelist(bin, flags, mnode, mtype);
if (pp != NULL) {
VM_STAT_ADD(vmm_vmstats.pgc_allocok);
DTRACE_PROBE4(page__get,
lgrp_t *, lgrp,
int, mnode,
ulong_t, bin,
uint_t, flags);
return (pp);
}
}
lgrp_stat_add(lgrp->lgrp_id, LGRP_NUM_ALLOC_FAIL, 1);
LGRP_MNODE_COOKIE_UPGRADE(lgrp_cookie);
while ((mnode = lgrp_memnode_choose(&lgrp_cookie)) >= 0) {
pp = page_get_mnode_freelist(mnode, bin, mtype,
0, flags);
if (pp != NULL) {
VM_STAT_ADD(vmm_vmstats.pgc_allocokdeferred);
DTRACE_PROBE4(page__get,
lgrp_t *, lgrp,
int, mnode,
ulong_t, bin,
uint_t, flags);
return (pp);
}
pp = page_get_mnode_cachelist(bin, flags, mnode, mtype);
if (pp != NULL) {
VM_STAT_ADD(vmm_vmstats.pgc_allocokrem);
DTRACE_PROBE4(page__get,
lgrp_t *, lgrp,
int, mnode,
ulong_t, bin,
uint_t, flags);
return (pp);
}
}
VM_STAT_ADD(vmm_vmstats.pgc_allocfailed);
return (NULL);
}
page_t *
page_get_mnode_cachelist(uint_t bin, uint_t flags, int mnode, int mtype)
{
kmutex_t *pcm;
page_t *pp, *first_pp;
uint_t sbin;
int plw_initialized;
page_list_walker_t plw;
VM_STAT_ADD(vmm_vmstats.pgmc_alloc);
MTYPE_START(mnode, mtype, flags);
if (mtype < 0) {
VM_STAT_ADD(vmm_vmstats.pgmc_allocempty);
return (NULL);
}
try_again:
plw_initialized = 0;
plw.plw_ceq_dif = 1;
for (plw.plw_count = 0;
plw.plw_count < plw.plw_ceq_dif; plw.plw_count++) {
sbin = bin;
do {
if (!PAGE_CACHELISTS(mnode, bin, mtype))
goto bin_empty_1;
pcm = PC_BIN_MUTEX(mnode, bin, PG_CACHE_LIST);
mutex_enter(pcm);
pp = PAGE_CACHELISTS(mnode, bin, mtype);
if (pp == NULL)
goto bin_empty_0;
first_pp = pp;
ASSERT(pp->p_vnode);
ASSERT(PP_ISAGED(pp) == 0);
ASSERT(pp->p_szc == 0);
ASSERT(PFN_2_MEM_NODE(pp->p_pagenum) == mnode);
while (IS_DUMP_PAGE(pp) || !page_trylock(pp, SE_EXCL)) {
pp = pp->p_next;
ASSERT(pp->p_szc == 0);
if (pp == first_pp) {
pp = NULL;
break;
}
ASSERT(pp->p_vnode);
ASSERT(PP_ISFREE(pp));
ASSERT(PP_ISAGED(pp) == 0);
ASSERT(PFN_2_MEM_NODE(pp->p_pagenum) ==
mnode);
}
if (pp) {
page_t **ppp;
ASSERT(mtype == PP_2_MTYPE(pp));
ppp = &PAGE_CACHELISTS(mnode, bin, mtype);
page_sub(ppp, pp);
page_ctr_sub(mnode, mtype, pp, PG_CACHE_LIST);
mutex_exit(pcm);
ASSERT(pp->p_vnode);
ASSERT(PP_ISAGED(pp) == 0);
#if defined(__sparc)
ASSERT(!kcage_on ||
(flags & PG_NORELOC) == 0 ||
PP_ISNORELOC(pp));
if (PP_ISNORELOC(pp)) {
kcage_freemem_sub(1);
}
#endif
VM_STAT_ADD(vmm_vmstats. pgmc_allocok);
return (pp);
}
bin_empty_0:
mutex_exit(pcm);
bin_empty_1:
if (plw_initialized == 0) {
page_list_walk_init(0, flags, bin, 0, 1, &plw);
plw_initialized = 1;
}
bin = ADD_MASKED(bin, plw.plw_bin_step,
plw.plw_ceq_mask[0], plw.plw_color_mask);
} while (sbin != bin);
if (plw.plw_ceq_dif > 1)
bin = page_list_walk_next_bin(0, bin, &plw);
}
MTYPE_NEXT(mnode, mtype, flags);
if (mtype >= 0)
goto try_again;
VM_STAT_ADD(vmm_vmstats.pgmc_allocfailed);
return (NULL);
}
#ifdef DEBUG
#define REPL_PAGE_STATS
#endif
#ifdef REPL_PAGE_STATS
struct repl_page_stats {
uint_t ngets;
uint_t ngets_noreloc;
uint_t npgr_noreloc;
uint_t nnopage_first;
uint_t nnopage;
uint_t nhashout;
uint_t nnofree;
uint_t nnext_pp;
} repl_page_stats;
#define REPL_STAT_INCR(v) atomic_inc_32(&repl_page_stats.v)
#else
#define REPL_STAT_INCR(v)
#endif
int pgrppgcp;
page_t *
page_get_replacement_page(page_t *orig_like_pp, struct lgrp *lgrp_target,
uint_t pgrflags)
{
page_t *like_pp;
page_t *pp, *pplist;
page_t *pl = NULL;
ulong_t bin;
int mnode, page_mnode;
int szc;
spgcnt_t npgs, pg_cnt;
pfn_t pfnum;
int mtype;
int flags = 0;
lgrp_mnode_cookie_t lgrp_cookie;
lgrp_t *lgrp;
mnode = 0;
lgrp = NULL;
REPL_STAT_INCR(ngets);
like_pp = orig_like_pp;
ASSERT(PAGE_EXCL(like_pp));
szc = like_pp->p_szc;
npgs = page_get_pagecnt(szc);
pfnum = PFN_BASE(like_pp->p_pagenum, szc);
like_pp = page_numtopp_nolock(pfnum);
ASSERT(like_pp->p_szc == szc);
if (PP_ISNORELOC(like_pp)) {
ASSERT(kcage_on);
REPL_STAT_INCR(ngets_noreloc);
flags = PGI_RELOCONLY;
} else if (pgrflags & PGR_NORELOC) {
ASSERT(kcage_on);
REPL_STAT_INCR(npgr_noreloc);
flags = PG_NORELOC;
}
if (PP_ISKAS(like_pp))
pgrflags |= PGR_SAMESZC;
MTYPE_PGR_INIT(mtype, flags, like_pp, npgs);
while (npgs) {
pplist = NULL;
for (;;) {
pg_cnt = page_get_pagecnt(szc);
bin = PP_2_BIN(like_pp);
ASSERT(like_pp->p_szc == orig_like_pp->p_szc);
ASSERT(pg_cnt <= npgs);
if (LGRP_EXISTS(lgrp_target)) {
lgrp = lgrp_target;
LGRP_MNODE_COOKIE_INIT(lgrp_cookie, lgrp,
LGRP_SRCH_LOCAL);
while ((pplist == NULL) &&
(mnode = lgrp_memnode_choose(&lgrp_cookie))
!= -1) {
pplist =
page_get_mnode_freelist(mnode, bin,
mtype, szc, flags);
}
if (pplist != NULL || szc != 0)
break;
LGRP_MNODE_COOKIE_INIT(lgrp_cookie, lgrp,
LGRP_SRCH_LOCAL);
while ((pplist == NULL) &&
(mnode = lgrp_memnode_choose(&lgrp_cookie))
!= -1) {
pplist =
page_get_mnode_cachelist(bin, flags,
mnode, mtype);
}
if (pplist != NULL) {
page_hashout(pplist, NULL);
PP_SETAGED(pplist);
REPL_STAT_INCR(nhashout);
break;
}
break;
}
mnode = PP_2_MEM_NODE(like_pp);
pplist = page_get_mnode_freelist(mnode, bin,
mtype, szc, flags);
if (pplist != NULL)
break;
REPL_STAT_INCR(nnofree);
if (szc == 0) {
pplist = page_get_mnode_cachelist(bin, flags,
mnode, mtype);
if (pplist != NULL) {
page_hashout(pplist, NULL);
PP_SETAGED(pplist);
REPL_STAT_INCR(nhashout);
break;
}
}
page_mnode = mnode;
lgrp =
lgrp_hand_to_lgrp(MEM_NODE_2_LGRPHAND(page_mnode));
LGRP_MNODE_COOKIE_INIT(lgrp_cookie, lgrp,
LGRP_SRCH_HIER);
while (pplist == NULL &&
(mnode = lgrp_memnode_choose(&lgrp_cookie))
!= -1) {
if ((mnode == page_mnode) ||
(mem_node_config[mnode].exists == 0))
continue;
pplist = page_get_mnode_freelist(mnode,
bin, mtype, szc, flags);
}
if (pplist != NULL)
break;
LGRP_MNODE_COOKIE_INIT(lgrp_cookie, lgrp,
LGRP_SRCH_HIER);
while (pplist == NULL && szc == 0) {
mnode = lgrp_memnode_choose(&lgrp_cookie);
if (mnode == -1)
break;
if ((mnode == page_mnode) ||
(mem_node_config[mnode].exists == 0))
continue;
pplist = page_get_mnode_cachelist(bin,
flags, mnode, mtype);
if (pplist != NULL) {
page_hashout(pplist, NULL);
PP_SETAGED(pplist);
REPL_STAT_INCR(nhashout);
break;
}
}
if (pplist != NULL || szc == 0)
break;
if ((pgrflags & PGR_SAMESZC) || pgrppgcp) {
LGRP_MNODE_COOKIE_INIT(lgrp_cookie, lgrp,
LGRP_SRCH_HIER);
while ((pplist == NULL) &&
(mnode =
lgrp_memnode_choose(&lgrp_cookie))
!= -1) {
pplist = page_get_contig_pages(
mnode, bin, mtype, szc,
flags | PGI_PGCPHIPRI);
}
break;
}
szc = 0;
}
if (pplist != NULL) {
DTRACE_PROBE4(page__get,
lgrp_t *, lgrp,
int, mnode,
ulong_t, bin,
uint_t, flags);
while (pplist != NULL && pg_cnt--) {
ASSERT(pplist != NULL);
pp = pplist;
page_sub(&pplist, pp);
PP_CLRFREE(pp);
PP_CLRAGED(pp);
page_list_concat(&pl, &pp);
npgs--;
like_pp = like_pp + 1;
REPL_STAT_INCR(nnext_pp);
}
ASSERT(pg_cnt == 0);
} else {
break;
}
}
if (npgs) {
REPL_STAT_INCR(nnopage);
page_free_replacement_page(pl);
return (NULL);
} else {
return (pl);
}
}
void
page_demote_free_pages(page_t *pp)
{
int mnode;
ASSERT(pp != NULL);
ASSERT(PAGE_LOCKED(pp));
ASSERT(PP_ISFREE(pp));
ASSERT(pp->p_szc != 0 && pp->p_szc < mmu_page_sizes);
mnode = PP_2_MEM_NODE(pp);
page_freelist_lock(mnode);
if (pp->p_szc != 0) {
(void) page_demote(mnode, PFN_BASE(pp->p_pagenum,
pp->p_szc), 0, pp->p_szc, 0, PC_NO_COLOR, PC_FREE);
}
page_freelist_unlock(mnode);
ASSERT(pp->p_szc == 0);
}
void
page_set_colorequiv_arr(void)
{
if (colorequiv > 1) {
int i;
uint_t sv_a = lowbit(colorequiv) - 1;
if (sv_a > 15)
sv_a = 15;
for (i = 0; i < MMU_PAGE_SIZES; i++) {
uint_t colors;
uint_t a = sv_a;
if ((colors = hw_page_array[i].hp_colors) <= 1) {
continue;
}
while ((colors >> a) == 0)
a--;
if ((a << 4) > colorequivszc[i]) {
colorequivszc[i] = (a << 4);
}
}
}
}