#include <sys/param.h>
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/pool.h>
#include <sys/proc.h>
#include <sys/mount.h>
#include <uvm/uvm_extern.h>
vaddr_t buf_kva_start, buf_kva_end;
int buf_needva;
TAILQ_HEAD(,buf) buf_valist;
extern struct bcachestats bcstats;
vaddr_t buf_unmap(struct buf *);
void
buf_mem_init(vsize_t size)
{
TAILQ_INIT(&buf_valist);
buf_kva_start = vm_map_min(kernel_map);
if (uvm_map(kernel_map, &buf_kva_start, size, NULL,
UVM_UNKNOWN_OFFSET, PAGE_SIZE, UVM_MAPFLAG(PROT_NONE,
PROT_NONE, MAP_INHERIT_NONE, MADV_NORMAL, 0)))
panic("%s: can't reserve VM for buffers", __func__);
buf_kva_end = buf_kva_start + size;
bcstats.kvaslots = bcstats.kvaslots_avail = size / MAXPHYS;
}
void
buf_acquire(struct buf *bp)
{
KASSERT((bp->b_flags & B_BUSY) == 0);
splassert(IPL_BIO);
SET(bp->b_flags, B_BUSY);
buf_map(bp);
}
void
buf_acquire_nomap(struct buf *bp)
{
splassert(IPL_BIO);
SET(bp->b_flags, B_BUSY);
if (bp->b_data != NULL) {
TAILQ_REMOVE(&buf_valist, bp, b_valist);
bcstats.kvaslots_avail--;
bcstats.busymapped++;
}
}
void
buf_map(struct buf *bp)
{
vaddr_t va;
splassert(IPL_BIO);
if (bp->b_data == NULL) {
unsigned long i;
if (buf_kva_start < buf_kva_end) {
va = buf_kva_start;
buf_kva_start += MAXPHYS;
bcstats.kvaslots_avail--;
} else {
struct buf *vbp;
vbp = TAILQ_FIRST(&buf_valist);
while ((curproc != syncerproc &&
curproc != cleanerproc &&
bcstats.kvaslots_avail <= RESERVE_SLOTS) ||
vbp == NULL) {
buf_needva++;
tsleep_nsec(&buf_needva, PRIBIO, "buf_needva",
INFSLP);
vbp = TAILQ_FIRST(&buf_valist);
}
va = buf_unmap(vbp);
}
for (i = 0; i < atop(bp->b_bufsize); i++) {
struct vm_page *pg = uvm_pagelookup(bp->b_pobj,
bp->b_poffs + ptoa(i));
KASSERT(pg != NULL);
pmap_kenter_pa(va + ptoa(i), VM_PAGE_TO_PHYS(pg),
PROT_READ | PROT_WRITE);
}
pmap_update(pmap_kernel());
bp->b_data = (caddr_t)va;
} else {
TAILQ_REMOVE(&buf_valist, bp, b_valist);
bcstats.kvaslots_avail--;
}
bcstats.busymapped++;
}
void
buf_release(struct buf *bp)
{
KASSERT(bp->b_flags & B_BUSY);
splassert(IPL_BIO);
if (bp->b_data) {
bcstats.busymapped--;
TAILQ_INSERT_TAIL(&buf_valist, bp, b_valist);
bcstats.kvaslots_avail++;
if (buf_needva) {
buf_needva=0;
wakeup(&buf_needva);
}
}
CLR(bp->b_flags, B_BUSY);
}
int
buf_dealloc_mem(struct buf *bp)
{
caddr_t data;
splassert(IPL_BIO);
data = bp->b_data;
bp->b_data = NULL;
if (data) {
if (bp->b_flags & B_BUSY)
bcstats.busymapped--;
pmap_kremove((vaddr_t)data, bp->b_bufsize);
pmap_update(pmap_kernel());
}
if (bp->b_pobj)
buf_free_pages(bp);
if (data == NULL)
return (0);
bp->b_data = data;
if (!(bp->b_flags & B_BUSY)) {
TAILQ_REMOVE(&buf_valist, bp, b_valist);
bcstats.kvaslots_avail--;
} else {
CLR(bp->b_flags, B_BUSY);
if (buf_needva) {
buf_needva = 0;
wakeup(&buf_needva);
}
}
SET(bp->b_flags, B_RELEASED);
TAILQ_INSERT_HEAD(&buf_valist, bp, b_valist);
bcstats.kvaslots_avail++;
return (1);
}
void
buf_fix_mapping(struct buf *bp, vsize_t newsize)
{
vaddr_t va = (vaddr_t)bp->b_data;
if (newsize < bp->b_bufsize) {
pmap_kremove(va + newsize, bp->b_bufsize - newsize);
pmap_update(pmap_kernel());
bp->b_bufsize = newsize;
}
}
vaddr_t
buf_unmap(struct buf *bp)
{
vaddr_t va;
KASSERT((bp->b_flags & B_BUSY) == 0);
KASSERT(bp->b_data != NULL);
splassert(IPL_BIO);
TAILQ_REMOVE(&buf_valist, bp, b_valist);
bcstats.kvaslots_avail--;
va = (vaddr_t)bp->b_data;
bp->b_data = NULL;
pmap_kremove(va, bp->b_bufsize);
pmap_update(pmap_kernel());
if (bp->b_flags & B_RELEASED)
pool_put(&bufpool, bp);
return (va);
}
void
buf_alloc_pages(struct buf *bp, vsize_t size)
{
int i;
KASSERT(size == round_page(size));
KASSERT(bp->b_pobj == NULL);
KASSERT(bp->b_data == NULL);
splassert(IPL_BIO);
uvm_obj_init(&bp->b_uobj, &bufcache_pager, 1);
do {
i = uvm_pagealloc_multi(&bp->b_uobj, 0, size,
UVM_PLA_NOWAIT | UVM_PLA_NOWAKE);
if (i == 0)
break;
} while (bufbackoff(&dma_constraint, atop(size)) >= atop(size));
if (i != 0)
i = uvm_pagealloc_multi(&bp->b_uobj, 0, size,
UVM_PLA_WAITOK);
if (i != 0)
panic("uvm_pagealloc_multi unable to allocate an buf_object "
"of size %lu", size);
bcstats.numbufpages += atop(size);
bp->b_pobj = &bp->b_uobj;
bp->b_poffs = 0;
bp->b_bufsize = size;
}
void
buf_free_pages(struct buf *bp)
{
struct uvm_object *uobj = bp->b_pobj;
struct vm_page *pg;
voff_t off, i;
KASSERT(bp->b_data == NULL);
KASSERT(uobj != NULL);
splassert(IPL_BIO);
off = bp->b_poffs;
bp->b_pobj = NULL;
bp->b_poffs = 0;
for (i = 0; i < atop(bp->b_bufsize); i++) {
pg = uvm_pagelookup(uobj, off + ptoa(i));
KASSERT(pg != NULL);
KASSERT(pg->wire_count == 1);
pg->wire_count = 0;
bcstats.numbufpages--;
}
uvm_obj_free(uobj);
}