#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <sys/sysctl.h>
#include <vm/vm.h>
#include <vm/vm_param.h>
#include <sys/lock.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_kern.h>
#include <vm/vm_extern.h>
static struct vm_map kernel_map_store;
static struct vm_map clean_map_store;
static struct vm_map buffer_map_store;
struct vm_map *kernel_map = &kernel_map_store;
struct vm_map *clean_map = &clean_map_store;
struct vm_map *buffer_map = &buffer_map_store;
static __inline
int
KMVMCPU(int kmflags)
{
if ((kmflags & KM_CPU_SPEC) == 0)
return 0;
return VM_ALLOC_CPU(KM_GETCPU(kmflags));
}
void *
kmem_alloc_swapbacked(kmem_anon_desc_t *kp, vm_size_t size, vm_subsys_t id)
{
int error;
vm_pindex_t npages;
size = round_page(size);
npages = size / PAGE_SIZE;
if (kp->map == NULL)
kp->map = kernel_map;
kp->data = vm_map_min(kernel_map);
kp->size = size;
kp->object = vm_object_allocate(OBJT_DEFAULT, npages);
error = vm_map_find(kp->map, kp->object, NULL, 0,
&kp->data, size,
PAGE_SIZE, TRUE,
VM_MAPTYPE_NORMAL, id,
VM_PROT_ALL, VM_PROT_ALL, 0);
if (error) {
kprintf("kmem_alloc_swapbacked: %zd bytes failed %d\n",
size, error);
kp->data = (vm_offset_t)0;
kmem_free_swapbacked(kp);
return NULL;
}
return ((void *)(intptr_t)kp->data);
}
void
kmem_free_swapbacked(kmem_anon_desc_t *kp)
{
if (kp->data) {
kmem_free(kp->map, kp->data, kp->size);
kp->data = (vm_offset_t)0;
} else {
vm_object_deallocate(kp->object);
}
kp->object = NULL;
}
vm_offset_t
kmem_alloc_pageable(vm_map_t map, vm_size_t size, vm_subsys_t id)
{
vm_offset_t addr;
int result;
size = round_page(size);
addr = vm_map_min(map);
result = vm_map_find(map, NULL, NULL,
(vm_offset_t) 0, &addr, size,
PAGE_SIZE, TRUE,
VM_MAPTYPE_NORMAL, id,
VM_PROT_ALL, VM_PROT_ALL, 0);
if (result != KERN_SUCCESS)
return (0);
return (addr);
}
vm_offset_t
kmem_alloc_nofault(vm_map_t map, vm_size_t size, vm_subsys_t id,
vm_size_t align)
{
vm_offset_t addr;
int result;
size = round_page(size);
addr = vm_map_min(map);
result = vm_map_find(map, NULL, NULL,
(vm_offset_t) 0, &addr, size,
align, TRUE,
VM_MAPTYPE_NORMAL, id,
VM_PROT_ALL, VM_PROT_ALL, COWF_NOFAULT);
if (result != KERN_SUCCESS)
return (0);
return (addr);
}
vm_offset_t
kmem_alloc3(vm_map_t map, vm_size_t size, vm_subsys_t id, int kmflags)
{
vm_offset_t addr;
vm_offset_t gstart;
vm_offset_t i;
int count;
int cow;
size = round_page(size);
if (kmflags & KM_KRESERVE)
count = vm_map_entry_kreserve(MAP_RESERVE_COUNT);
else
count = vm_map_entry_reserve(MAP_RESERVE_COUNT);
if (kmflags & KM_STACK) {
cow = COWF_IS_KSTACK;
gstart = PAGE_SIZE;
} else {
cow = 0;
gstart = 0;
}
vm_map_lock(map);
if (vm_map_findspace(map, vm_map_min(map), size, PAGE_SIZE, 0, &addr)) {
vm_map_unlock(map);
if (kmflags & KM_KRESERVE)
vm_map_entry_krelease(count);
else
vm_map_entry_release(count);
return (0);
}
vm_object_hold(kernel_object);
vm_object_reference_locked(kernel_object);
vm_map_insert(map, &count,
kernel_object, NULL,
addr, NULL,
addr, addr + size,
VM_MAPTYPE_NORMAL, id,
VM_PROT_ALL, VM_PROT_ALL, cow);
vm_object_drop(kernel_object);
vm_map_unlock(map);
if (kmflags & KM_KRESERVE)
vm_map_entry_krelease(count);
else
vm_map_entry_release(count);
vm_object_hold(kernel_object);
for (i = gstart; i < size; i += PAGE_SIZE) {
vm_page_t mem;
mem = vm_page_grab(kernel_object, OFF_TO_IDX(addr + i),
VM_ALLOC_FORCE_ZERO | VM_ALLOC_NORMAL |
VM_ALLOC_RETRY | KMVMCPU(kmflags));
vm_page_unqueue_nowakeup(mem);
vm_page_wakeup(mem);
}
vm_object_drop(kernel_object);
vm_map_kernel_wiring(map, addr, addr + size, kmflags);
return (addr);
}
void
kmem_free(vm_map_t map, vm_offset_t addr, vm_size_t size)
{
vm_map_remove(map, trunc_page(addr), round_page(addr + size));
}
void
kmem_suballoc(vm_map_t parent, vm_map_t result,
vm_offset_t *min, vm_offset_t *max, vm_size_t size)
{
int ret;
size = round_page(size);
*min = (vm_offset_t) vm_map_min(parent);
ret = vm_map_find(parent, NULL, NULL,
(vm_offset_t) 0, min, size,
PAGE_SIZE, TRUE,
VM_MAPTYPE_UNSPECIFIED, VM_SUBSYS_SYSMAP,
VM_PROT_ALL, VM_PROT_ALL, 0);
if (ret != KERN_SUCCESS) {
kprintf("kmem_suballoc: bad status return of %d.\n", ret);
panic("kmem_suballoc");
}
*max = *min + size;
pmap_reference(vm_map_pmap(parent));
vm_map_init(result, *min, *max, vm_map_pmap(parent));
if ((ret = vm_map_submap(parent, *min, *max, result)) != KERN_SUCCESS)
panic("kmem_suballoc: unable to change range to submap");
}
vm_offset_t
kmem_alloc_wait(vm_map_t map, vm_size_t size, vm_subsys_t id)
{
vm_offset_t addr;
int count;
size = round_page(size);
count = vm_map_entry_reserve(MAP_RESERVE_COUNT);
for (;;) {
vm_map_lock(map);
if (vm_map_findspace(map, vm_map_min(map),
size, PAGE_SIZE, 0, &addr) == 0) {
break;
}
if (vm_map_max(map) - vm_map_min(map) < size) {
vm_map_entry_release(count);
vm_map_unlock(map);
return (0);
}
vm_map_unlock(map);
tsleep(map, 0, "kmaw", 0);
}
vm_map_insert(map, &count,
NULL, NULL,
(vm_offset_t)0, NULL,
addr, addr + size,
VM_MAPTYPE_NORMAL, id,
VM_PROT_ALL, VM_PROT_ALL, 0);
vm_map_unlock(map);
vm_map_entry_release(count);
return (addr);
}
vm_offset_t
kmem_alloc_attr(vm_map_t map, vm_size_t size, vm_subsys_t id,
int flags, vm_paddr_t low,
vm_paddr_t high, vm_memattr_t memattr)
{
vm_offset_t addr, i, offset;
vm_page_t m;
int count;
size = round_page(size);
count = vm_map_entry_reserve(MAP_RESERVE_COUNT);
vm_map_lock(map);
if (vm_map_findspace(map, vm_map_min(map), size, PAGE_SIZE,
flags, &addr)) {
vm_map_unlock(map);
vm_map_entry_release(count);
return (0);
}
offset = addr - vm_map_min(kernel_map);
vm_object_hold(kernel_object);
vm_object_reference_locked(kernel_object);
vm_map_insert(map, &count,
kernel_object, NULL,
offset, NULL,
addr, addr + size,
VM_MAPTYPE_NORMAL, id,
VM_PROT_ALL, VM_PROT_ALL, 0);
vm_map_unlock(map);
vm_map_entry_release(count);
vm_object_drop(kernel_object);
for (i = 0; i < size; i += PAGE_SIZE) {
m = vm_page_alloc_contig(low, high, PAGE_SIZE, 0,
PAGE_SIZE, memattr);
if (!m) {
return (0);
}
vm_object_hold(kernel_object);
vm_page_insert(m, kernel_object, OFF_TO_IDX(offset + i));
vm_object_drop(kernel_object);
if (flags & M_ZERO)
pmap_zero_page(VM_PAGE_TO_PHYS(m));
m->valid = VM_PAGE_BITS_ALL;
}
vm_map_kernel_wiring(map, addr, addr + size, 0);
return (addr);
}
void
kmem_free_wakeup(vm_map_t map, vm_offset_t addr, vm_size_t size)
{
int count;
count = vm_map_entry_reserve(MAP_RESERVE_COUNT);
vm_map_lock(map);
vm_map_delete(map, trunc_page(addr), round_page(addr + size), &count);
wakeup(map);
vm_map_unlock(map);
vm_map_entry_release(count);
}
void
kmem_init(void)
{
vm_offset_t addr;
vm_map_t m;
int count;
m = kernel_map;
vm_map_init(m, KvaStart, KvaEnd, kernel_pmap);
vm_map_lock(m);
m->system_map = 1;
count = vm_map_entry_reserve(MAP_RESERVE_COUNT);
addr = KvaStart;
if (virtual2_start) {
if (addr < virtual2_start) {
vm_map_insert(m, &count,
NULL, NULL,
(vm_offset_t) 0, NULL,
addr, virtual2_start,
VM_MAPTYPE_NORMAL, VM_SUBSYS_RESERVED,
VM_PROT_ALL, VM_PROT_ALL, 0);
}
addr = virtual2_end;
}
if (addr < virtual_start) {
vm_map_insert(m, &count,
NULL, NULL,
(vm_offset_t) 0, NULL,
addr, virtual_start,
VM_MAPTYPE_NORMAL, VM_SUBSYS_RESERVED,
VM_PROT_ALL, VM_PROT_ALL, 0);
}
addr = virtual_end;
if (addr < KvaEnd) {
vm_map_insert(m, &count,
NULL, NULL,
(vm_offset_t) 0, NULL,
addr, KvaEnd,
VM_MAPTYPE_NORMAL, VM_SUBSYS_RESERVED,
VM_PROT_ALL, VM_PROT_ALL, 0);
}
vm_map_unlock(m);
vm_map_entry_release(count);
}
static int
kvm_size(SYSCTL_HANDLER_ARGS)
{
unsigned long ksize = KvaSize;
return sysctl_handle_long(oidp, &ksize, 0, req);
}
SYSCTL_PROC(_vm, OID_AUTO, kvm_size, CTLTYPE_ULONG|CTLFLAG_RD,
0, 0, kvm_size, "LU", "Size of KVM");
static int
kvm_free(SYSCTL_HANDLER_ARGS)
{
unsigned long kfree = virtual_end - kernel_vm_end;
return sysctl_handle_long(oidp, &kfree, 0, req);
}
SYSCTL_PROC(_vm, OID_AUTO, kvm_free, CTLTYPE_ULONG|CTLFLAG_RD,
0, 0, kvm_free, "LU", "Amount of KVM free");