#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/vnode.h>
#include <sys/buf.h>
#include <sys/ucred.h>
#include <sys/dsched.h>
#include <sys/proc.h>
#include <sys/sysctl.h>
#include <vm/vm.h>
#include <vm/vm_param.h>
#include <vm/vm_kern.h>
#include <vm/vm_object.h>
#include <vm/vm_page.h>
#include <vm/vm_pager.h>
#include <vm/vm_extern.h>
#include <sys/buf2.h>
#include <vm/vm_page2.h>
static pgo_dealloc_t dead_pager_dealloc;
static pgo_getpage_t dead_pager_getpage;
static pgo_putpages_t dead_pager_putpages;
static pgo_haspage_t dead_pager_haspage;
static struct pagerops deadpagerops = {
.pgo_dealloc = dead_pager_dealloc,
.pgo_getpage = dead_pager_getpage,
.pgo_putpages = dead_pager_putpages,
.pgo_haspage = dead_pager_haspage
};
extern struct pagerops defaultpagerops;
extern struct pagerops swappagerops;
extern struct pagerops vnodepagerops;
extern struct pagerops devicepagerops;
extern struct pagerops physpagerops;
static int
dead_pager_getpage(vm_object_t obj, vm_pindex_t pindex,
vm_page_t *mpp, int seqaccess)
{
return VM_PAGER_FAIL;
}
static void
dead_pager_putpages(vm_object_t object, vm_page_t *m, int count, int flags,
int *rtvals)
{
int i;
for (i = 0; i < count; i++) {
rtvals[i] = VM_PAGER_AGAIN;
}
}
static boolean_t
dead_pager_haspage(vm_object_t object, vm_pindex_t pindex)
{
return FALSE;
}
static void
dead_pager_dealloc(vm_object_t object)
{
KKASSERT(object->swblock_count == 0);
return;
}
struct pagerops *pagertab[] = {
&defaultpagerops,
&swappagerops,
&vnodepagerops,
&devicepagerops,
&devicepagerops,
&physpagerops,
&deadpagerops
};
#define PAGER_MAP_SIZE (8 * 1024 * 1024)
#define BSWHSIZE 16
#define BSWHMASK (BSWHSIZE - 1)
TAILQ_HEAD(swqueue, buf);
int pager_map_size = PAGER_MAP_SIZE;
static struct vm_map pager_map_store;
struct vm_map *pager_map = &pager_map_store;
static vm_offset_t swapbkva_mem;
static vm_offset_t swapbkva_kva;
static struct swqueue bswlist_mem[BSWHSIZE];
static struct swqueue bswlist_kva[BSWHSIZE];
static struct swqueue bswlist_raw[BSWHSIZE];
static struct spinlock bswspin_mem[BSWHSIZE];
static struct spinlock bswspin_kva[BSWHSIZE];
static struct spinlock bswspin_raw[BSWHSIZE];
static int pbuf_raw_count;
static int pbuf_kva_count;
static int pbuf_mem_count;
SYSCTL_INT(_vm, OID_AUTO, pbuf_raw_count, CTLFLAG_RD, &pbuf_raw_count, 0,
"Kernel pbuf raw reservations");
SYSCTL_INT(_vm, OID_AUTO, pbuf_kva_count, CTLFLAG_RD, &pbuf_kva_count, 0,
"Kernel pbuf kva reservations");
SYSCTL_INT(_vm, OID_AUTO, pbuf_mem_count, CTLFLAG_RD, &pbuf_mem_count, 0,
"Kernel pbuf mem reservations");
static void
vm_pager_init(void *arg __unused)
{
int i;
for (i = 0; i < BSWHSIZE; ++i) {
TAILQ_INIT(&bswlist_mem[i]);
TAILQ_INIT(&bswlist_kva[i]);
TAILQ_INIT(&bswlist_raw[i]);
spin_init(&bswspin_mem[i], "bswmem");
spin_init(&bswspin_kva[i], "bswkva");
spin_init(&bswspin_raw[i], "bswraw");
}
}
SYSINIT(vm_mem, SI_BOOT1_VM, SI_ORDER_SECOND, vm_pager_init, NULL);
static
void
vm_pager_bufferinit(void *dummy __unused)
{
struct buf *bp;
long i;
swapbkva_mem = kmem_alloc_pageable(pager_map, nswbuf_mem * MAXPHYS,
VM_SUBSYS_BUFDATA);
if (!swapbkva_mem)
panic("Not enough pager_map VM space for physical buffers");
swapbkva_kva = kmem_alloc_pageable(pager_map, nswbuf_kva * MAXPHYS,
VM_SUBSYS_BUFDATA);
if (!swapbkva_kva)
panic("Not enough pager_map VM space for physical buffers");
bp = swbuf_mem;
for (i = 0; i < nswbuf_mem; ++i, ++bp) {
vm_page_t m;
vm_pindex_t pg;
int j;
bp->b_kvabase = (caddr_t)((intptr_t)i * MAXPHYS) + swapbkva_mem;
bp->b_kvasize = MAXPHYS;
bp->b_swindex = i & BSWHMASK;
bp->b_cpumask = smp_active_mask;
BUF_LOCKINIT(bp);
buf_dep_init(bp);
TAILQ_INSERT_HEAD(&bswlist_mem[i & BSWHMASK], bp, b_freelist);
atomic_add_int(&pbuf_mem_count, 1);
bp->b_data = bp->b_kvabase;
bp->b_bcount = MAXPHYS;
bp->b_xio.xio_pages = bp->b_xio.xio_internal_pages;
pg = (vm_offset_t)bp->b_kvabase >> PAGE_SHIFT;
vm_object_hold(kernel_object);
for (j = 0; j < MAXPHYS / PAGE_SIZE; ++j) {
m = vm_page_alloc(kernel_object, pg, VM_ALLOC_NORMAL |
VM_ALLOC_SYSTEM);
KKASSERT(m != NULL);
bp->b_xio.xio_internal_pages[j] = m;
vm_page_wire(m);
pmap_kenter_noinval(pg * PAGE_SIZE, VM_PAGE_TO_PHYS(m));
cpu_invlpg((void *)(pg * PAGE_SIZE));
vm_page_wakeup(m);
++pg;
}
vm_object_drop(kernel_object);
bp->b_xio.xio_npages = j;
}
bp = swbuf_kva;
for (i = 0; i < nswbuf_kva; ++i, ++bp) {
bp->b_kvabase = (caddr_t)((intptr_t)i * MAXPHYS) + swapbkva_kva;
bp->b_kvasize = MAXPHYS;
bp->b_swindex = i & BSWHMASK;
BUF_LOCKINIT(bp);
buf_dep_init(bp);
TAILQ_INSERT_HEAD(&bswlist_kva[i & BSWHMASK], bp, b_freelist);
atomic_add_int(&pbuf_kva_count, 1);
}
nswbuf_raw = nbuf * 2;
swbuf_raw = (void *)kmem_alloc3(kernel_map,
round_page(nswbuf_raw * sizeof(struct buf)),
VM_SUBSYS_BUFDATA,
KM_NOTLBSYNC);
smp_invltlb();
bp = swbuf_raw;
for (i = 0; i < nswbuf_raw; ++i, ++bp) {
bp->b_swindex = i & BSWHMASK;
BUF_LOCKINIT(bp);
buf_dep_init(bp);
TAILQ_INSERT_HEAD(&bswlist_raw[i & BSWHMASK], bp, b_freelist);
atomic_add_int(&pbuf_raw_count, 1);
}
}
SYSINIT(do_vmpg, SI_BOOT2_MACHDEP, SI_ORDER_FIRST, vm_pager_bufferinit, NULL);
void
vm_pager_deallocate(vm_object_t object)
{
(*pagertab[object->type]->pgo_dealloc) (object);
}
vm_object_t
vm_pager_object_lookup(struct pagerlst *pg_list, void *handle)
{
vm_object_t object;
TAILQ_FOREACH(object, pg_list, pager_object_entry) {
if (object->handle == handle) {
VM_OBJECT_LOCK(object);
if ((object->flags & OBJ_DEAD) == 0) {
vm_object_reference_locked(object);
VM_OBJECT_UNLOCK(object);
break;
}
VM_OBJECT_UNLOCK(object);
}
}
return (object);
}
static void
initpbuf(struct buf *bp)
{
bp->b_qindex = 0;
bp->b_data = bp->b_kvabase;
bp->b_flags = B_PAGING;
bp->b_cmd = BUF_CMD_DONE;
bp->b_error = 0;
bp->b_bcount = 0;
bp->b_bufsize = MAXPHYS;
initbufbio(bp);
xio_init(&bp->b_xio);
BUF_LOCK(bp, LK_EXCLUSIVE);
}
struct buf *
getpbuf(int *pfreecnt)
{
struct buf *bp;
int iter;
int loops;
for (;;) {
while (pfreecnt && *pfreecnt <= 0) {
tsleep_interlock(pfreecnt, 0);
if ((int)atomic_fetchadd_int(pfreecnt, 0) <= 0)
tsleep(pfreecnt, PINTERLOCKED, "wswbuf0", 0);
}
if (pbuf_raw_count <= 0) {
tsleep_interlock(&pbuf_raw_count, 0);
if ((int)atomic_fetchadd_int(&pbuf_raw_count, 0) <= 0)
tsleep(&pbuf_raw_count, PINTERLOCKED,
"wswbuf1", 0);
continue;
}
iter = mycpuid & BSWHMASK;
for (loops = BSWHSIZE; loops; --loops) {
if (TAILQ_FIRST(&bswlist_raw[iter]) == NULL) {
iter = (iter + 1) & BSWHMASK;
continue;
}
spin_lock(&bswspin_raw[iter]);
if ((bp = TAILQ_FIRST(&bswlist_raw[iter])) == NULL) {
spin_unlock(&bswspin_raw[iter]);
iter = (iter + 1) & BSWHMASK;
continue;
}
TAILQ_REMOVE(&bswlist_raw[iter], bp, b_freelist);
atomic_add_int(&pbuf_raw_count, -1);
if (pfreecnt)
atomic_add_int(pfreecnt, -1);
spin_unlock(&bswspin_raw[iter]);
initpbuf(bp);
return bp;
}
}
}
struct buf *
getpbuf_kva(int *pfreecnt)
{
struct buf *bp;
int iter;
int loops;
for (;;) {
while (pfreecnt && *pfreecnt <= 0) {
tsleep_interlock(pfreecnt, 0);
if ((int)atomic_fetchadd_int(pfreecnt, 0) <= 0)
tsleep(pfreecnt, PINTERLOCKED, "wswbuf2", 0);
}
if (pbuf_kva_count <= 0) {
tsleep_interlock(&pbuf_kva_count, 0);
if ((int)atomic_fetchadd_int(&pbuf_kva_count, 0) <= 0)
tsleep(&pbuf_kva_count, PINTERLOCKED,
"wswbuf3", 0);
continue;
}
iter = mycpuid & BSWHMASK;
for (loops = BSWHSIZE; loops; --loops) {
if (TAILQ_FIRST(&bswlist_kva[iter]) == NULL) {
iter = (iter + 1) & BSWHMASK;
continue;
}
spin_lock(&bswspin_kva[iter]);
if ((bp = TAILQ_FIRST(&bswlist_kva[iter])) == NULL) {
spin_unlock(&bswspin_kva[iter]);
iter = (iter + 1) & BSWHMASK;
continue;
}
TAILQ_REMOVE(&bswlist_kva[iter], bp, b_freelist);
atomic_add_int(&pbuf_kva_count, -1);
if (pfreecnt)
atomic_add_int(pfreecnt, -1);
spin_unlock(&bswspin_kva[iter]);
initpbuf(bp);
return bp;
}
}
}
struct buf *
getpbuf_mem(int *pfreecnt)
{
struct buf *bp;
int iter;
int loops;
for (;;) {
while (pfreecnt && *pfreecnt <= 0) {
tsleep_interlock(pfreecnt, 0);
if ((int)atomic_fetchadd_int(pfreecnt, 0) <= 0)
tsleep(pfreecnt, PINTERLOCKED, "wswbuf4", 0);
}
if (pbuf_mem_count <= 0) {
tsleep_interlock(&pbuf_mem_count, 0);
if ((int)atomic_fetchadd_int(&pbuf_mem_count, 0) <= 0)
tsleep(&pbuf_mem_count, PINTERLOCKED,
"wswbuf5", 0);
continue;
}
iter = mycpuid & BSWHMASK;
for (loops = BSWHSIZE; loops; --loops) {
if (TAILQ_FIRST(&bswlist_mem[iter]) == NULL) {
iter = (iter + 1) & BSWHMASK;
continue;
}
spin_lock(&bswspin_mem[iter]);
if ((bp = TAILQ_FIRST(&bswlist_mem[iter])) == NULL) {
spin_unlock(&bswspin_mem[iter]);
iter = (iter + 1) & BSWHMASK;
continue;
}
TAILQ_REMOVE(&bswlist_mem[iter], bp, b_freelist);
atomic_add_int(&pbuf_mem_count, -1);
if (pfreecnt)
atomic_add_int(pfreecnt, -1);
spin_unlock(&bswspin_mem[iter]);
initpbuf(bp);
return bp;
}
}
}
struct buf *
trypbuf(int *pfreecnt)
{
struct buf *bp;
int iter = mycpuid & BSWHMASK;
int loops;
for (loops = BSWHSIZE; loops; --loops) {
if (*pfreecnt <= 0 || TAILQ_FIRST(&bswlist_raw[iter]) == NULL) {
iter = (iter + 1) & BSWHMASK;
continue;
}
spin_lock(&bswspin_raw[iter]);
if (*pfreecnt <= 0 ||
(bp = TAILQ_FIRST(&bswlist_raw[iter])) == NULL) {
spin_unlock(&bswspin_raw[iter]);
iter = (iter + 1) & BSWHMASK;
continue;
}
TAILQ_REMOVE(&bswlist_raw[iter], bp, b_freelist);
atomic_add_int(&pbuf_raw_count, -1);
atomic_add_int(pfreecnt, -1);
spin_unlock(&bswspin_raw[iter]);
initpbuf(bp);
return bp;
}
return NULL;
}
struct buf *
trypbuf_kva(int *pfreecnt)
{
struct buf *bp;
int iter = mycpuid & BSWHMASK;
int loops;
for (loops = BSWHSIZE; loops; --loops) {
if (*pfreecnt <= 0 || TAILQ_FIRST(&bswlist_kva[iter]) == NULL) {
iter = (iter + 1) & BSWHMASK;
continue;
}
spin_lock(&bswspin_kva[iter]);
if (*pfreecnt <= 0 ||
(bp = TAILQ_FIRST(&bswlist_kva[iter])) == NULL) {
spin_unlock(&bswspin_kva[iter]);
iter = (iter + 1) & BSWHMASK;
continue;
}
TAILQ_REMOVE(&bswlist_kva[iter], bp, b_freelist);
atomic_add_int(&pbuf_kva_count, -1);
atomic_add_int(pfreecnt, -1);
spin_unlock(&bswspin_kva[iter]);
initpbuf(bp);
return bp;
}
return NULL;
}
void
relpbuf(struct buf *bp, int *pfreecnt)
{
int wake = 0;
int wake_free = 0;
int iter = bp->b_swindex;
KKASSERT(bp->b_flags & B_PAGING);
dsched_buf_exit(bp);
BUF_UNLOCK(bp);
if (bp >= swbuf_mem && bp < &swbuf_mem[nswbuf_mem]) {
KKASSERT(bp->b_kvabase);
spin_lock(&bswspin_mem[iter]);
TAILQ_INSERT_HEAD(&bswlist_mem[iter], bp, b_freelist);
if (atomic_fetchadd_int(&pbuf_mem_count, 1) == nswbuf_mem / 4)
wake = 1;
if (pfreecnt) {
if (atomic_fetchadd_int(pfreecnt, 1) == 1)
wake_free = 1;
}
spin_unlock(&bswspin_mem[iter]);
if (wake)
wakeup(&pbuf_mem_count);
} else if (bp >= swbuf_kva && bp < &swbuf_kva[nswbuf_kva]) {
KKASSERT(bp->b_kvabase);
CPUMASK_ASSZERO(bp->b_cpumask);
spin_lock(&bswspin_kva[iter]);
TAILQ_INSERT_HEAD(&bswlist_kva[iter], bp, b_freelist);
if (atomic_fetchadd_int(&pbuf_kva_count, 1) == nswbuf_kva / 4)
wake = 1;
if (pfreecnt) {
if (atomic_fetchadd_int(pfreecnt, 1) == 1)
wake_free = 1;
}
spin_unlock(&bswspin_kva[iter]);
if (wake)
wakeup(&pbuf_kva_count);
} else {
KKASSERT(bp->b_kvabase == NULL);
KKASSERT(bp >= swbuf_raw && bp < &swbuf_raw[nswbuf_raw]);
CPUMASK_ASSZERO(bp->b_cpumask);
spin_lock(&bswspin_raw[iter]);
TAILQ_INSERT_HEAD(&bswlist_raw[iter], bp, b_freelist);
if (atomic_fetchadd_int(&pbuf_raw_count, 1) == nswbuf_raw / 4)
wake = 1;
if (pfreecnt) {
if (atomic_fetchadd_int(pfreecnt, 1) == 1)
wake_free = 1;
}
spin_unlock(&bswspin_raw[iter]);
if (wake)
wakeup(&pbuf_raw_count);
}
if (wake_free)
wakeup(pfreecnt);
}
void
pbuf_adjcount(int *pfreecnt, int n)
{
if (n) {
atomic_add_int(pfreecnt, n);
wakeup(pfreecnt);
}
}