#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/serialize.h>
#include <sys/lock.h>
#include <sys/vmmeter.h>
#include <sys/mman.h>
#include <sys/vnode.h>
#include <sys/resourcevar.h>
#include <sys/shm.h>
#include <sys/tree.h>
#include <sys/malloc.h>
#include <sys/objcache.h>
#include <sys/kern_syscall.h>
#include <vm/vm.h>
#include <vm/vm_param.h>
#include <vm/pmap.h>
#include <vm/vm_map.h>
#include <vm/vm_page.h>
#include <vm/vm_object.h>
#include <vm/vm_pager.h>
#include <vm/vm_kern.h>
#include <vm/vm_extern.h>
#include <vm/swap_pager.h>
#include <vm/vm_zone.h>
#include <sys/random.h>
#include <sys/sysctl.h>
#include <sys/spinlock.h>
#include <sys/thread2.h>
#include <sys/spinlock2.h>
static boolean_t vmspace_ctor(void *obj, void *privdata, int ocflags);
static void vmspace_dtor(void *obj, void *privdata);
static void vmspace_terminate(struct vmspace *vm, int final);
MALLOC_DEFINE(M_VMSPACE, "vmspace", "vmspace objcache backingstore");
MALLOC_DEFINE(M_MAP_BACKING, "map_backing", "vm_map_backing to entry");
static struct objcache *vmspace_cache;
#define MAPENTRYBSP_CACHE (MAXCPU+1)
#define MAPENTRYAP_CACHE 8
#define MAP_ENTRY_PARTITION_SIZE ((vm_offset_t)(32 * 1024 * 1024))
#define MAP_ENTRY_PARTITION_MASK (MAP_ENTRY_PARTITION_SIZE - 1)
#define VM_MAP_ENTRY_WITHIN_PARTITION(entry) \
((((entry)->ba.start ^ (entry)->ba.end) & ~MAP_ENTRY_PARTITION_MASK) == 0)
static struct vm_zone mapentzone_store;
__read_mostly static vm_zone_t mapentzone;
static struct vm_map_entry map_entry_init[MAX_MAPENT];
static struct vm_map_entry cpu_map_entry_init_bsp[MAPENTRYBSP_CACHE];
static struct vm_map_entry cpu_map_entry_init_ap[MAXCPU][MAPENTRYAP_CACHE];
__read_mostly static int randomize_mmap;
SYSCTL_INT(_vm, OID_AUTO, randomize_mmap, CTLFLAG_RW, &randomize_mmap, 0,
"Randomize mmap offsets");
__read_mostly static int vm_map_relock_enable = 1;
SYSCTL_INT(_vm, OID_AUTO, map_relock_enable, CTLFLAG_RW,
&vm_map_relock_enable, 0, "insert pop pgtable optimization");
__read_mostly static int vm_map_partition_enable = 1;
SYSCTL_INT(_vm, OID_AUTO, map_partition_enable, CTLFLAG_RW,
&vm_map_partition_enable, 0, "Break up larger vm_map_entry's");
__read_mostly static int vm_map_backing_limit = 5;
SYSCTL_INT(_vm, OID_AUTO, map_backing_limit, CTLFLAG_RW,
&vm_map_backing_limit, 0, "ba.backing_ba link depth");
__read_mostly static int vm_map_backing_shadow_test = 1;
SYSCTL_INT(_vm, OID_AUTO, map_backing_shadow_test, CTLFLAG_RW,
&vm_map_backing_shadow_test, 0, "ba.object shadow test");
static long vpagetable_setmap_count = 0;
SYSCTL_LONG(_vm, OID_AUTO, vpagetable_setmap, CTLFLAG_RW,
&vpagetable_setmap_count, 0, "Number of MADV_SETMAP calls");
static long vpagetable_inval_count = 0;
SYSCTL_LONG(_vm, OID_AUTO, vpagetable_inval, CTLFLAG_RW,
&vpagetable_inval_count, 0, "Number of MADV_INVAL calls");
extern int debug_vpagetable;
static void vmspace_drop_notoken(struct vmspace *vm);
static void vm_map_entry_shadow(vm_map_entry_t entry);
static vm_map_entry_t vm_map_entry_create(int *);
static void vm_map_entry_dispose (vm_map_t map, vm_map_entry_t entry, int *);
static void vm_map_entry_dispose_ba (vm_map_entry_t entry, vm_map_backing_t ba);
static void vm_map_backing_replicated(vm_map_t map,
vm_map_entry_t entry, int flags);
static void vm_map_backing_adjust_start(vm_map_entry_t entry,
vm_ooffset_t start);
static void vm_map_backing_adjust_end(vm_map_entry_t entry,
vm_ooffset_t end);
static void vm_map_backing_attach (vm_map_entry_t entry, vm_map_backing_t ba);
static void vm_map_backing_detach (vm_map_entry_t entry, vm_map_backing_t ba);
static void _vm_map_clip_end (vm_map_t, vm_map_entry_t, vm_offset_t, int *);
static void _vm_map_clip_start (vm_map_t, vm_map_entry_t, vm_offset_t, int *);
static void vm_map_entry_delete (vm_map_t, vm_map_entry_t, int *);
static void vm_map_entry_unwire_all (vm_map_t, vm_map_entry_t);
static void vm_map_copy_entry (vm_map_t, vm_map_t, vm_map_entry_t,
vm_map_entry_t);
static void vm_map_unclip_range (vm_map_t map, vm_map_entry_t start_entry,
vm_offset_t start, vm_offset_t end, int *countp, int flags);
static void vm_map_entry_partition(vm_map_t map, vm_map_entry_t entry,
vm_offset_t vaddr, int *countp);
#define MAP_BACK_CLIPPED 0x0001
#define MAP_BACK_BASEOBJREFD 0x0002
void
vm_map_startup(void)
{
mapentzone = &mapentzone_store;
zbootinit(mapentzone, "MAP ENTRY", sizeof (struct vm_map_entry),
map_entry_init, MAX_MAPENT);
mapentzone_store.zflags |= ZONE_SPECIAL;
}
void
vm_init2(void)
{
vmspace_cache = objcache_create_mbacked(M_VMSPACE,
sizeof(struct vmspace),
0, ncpus * 4,
vmspace_ctor, vmspace_dtor,
NULL);
zinitna(mapentzone, NULL, 0, 0, ZONE_USE_RESERVE | ZONE_SPECIAL);
pmap_init2();
vm_object_init2();
}
static
boolean_t
vmspace_ctor(void *obj, void *privdata, int ocflags)
{
struct vmspace *vm = obj;
bzero(vm, sizeof(*vm));
vm->vm_refcnt = VM_REF_DELETED;
return 1;
}
static
void
vmspace_dtor(void *obj, void *privdata)
{
struct vmspace *vm = obj;
KKASSERT(vm->vm_refcnt == VM_REF_DELETED);
pmap_puninit(vmspace_pmap(vm));
}
static int rb_vm_map_compare(vm_map_entry_t a, vm_map_entry_t b);
RB_GENERATE(vm_map_rb_tree, vm_map_entry, rb_entry, rb_vm_map_compare);
static int
rb_vm_map_compare(vm_map_entry_t a, vm_map_entry_t b)
{
if (a->ba.start < b->ba.start)
return(-1);
else if (a->ba.start > b->ba.start)
return(1);
return(0);
}
void
vmspace_initrefs(struct vmspace *vm)
{
vm->vm_refcnt = 1;
vm->vm_holdcnt = 1;
}
struct vmspace *
vmspace_alloc(vm_offset_t min, vm_offset_t max)
{
struct vmspace *vm;
vm = objcache_get(vmspace_cache, M_WAITOK);
bzero(&vm->vm_startcopy,
(char *)&vm->vm_endcopy - (char *)&vm->vm_startcopy);
vm_map_init(&vm->vm_map, min, max, NULL);
KKASSERT(vm->vm_holdcnt == 0);
KKASSERT(vm->vm_refcnt == VM_REF_DELETED);
vmspace_initrefs(vm);
vmspace_hold(vm);
pmap_pinit(vmspace_pmap(vm));
vm->vm_map.pmap = vmspace_pmap(vm);
vm->vm_shm = NULL;
vm->vm_flags = 0;
cpu_vmspace_alloc(vm);
vmspace_drop(vm);
return (vm);
}
int
vmspace_getrefs(struct vmspace *vm)
{
int32_t n;
n = vm->vm_refcnt;
cpu_ccfence();
if (n & VM_REF_DELETED)
n = -1;
return n;
}
void
vmspace_hold(struct vmspace *vm)
{
atomic_add_int(&vm->vm_holdcnt, 1);
lwkt_gettoken(&vm->vm_map.token);
}
void
vmspace_drop(struct vmspace *vm)
{
lwkt_reltoken(&vm->vm_map.token);
vmspace_drop_notoken(vm);
}
static void
vmspace_drop_notoken(struct vmspace *vm)
{
if (atomic_fetchadd_int(&vm->vm_holdcnt, -1) == 1) {
if (vm->vm_refcnt & VM_REF_DELETED)
vmspace_terminate(vm, 1);
}
}
void
vmspace_ref(struct vmspace *vm)
{
uint32_t n;
atomic_add_int(&vm->vm_holdcnt, 1);
n = atomic_fetchadd_int(&vm->vm_refcnt, 1);
KKASSERT((n & VM_REF_DELETED) == 0);
}
void
vmspace_rel(struct vmspace *vm)
{
uint32_t n;
n = atomic_fetchadd_int(&vm->vm_refcnt, -1) - 1;
while (n == 0) {
if (atomic_cmpset_int(&vm->vm_refcnt, 0, VM_REF_DELETED)) {
vmspace_terminate(vm, 0);
break;
}
n = vm->vm_refcnt;
cpu_ccfence();
}
vmspace_drop_notoken(vm);
}
void
vmspace_relexit(struct vmspace *vm)
{
atomic_add_int(&vm->vm_holdcnt, 1);
vmspace_rel(vm);
}
void
vmspace_exitfree(struct proc *p)
{
struct vmspace *vm;
vm = p->p_vmspace;
p->p_vmspace = NULL;
vmspace_drop_notoken(vm);
}
static void
vmspace_terminate(struct vmspace *vm, int final)
{
int count;
lwkt_gettoken(&vm->vm_map.token);
if (final == 0) {
KKASSERT((vm->vm_flags & VMSPACE_EXIT1) == 0);
vm->vm_flags |= VMSPACE_EXIT1;
shmexit(vm);
pmap_remove_pages(vmspace_pmap(vm), VM_MIN_USER_ADDRESS,
VM_MAX_USER_ADDRESS);
vm_map_remove(&vm->vm_map, VM_MIN_USER_ADDRESS,
VM_MAX_USER_ADDRESS);
#if 0
if (vmspace_pmap(vm)->pm_stats.wired_count) {
vm_map_remove(&vm->vm_map, VM_MIN_USER_ADDRESS,
VM_MAX_USER_ADDRESS);
pmap_remove_pages(vmspace_pmap(vm), VM_MIN_USER_ADDRESS,
VM_MAX_USER_ADDRESS);
} else {
pmap_remove_pages(vmspace_pmap(vm), VM_MIN_USER_ADDRESS,
VM_MAX_USER_ADDRESS);
vm_map_remove(&vm->vm_map, VM_MIN_USER_ADDRESS,
VM_MAX_USER_ADDRESS);
}
#endif
lwkt_reltoken(&vm->vm_map.token);
} else {
KKASSERT((vm->vm_flags & VMSPACE_EXIT1) != 0);
KKASSERT((vm->vm_flags & VMSPACE_EXIT2) == 0);
vm->vm_flags |= VMSPACE_EXIT2;
shmexit(vm);
count = vm_map_entry_reserve(MAP_RESERVE_COUNT);
vm_map_lock(&vm->vm_map);
cpu_vmspace_free(vm);
vm_map_delete(&vm->vm_map,
vm_map_min(&vm->vm_map),
vm_map_max(&vm->vm_map),
&count);
vm_map_unlock(&vm->vm_map);
vm_map_entry_release(count);
pmap_release(vmspace_pmap(vm));
lwkt_reltoken(&vm->vm_map.token);
objcache_put(vmspace_cache, vm);
}
}
vm_offset_t
vmspace_swap_count(struct vmspace *vm)
{
vm_map_t map = &vm->vm_map;
vm_map_entry_t cur;
vm_object_t object;
vm_offset_t count = 0;
vm_offset_t n;
vmspace_hold(vm);
RB_FOREACH(cur, vm_map_rb_tree, &map->rb_root) {
switch(cur->maptype) {
case VM_MAPTYPE_NORMAL:
case VM_MAPTYPE_VPAGETABLE:
if ((object = cur->ba.object) == NULL)
break;
if (object->swblock_count) {
n = (cur->ba.end - cur->ba.start) / PAGE_SIZE;
count += object->swblock_count *
SWAP_META_PAGES * n / object->size + 1;
}
break;
default:
break;
}
}
vmspace_drop(vm);
return(count);
}
vm_offset_t
vmspace_anonymous_count(struct vmspace *vm)
{
vm_map_t map = &vm->vm_map;
vm_map_entry_t cur;
vm_object_t object;
vm_offset_t count = 0;
vmspace_hold(vm);
RB_FOREACH(cur, vm_map_rb_tree, &map->rb_root) {
switch(cur->maptype) {
case VM_MAPTYPE_NORMAL:
case VM_MAPTYPE_VPAGETABLE:
if ((object = cur->ba.object) == NULL)
break;
if (object->type != OBJT_DEFAULT &&
object->type != OBJT_SWAP) {
break;
}
count += object->resident_page_count;
break;
default:
break;
}
}
vmspace_drop(vm);
return(count);
}
void
vm_map_init(struct vm_map *map, vm_offset_t min_addr, vm_offset_t max_addr,
pmap_t pmap)
{
RB_INIT(&map->rb_root);
spin_init(&map->ilock_spin, "ilock");
map->ilock_base = NULL;
map->nentries = 0;
map->size = 0;
map->system_map = 0;
vm_map_min(map) = min_addr;
vm_map_max(map) = max_addr;
map->pmap = pmap;
map->timestamp = 0;
map->flags = 0;
bzero(&map->freehint, sizeof(map->freehint));
lwkt_token_init(&map->token, "vm_map");
lockinit(&map->lock, "vm_maplk", (hz + 9) / 10, 0);
}
static
vm_offset_t
vm_map_freehint_find(vm_map_t map, vm_size_t length, vm_size_t align)
{
vm_map_freehint_t *scan;
scan = &map->freehint[0];
while (scan < &map->freehint[VM_MAP_FFCOUNT]) {
if (scan->length == length && scan->align == align)
return(scan->start);
++scan;
}
return 0;
}
static
void
vm_map_freehint_update(vm_map_t map, vm_offset_t start,
vm_size_t length, vm_size_t align)
{
vm_map_freehint_t *scan;
scan = &map->freehint[0];
while (scan < &map->freehint[VM_MAP_FFCOUNT]) {
if (scan->length == length && scan->align == align) {
scan->start = start;
return;
}
++scan;
}
scan = &map->freehint[map->freehint_newindex & VM_MAP_FFMASK];
scan->start = start;
scan->align = align;
scan->length = length;
++map->freehint_newindex;
}
static
void
vm_map_freehint_hole(vm_map_t map, vm_offset_t start, vm_size_t length)
{
vm_map_freehint_t *scan;
scan = &map->freehint[0];
while (scan < &map->freehint[VM_MAP_FFCOUNT]) {
if (scan->length <= length && scan->start > start)
scan->start = start;
++scan;
}
}
static
void
vm_map_entry_shadow(vm_map_entry_t entry)
{
vm_map_backing_t ba;
vm_size_t length;
vm_object_t source;
vm_object_t result;
length = atop(entry->ba.end - entry->ba.start);
source = entry->ba.object;
KKASSERT(source);
if (source->type != OBJT_VNODE) {
if (source->ref_count == 1 &&
source->handle == NULL &&
(source->type == OBJT_DEFAULT ||
source->type == OBJT_SWAP)) {
goto done;
}
}
ba = kmalloc(sizeof(*ba), M_MAP_BACKING, M_INTWAIT);
vm_object_hold_shared(source);
vm_object_clear_flag(source, OBJ_ONEMAPPING);
result = vm_object_allocate_hold(OBJT_DEFAULT, length);
if (result == NULL)
panic("vm_object_shadow: no object for shadowing");
vm_map_backing_detach(entry, &entry->ba);
*ba = entry->ba;
entry->ba.object = result;
entry->ba.backing_ba = ba;
entry->ba.backing_count = ba->backing_count + 1;
entry->ba.offset = 0;
result->pg_color = vm_quickcolor();
vm_map_backing_attach(entry, &entry->ba);
vm_map_backing_attach(entry, ba);
vm_object_drop(result);
vm_object_drop(source);
done:
entry->eflags &= ~MAP_ENTRY_NEEDS_COPY;
}
void
vm_map_entry_allocate_object(vm_map_entry_t entry)
{
vm_object_t obj;
entry->ba.offset = 0;
obj = vm_object_allocate(OBJT_DEFAULT,
atop(entry->ba.end - entry->ba.start) +
entry->ba.offset);
entry->ba.object = obj;
vm_map_backing_attach(entry, &entry->ba);
}
void
vm_map_entry_reserve_cpu_init(globaldata_t gd)
{
vm_map_entry_t entry;
int count;
int i;
atomic_add_int(&gd->gd_vme_avail, -MAP_RESERVE_COUNT * 2);
if (gd->gd_cpuid == 0) {
entry = &cpu_map_entry_init_bsp[0];
count = MAPENTRYBSP_CACHE;
} else {
entry = &cpu_map_entry_init_ap[gd->gd_cpuid][0];
count = MAPENTRYAP_CACHE;
}
for (i = 0; i < count; ++i, ++entry) {
MAPENT_FREELIST(entry) = gd->gd_vme_base;
gd->gd_vme_base = entry;
}
}
int
vm_map_entry_reserve(int count)
{
struct globaldata *gd = mycpu;
vm_map_entry_t entry;
if (gd->gd_vme_avail < count) {
crit_enter();
while (gd->gd_vme_avail < count) {
entry = zalloc(mapentzone);
MAPENT_FREELIST(entry) = gd->gd_vme_base;
gd->gd_vme_base = entry;
atomic_add_int(&gd->gd_vme_avail, 1);
}
crit_exit();
}
atomic_add_int(&gd->gd_vme_avail, -count);
return(count);
}
void
vm_map_entry_release(int count)
{
struct globaldata *gd = mycpu;
vm_map_entry_t entry;
vm_map_entry_t efree;
count = atomic_fetchadd_int(&gd->gd_vme_avail, count) + count;
if (gd->gd_vme_avail > MAP_RESERVE_SLOP) {
efree = NULL;
crit_enter();
while (gd->gd_vme_avail > MAP_RESERVE_HYST) {
entry = gd->gd_vme_base;
KKASSERT(entry != NULL);
gd->gd_vme_base = MAPENT_FREELIST(entry);
atomic_add_int(&gd->gd_vme_avail, -1);
MAPENT_FREELIST(entry) = efree;
efree = entry;
}
crit_exit();
while ((entry = efree) != NULL) {
efree = MAPENT_FREELIST(efree);
zfree(mapentzone, entry);
}
}
}
int
vm_map_entry_kreserve(int count)
{
struct globaldata *gd = mycpu;
atomic_add_int(&gd->gd_vme_avail, -count);
KASSERT(gd->gd_vme_base != NULL,
("no reserved entries left, gd_vme_avail = %d",
gd->gd_vme_avail));
return(count);
}
void
vm_map_entry_krelease(int count)
{
struct globaldata *gd = mycpu;
atomic_add_int(&gd->gd_vme_avail, count);
}
static vm_map_entry_t
vm_map_entry_create(int *countp)
{
struct globaldata *gd = mycpu;
vm_map_entry_t entry;
KKASSERT(*countp > 0);
--*countp;
crit_enter();
entry = gd->gd_vme_base;
KASSERT(entry != NULL, ("gd_vme_base NULL! count %d", *countp));
gd->gd_vme_base = MAPENT_FREELIST(entry);
crit_exit();
return(entry);
}
static void
vm_map_backing_attach(vm_map_entry_t entry, vm_map_backing_t ba)
{
vm_object_t obj;
switch(entry->maptype) {
case VM_MAPTYPE_VPAGETABLE:
case VM_MAPTYPE_NORMAL:
obj = ba->object;
lockmgr(&obj->backing_lk, LK_EXCLUSIVE);
TAILQ_INSERT_TAIL(&obj->backing_list, ba, entry);
lockmgr(&obj->backing_lk, LK_RELEASE);
break;
case VM_MAPTYPE_UKSMAP:
ba->uksmap(ba, UKSMAPOP_ADD, entry->aux.dev, NULL);
break;
}
}
static void
vm_map_backing_detach(vm_map_entry_t entry, vm_map_backing_t ba)
{
vm_object_t obj;
switch(entry->maptype) {
case VM_MAPTYPE_VPAGETABLE:
case VM_MAPTYPE_NORMAL:
obj = ba->object;
lockmgr(&obj->backing_lk, LK_EXCLUSIVE);
TAILQ_REMOVE(&obj->backing_list, ba, entry);
lockmgr(&obj->backing_lk, LK_RELEASE);
break;
case VM_MAPTYPE_UKSMAP:
ba->uksmap(ba, UKSMAPOP_REM, entry->aux.dev, NULL);
break;
}
}
static void
vm_map_entry_dispose_ba(vm_map_entry_t entry, vm_map_backing_t ba)
{
vm_map_backing_t next;
while (ba) {
if (ba->map_object) {
vm_map_backing_detach(entry, ba);
vm_object_deallocate(ba->object);
}
next = ba->backing_ba;
kfree(ba, M_MAP_BACKING);
ba = next;
}
}
static void
vm_map_entry_dispose(vm_map_t map, vm_map_entry_t entry, int *countp)
{
struct globaldata *gd = mycpu;
switch(entry->maptype) {
case VM_MAPTYPE_NORMAL:
case VM_MAPTYPE_VPAGETABLE:
if (entry->ba.map_object) {
vm_map_backing_detach(entry, &entry->ba);
vm_object_deallocate(entry->ba.object);
}
break;
case VM_MAPTYPE_SUBMAP:
break;
case VM_MAPTYPE_UKSMAP:
vm_map_backing_detach(entry, &entry->ba);
break;
default:
break;
}
vm_map_entry_dispose_ba(entry, entry->ba.backing_ba);
entry->ba.backing_ba = NULL;
entry->ba.object = NULL;
entry->ba.offset = 0;
++*countp;
crit_enter();
MAPENT_FREELIST(entry) = gd->gd_vme_base;
gd->gd_vme_base = entry;
crit_exit();
}
static __inline void
vm_map_entry_link(vm_map_t map, vm_map_entry_t entry)
{
ASSERT_VM_MAP_LOCKED(map);
map->nentries++;
if (vm_map_rb_tree_RB_INSERT(&map->rb_root, entry))
panic("vm_map_entry_link: dup addr map %p ent %p", map, entry);
}
static __inline void
vm_map_entry_unlink(vm_map_t map,
vm_map_entry_t entry)
{
ASSERT_VM_MAP_LOCKED(map);
if (entry->eflags & MAP_ENTRY_IN_TRANSITION) {
panic("vm_map_entry_unlink: attempt to mess with "
"locked entry! %p", entry);
}
vm_map_rb_tree_RB_REMOVE(&map->rb_root, entry);
map->nentries--;
}
boolean_t
vm_map_lookup_entry(vm_map_t map, vm_offset_t address, vm_map_entry_t *entry)
{
vm_map_entry_t tmp;
vm_map_entry_t last;
ASSERT_VM_MAP_LOCKED(map);
last = NULL;
tmp = RB_ROOT(&map->rb_root);
while (tmp) {
if (address >= tmp->ba.start) {
if (address < tmp->ba.end) {
*entry = tmp;
return(TRUE);
}
last = tmp;
tmp = RB_RIGHT(tmp, rb_entry);
} else {
tmp = RB_LEFT(tmp, rb_entry);
}
}
*entry = last;
return (FALSE);
}
int
vm_map_insert(vm_map_t map, int *countp,
void *map_object, void *map_aux,
vm_ooffset_t offset, void *aux_info,
vm_offset_t start, vm_offset_t end,
vm_maptype_t maptype, vm_subsys_t id,
vm_prot_t prot, vm_prot_t max, int cow)
{
vm_map_entry_t new_entry;
vm_map_entry_t prev_entry;
vm_map_entry_t next;
vm_map_entry_t temp_entry;
vm_eflags_t protoeflags;
vm_object_t object;
int must_drop = 0;
if (maptype == VM_MAPTYPE_UKSMAP)
object = NULL;
else
object = map_object;
ASSERT_VM_MAP_LOCKED(map);
if (object)
ASSERT_LWKT_TOKEN_HELD(vm_object_token(object));
if ((start < vm_map_min(map)) || (end > vm_map_max(map)) ||
(start >= end)) {
return (KERN_INVALID_ADDRESS);
}
if (vm_map_lookup_entry(map, start, &temp_entry))
return (KERN_NO_SPACE);
prev_entry = temp_entry;
if (prev_entry)
next = vm_map_rb_tree_RB_NEXT(prev_entry);
else
next = RB_MIN(vm_map_rb_tree, &map->rb_root);
if (next && next->ba.start < end)
return (KERN_NO_SPACE);
protoeflags = 0;
if (cow & COWF_COPY_ON_WRITE)
protoeflags |= MAP_ENTRY_COW|MAP_ENTRY_NEEDS_COPY;
if (cow & COWF_NOFAULT) {
protoeflags |= MAP_ENTRY_NOFAULT;
KASSERT(object == NULL,
("vm_map_insert: paradoxical NOFAULT request"));
}
if (cow & COWF_DISABLE_SYNCER)
protoeflags |= MAP_ENTRY_NOSYNC;
if (cow & COWF_DISABLE_COREDUMP)
protoeflags |= MAP_ENTRY_NOCOREDUMP;
if (cow & COWF_IS_STACK)
protoeflags |= MAP_ENTRY_STACK;
if (cow & COWF_IS_KSTACK)
protoeflags |= MAP_ENTRY_KSTACK;
if (maptype == VM_MAPTYPE_VPAGETABLE)
protoeflags |= MAP_ENTRY_VPAGETABLE_WIRED;
lwkt_gettoken(&map->token);
if (object) {
;
} else if (prev_entry &&
(prev_entry->eflags == protoeflags) &&
(prev_entry->ba.end == start) &&
(prev_entry->wired_count == 0) &&
(prev_entry->id == id) &&
prev_entry->maptype == maptype &&
maptype == VM_MAPTYPE_NORMAL &&
prev_entry->ba.backing_ba == NULL &&
((prev_entry->ba.object == NULL) ||
vm_object_coalesce(prev_entry->ba.object,
OFF_TO_IDX(prev_entry->ba.offset),
(vm_size_t)(prev_entry->ba.end - prev_entry->ba.start),
(vm_size_t)(end - prev_entry->ba.end)))) {
if ((prev_entry->inheritance == VM_INHERIT_DEFAULT) &&
(prev_entry->protection == prot) &&
(prev_entry->max_protection == max)) {
map->size += (end - prev_entry->ba.end);
vm_map_backing_adjust_end(prev_entry, end);
vm_map_simplify_entry(map, prev_entry, countp);
lwkt_reltoken(&map->token);
return (KERN_SUCCESS);
}
object = prev_entry->ba.object;
offset = prev_entry->ba.offset +
(prev_entry->ba.end - prev_entry->ba.start);
if (object) {
vm_object_hold(object);
vm_object_lock_swap();
vm_object_reference_locked(object);
map_object = object;
must_drop = 1;
}
}
new_entry = vm_map_entry_create(countp);
new_entry->ba.pmap = map->pmap;
new_entry->ba.start = start;
new_entry->ba.end = end;
new_entry->id = id;
new_entry->maptype = maptype;
new_entry->eflags = protoeflags;
new_entry->aux.master_pde = 0;
new_entry->aux.map_aux = map_aux;
new_entry->ba.map_object = map_object;
new_entry->ba.backing_ba = NULL;
new_entry->ba.backing_count = 0;
new_entry->ba.offset = offset;
new_entry->ba.aux_info = aux_info;
new_entry->ba.flags = 0;
new_entry->ba.pmap = map->pmap;
if (protoeflags & MAP_ENTRY_VPAGETABLE_WIRED)
new_entry->ba.flags |= VM_MAP_BACK_VPAGETABLE;
new_entry->inheritance = VM_INHERIT_DEFAULT;
new_entry->protection = prot;
new_entry->max_protection = max;
if (protoeflags & MAP_ENTRY_VPAGETABLE_WIRED)
new_entry->wired_count = 1;
else
new_entry->wired_count = 0;
if (protoeflags & MAP_ENTRY_USER_WIRED)
++new_entry->wired_count;
vm_map_backing_replicated(map, new_entry, MAP_BACK_BASEOBJREFD);
vm_map_entry_link(map, new_entry);
map->size += new_entry->ba.end - new_entry->ba.start;
#if 0
vm_map_simplify_entry(map, new_entry, countp);
#endif
if ((cow & (COWF_PREFAULT | COWF_PREFAULT_PARTIAL)) &&
maptype != VM_MAPTYPE_VPAGETABLE &&
maptype != VM_MAPTYPE_UKSMAP)
{
int dorelock = 0;
if (vm_map_relock_enable && (cow & COWF_PREFAULT_RELOCK)) {
dorelock = 1;
vm_object_lock_swap();
vm_object_drop(object);
}
pmap_object_init_pt(map->pmap, new_entry,
new_entry->ba.start,
new_entry->ba.end - new_entry->ba.start,
cow & COWF_PREFAULT_PARTIAL);
if (dorelock) {
vm_object_hold(object);
vm_object_lock_swap();
}
}
lwkt_reltoken(&map->token);
if (must_drop)
vm_object_drop(object);
return (KERN_SUCCESS);
}
int
vm_map_findspace(vm_map_t map, vm_offset_t start, vm_size_t length,
vm_size_t align, int flags, vm_offset_t *addr)
{
vm_map_entry_t entry;
vm_map_entry_t tmp;
vm_offset_t hole_start;
vm_offset_t end;
vm_offset_t align_mask;
if (start < vm_map_min(map))
start = vm_map_min(map);
if (start > vm_map_max(map))
return (1);
if ((align | (align - 1)) + 1 != (align << 1))
align_mask = (vm_offset_t)-1;
else
align_mask = align - 1;
if ((flags & MAP_32BIT) == 0) {
hole_start = vm_map_freehint_find(map, length, align);
if (start < hole_start)
start = hole_start;
}
if (vm_map_lookup_entry(map, start, &tmp))
start = tmp->ba.end;
entry = tmp;
for (;;) {
if (align_mask == (vm_offset_t)-1)
end = roundup(start, align);
else
end = (start + align_mask) & ~align_mask;
if (end < start)
return (1);
start = end;
end = start + length;
if (end > vm_map_max(map) || end < start)
return (1);
if ((flags & MAP_32BIT) && end > 0x100000000L)
return (1);
if (entry)
entry = vm_map_rb_tree_RB_NEXT(entry);
else
entry = RB_MIN(vm_map_rb_tree, &map->rb_root);
if (entry == NULL)
break;
if (entry->ba.start >= end) {
if ((entry->eflags & MAP_ENTRY_STACK) == 0)
break;
if (flags & MAP_TRYFIXED)
break;
if (entry->ba.start - entry->aux.avail_ssize >= end)
break;
}
start = entry->ba.end;
}
vm_map_freehint_update(map, start, length, align);
if (map == kernel_map) {
vm_offset_t kstop;
kstop = round_page(start + length);
if (kstop > kernel_vm_end)
pmap_growkernel(start, kstop);
}
*addr = start;
return (0);
}
int
vm_map_find(vm_map_t map, void *map_object, void *map_aux,
vm_ooffset_t offset, vm_offset_t *addr,
vm_size_t length, vm_size_t align, boolean_t fitit,
vm_maptype_t maptype, vm_subsys_t id,
vm_prot_t prot, vm_prot_t max, int cow)
{
vm_offset_t start;
vm_object_t object;
void *aux_info;
int result;
int count;
int flags;
flags = (cow & COWF_32BIT) ? MAP_32BIT : 0;
aux_info = NULL;
if (maptype == VM_MAPTYPE_UKSMAP) {
KKASSERT(map_aux != NULL && map_object != NULL);
switch(minor(((struct cdev *)map_aux))) {
case 5:
aux_info = curproc;
break;
case 6:
break;
case 7:
aux_info = curthread->td_lwp;
break;
}
object = NULL;
} else {
object = map_object;
}
start = *addr;
count = vm_map_entry_reserve(MAP_RESERVE_COUNT);
vm_map_lock(map);
if (object)
vm_object_hold_shared(object);
if (fitit) {
if (vm_map_findspace(map, start, length, align, flags, addr)) {
if (object)
vm_object_drop(object);
vm_map_unlock(map);
vm_map_entry_release(count);
return (KERN_NO_SPACE);
}
start = *addr;
}
result = vm_map_insert(map, &count,
map_object, map_aux,
offset, aux_info,
start, start + length,
maptype, id, prot, max, cow);
if (object)
vm_object_drop(object);
vm_map_unlock(map);
vm_map_entry_release(count);
return (result);
}
void
vm_map_simplify_entry(vm_map_t map, vm_map_entry_t entry, int *countp)
{
vm_map_entry_t next, prev;
vm_size_t prevsize, esize;
if (entry->eflags & MAP_ENTRY_IN_TRANSITION) {
++mycpu->gd_cnt.v_intrans_coll;
return;
}
if (entry->maptype == VM_MAPTYPE_SUBMAP)
return;
if (entry->maptype == VM_MAPTYPE_UKSMAP)
return;
prev = vm_map_rb_tree_RB_PREV(entry);
if (prev) {
prevsize = prev->ba.end - prev->ba.start;
if ( (prev->ba.end == entry->ba.start) &&
(prev->maptype == entry->maptype) &&
(prev->ba.object == entry->ba.object) &&
(prev->ba.backing_ba == entry->ba.backing_ba) &&
(!prev->ba.object ||
(prev->ba.offset + prevsize == entry->ba.offset)) &&
(prev->eflags == entry->eflags) &&
(prev->protection == entry->protection) &&
(prev->max_protection == entry->max_protection) &&
(prev->inheritance == entry->inheritance) &&
(prev->id == entry->id) &&
(prev->wired_count == entry->wired_count)) {
vm_map_entry_unlink(map, prev);
vm_map_backing_adjust_start(entry, prev->ba.start);
if (entry->ba.object == NULL)
entry->ba.offset = 0;
vm_map_entry_dispose(map, prev, countp);
}
}
next = vm_map_rb_tree_RB_NEXT(entry);
if (next) {
esize = entry->ba.end - entry->ba.start;
if ((entry->ba.end == next->ba.start) &&
(next->maptype == entry->maptype) &&
(next->ba.object == entry->ba.object) &&
(next->ba.backing_ba == entry->ba.backing_ba) &&
(!entry->ba.object ||
(entry->ba.offset + esize == next->ba.offset)) &&
(next->eflags == entry->eflags) &&
(next->protection == entry->protection) &&
(next->max_protection == entry->max_protection) &&
(next->inheritance == entry->inheritance) &&
(next->id == entry->id) &&
(next->wired_count == entry->wired_count)) {
vm_map_entry_unlink(map, next);
vm_map_backing_adjust_end(entry, next->ba.end);
vm_map_entry_dispose(map, next, countp);
}
}
}
#define vm_map_clip_start(map, entry, startaddr, countp) \
{ \
if (startaddr > entry->ba.start) \
_vm_map_clip_start(map, entry, startaddr, countp); \
}
static void
_vm_map_clip_start(vm_map_t map, vm_map_entry_t entry, vm_offset_t start,
int *countp)
{
vm_map_entry_t new_entry;
vm_map_simplify_entry(map, entry, countp);
if (entry->ba.object == NULL && !map->system_map &&
VM_MAP_ENTRY_WITHIN_PARTITION(entry)) {
vm_map_entry_allocate_object(entry);
}
new_entry = vm_map_entry_create(countp);
*new_entry = *entry;
new_entry->ba.end = start;
vm_map_backing_replicated(map, new_entry, MAP_BACK_CLIPPED);
vm_map_backing_adjust_start(entry, start);
vm_map_entry_link(map, new_entry);
}
#define vm_map_clip_end(map, entry, endaddr, countp) \
{ \
if (endaddr < entry->ba.end) \
_vm_map_clip_end(map, entry, endaddr, countp); \
}
static void
_vm_map_clip_end(vm_map_t map, vm_map_entry_t entry, vm_offset_t end,
int *countp)
{
vm_map_entry_t new_entry;
if (entry->ba.object == NULL && !map->system_map &&
VM_MAP_ENTRY_WITHIN_PARTITION(entry)) {
vm_map_entry_allocate_object(entry);
}
new_entry = vm_map_entry_create(countp);
*new_entry = *entry;
new_entry->ba.start = end;
new_entry->ba.offset += (new_entry->ba.start - entry->ba.start);
vm_map_backing_replicated(map, new_entry, MAP_BACK_CLIPPED);
vm_map_backing_adjust_end(entry, end);
vm_map_entry_link(map, new_entry);
}
#define VM_MAP_RANGE_CHECK(map, start, end) \
{ \
if (start < vm_map_min(map)) \
start = vm_map_min(map); \
if (end > vm_map_max(map)) \
end = vm_map_max(map); \
if (start > end) \
start = end; \
}
void
vm_map_transition_wait(vm_map_t map, int relock)
{
tsleep_interlock(map, 0);
vm_map_unlock(map);
tsleep(map, PINTERLOCKED, "vment", 0);
if (relock)
vm_map_lock(map);
}
#define CLIP_CHECK_BACK(entry, save_start) \
do { \
while (entry->ba.start != save_start) { \
entry = vm_map_rb_tree_RB_PREV(entry); \
KASSERT(entry, ("bad entry clip")); \
} \
} while(0)
#define CLIP_CHECK_FWD(entry, save_end) \
do { \
while (entry->ba.end != save_end) { \
entry = vm_map_rb_tree_RB_NEXT(entry); \
KASSERT(entry, ("bad entry clip")); \
} \
} while(0)
static
vm_map_entry_t
vm_map_clip_range(vm_map_t map, vm_offset_t start, vm_offset_t end,
int *countp, int flags)
{
vm_map_entry_t start_entry;
vm_map_entry_t entry;
vm_map_entry_t next;
again:
if (vm_map_lookup_entry(map, start, &start_entry) == FALSE)
return (NULL);
entry = start_entry;
if (entry->eflags & MAP_ENTRY_IN_TRANSITION) {
entry->eflags |= MAP_ENTRY_NEEDS_WAKEUP;
++mycpu->gd_cnt.v_intrans_coll;
++mycpu->gd_cnt.v_intrans_wait;
vm_map_transition_wait(map, 1);
goto again;
}
vm_map_clip_start(map, entry, start, countp);
vm_map_clip_end(map, entry, end, countp);
entry->eflags |= MAP_ENTRY_IN_TRANSITION;
for (;;) {
next = vm_map_rb_tree_RB_NEXT(entry);
if (next == NULL || next->ba.start >= end)
break;
if (flags & MAP_CLIP_NO_HOLES) {
if (next->ba.start > entry->ba.end) {
vm_map_unclip_range(map, start_entry,
start, entry->ba.end, countp, flags);
return(NULL);
}
}
if (next->eflags & MAP_ENTRY_IN_TRANSITION) {
vm_offset_t save_end = entry->ba.end;
next->eflags |= MAP_ENTRY_NEEDS_WAKEUP;
++mycpu->gd_cnt.v_intrans_coll;
++mycpu->gd_cnt.v_intrans_wait;
vm_map_transition_wait(map, 1);
CLIP_CHECK_FWD(entry, save_end);
CLIP_CHECK_BACK(start_entry, start);
continue;
}
vm_map_clip_end(map, next, end, countp);
next->eflags |= MAP_ENTRY_IN_TRANSITION;
entry = next;
}
if (flags & MAP_CLIP_NO_HOLES) {
if (entry->ba.end != end) {
vm_map_unclip_range(map, start_entry,
start, entry->ba.end, countp, flags);
return(NULL);
}
}
return(start_entry);
}
static
void
vm_map_unclip_range(vm_map_t map, vm_map_entry_t start_entry,
vm_offset_t start, vm_offset_t end,
int *countp, int flags)
{
vm_map_entry_t entry;
entry = start_entry;
KASSERT(entry->ba.start == start, ("unclip_range: illegal base entry"));
while (entry && entry->ba.start < end) {
KASSERT(entry->eflags & MAP_ENTRY_IN_TRANSITION,
("in-transition flag not set during unclip on: %p",
entry));
KASSERT(entry->ba.end <= end,
("unclip_range: tail wasn't clipped"));
entry->eflags &= ~MAP_ENTRY_IN_TRANSITION;
if (entry->eflags & MAP_ENTRY_NEEDS_WAKEUP) {
entry->eflags &= ~MAP_ENTRY_NEEDS_WAKEUP;
wakeup(map);
}
entry = vm_map_rb_tree_RB_NEXT(entry);
}
entry = start_entry;
while (entry && entry->ba.start < end) {
vm_map_simplify_entry(map, entry, countp);
entry = vm_map_rb_tree_RB_NEXT(entry);
}
}
int
vm_map_submap(vm_map_t map, vm_offset_t start, vm_offset_t end, vm_map_t submap)
{
vm_map_entry_t entry;
int result = KERN_INVALID_ARGUMENT;
int count;
count = vm_map_entry_reserve(MAP_RESERVE_COUNT);
vm_map_lock(map);
VM_MAP_RANGE_CHECK(map, start, end);
if (vm_map_lookup_entry(map, start, &entry)) {
vm_map_clip_start(map, entry, start, &count);
} else if (entry) {
entry = vm_map_rb_tree_RB_NEXT(entry);
} else {
entry = RB_MIN(vm_map_rb_tree, &map->rb_root);
}
vm_map_clip_end(map, entry, end, &count);
if ((entry->ba.start == start) && (entry->ba.end == end) &&
((entry->eflags & MAP_ENTRY_COW) == 0) &&
(entry->ba.object == NULL)) {
entry->ba.sub_map = submap;
entry->maptype = VM_MAPTYPE_SUBMAP;
result = KERN_SUCCESS;
}
vm_map_unlock(map);
vm_map_entry_release(count);
return (result);
}
int
vm_map_protect(vm_map_t map, vm_offset_t start, vm_offset_t end,
vm_prot_t new_prot, boolean_t set_max)
{
vm_map_entry_t current;
vm_map_entry_t entry;
int count;
count = vm_map_entry_reserve(MAP_RESERVE_COUNT);
vm_map_lock(map);
VM_MAP_RANGE_CHECK(map, start, end);
if (vm_map_lookup_entry(map, start, &entry)) {
vm_map_clip_start(map, entry, start, &count);
} else if (entry) {
entry = vm_map_rb_tree_RB_NEXT(entry);
} else {
entry = RB_MIN(vm_map_rb_tree, &map->rb_root);
}
current = entry;
while (current && current->ba.start < end) {
if (current->maptype == VM_MAPTYPE_SUBMAP) {
vm_map_unlock(map);
vm_map_entry_release(count);
return (KERN_INVALID_ARGUMENT);
}
if ((new_prot & current->max_protection) != new_prot) {
vm_map_unlock(map);
vm_map_entry_release(count);
return (KERN_PROTECTION_FAILURE);
}
if (new_prot & PROT_WRITE &&
(current->eflags & MAP_ENTRY_NEEDS_COPY) == 0 &&
(current->maptype == VM_MAPTYPE_NORMAL ||
current->maptype == VM_MAPTYPE_VPAGETABLE) &&
current->ba.object &&
current->ba.object->type == OBJT_VNODE) {
struct vnode *vp;
vp = current->ba.object->handle;
if (vp && vn_lock(vp, LK_EXCLUSIVE | LK_RETRY | LK_NOWAIT) == 0) {
vfs_timestamp(&vp->v_lastwrite_ts);
vsetflags(vp, VLASTWRITETS);
vn_unlock(vp);
}
}
current = vm_map_rb_tree_RB_NEXT(current);
}
current = entry;
while (current && current->ba.start < end) {
vm_prot_t old_prot;
vm_map_clip_end(map, current, end, &count);
old_prot = current->protection;
if (set_max) {
current->max_protection = new_prot;
current->protection = new_prot & old_prot;
} else {
current->protection = new_prot;
}
if (current->protection != old_prot) {
#define MASK(entry) (((entry)->eflags & MAP_ENTRY_COW) ? ~VM_PROT_WRITE : \
VM_PROT_ALL)
pmap_protect(map->pmap, current->ba.start,
current->ba.end,
current->protection & MASK(current));
#undef MASK
}
vm_map_simplify_entry(map, current, &count);
current = vm_map_rb_tree_RB_NEXT(current);
}
vm_map_unlock(map);
vm_map_entry_release(count);
return (KERN_SUCCESS);
}
int
vm_map_madvise(vm_map_t map, vm_offset_t start, vm_offset_t end,
int behav, off_t value)
{
vm_map_entry_t current, entry;
int modify_map = 0;
int error = 0;
int count;
count = vm_map_entry_reserve(MAP_RESERVE_COUNT);
switch(behav) {
case MADV_NORMAL:
case MADV_SEQUENTIAL:
case MADV_RANDOM:
case MADV_NOSYNC:
case MADV_AUTOSYNC:
case MADV_NOCORE:
case MADV_CORE:
case MADV_SETMAP:
modify_map = 1;
vm_map_lock(map);
break;
case MADV_INVAL:
case MADV_WILLNEED:
case MADV_DONTNEED:
case MADV_FREE:
vm_map_lock_read(map);
break;
default:
vm_map_entry_release(count);
return (EINVAL);
}
VM_MAP_RANGE_CHECK(map, start, end);
if (vm_map_lookup_entry(map, start, &entry)) {
if (modify_map)
vm_map_clip_start(map, entry, start, &count);
} else if (entry) {
entry = vm_map_rb_tree_RB_NEXT(entry);
} else {
entry = RB_MIN(vm_map_rb_tree, &map->rb_root);
}
if (modify_map) {
for (current = entry;
current && current->ba.start < end;
current = vm_map_rb_tree_RB_NEXT(current)) {
if (current->maptype == VM_MAPTYPE_SUBMAP)
continue;
vm_map_clip_end(map, current, end, &count);
switch (behav) {
case MADV_NORMAL:
vm_map_entry_set_behavior(current, MAP_ENTRY_BEHAV_NORMAL);
break;
case MADV_SEQUENTIAL:
vm_map_entry_set_behavior(current, MAP_ENTRY_BEHAV_SEQUENTIAL);
break;
case MADV_RANDOM:
vm_map_entry_set_behavior(current, MAP_ENTRY_BEHAV_RANDOM);
break;
case MADV_NOSYNC:
current->eflags |= MAP_ENTRY_NOSYNC;
break;
case MADV_AUTOSYNC:
current->eflags &= ~MAP_ENTRY_NOSYNC;
break;
case MADV_NOCORE:
current->eflags |= MAP_ENTRY_NOCOREDUMP;
break;
case MADV_CORE:
current->eflags &= ~MAP_ENTRY_NOCOREDUMP;
break;
case MADV_SETMAP:
if (current->maptype != VM_MAPTYPE_VPAGETABLE) {
error = EINVAL;
break;
}
++vpagetable_setmap_count;
if (debug_vpagetable) {
kprintf("MADV_SETMAP: start=%lx end=%lx pde=%lx pid=%d\n",
current->ba.start, current->ba.end, value,
(curproc ? curproc->p_pid : -1));
}
current->aux.master_pde = value;
pmap_remove(map->pmap,
current->ba.start, current->ba.end);
break;
case MADV_INVAL:
++vpagetable_inval_count;
if (debug_vpagetable) {
kprintf("MADV_INVAL: start=%lx end=%lx pid=%d\n",
current->ba.start, current->ba.end,
(curproc ? curproc->p_pid : -1));
}
pmap_remove(map->pmap,
current->ba.start, current->ba.end);
break;
default:
error = EINVAL;
break;
}
vm_map_simplify_entry(map, current, &count);
}
vm_map_unlock(map);
} else {
vm_pindex_t pindex;
vm_pindex_t delta;
for (current = entry;
current && current->ba.start < end;
current = vm_map_rb_tree_RB_NEXT(current)) {
vm_offset_t useStart;
if (current->maptype != VM_MAPTYPE_NORMAL &&
(current->maptype != VM_MAPTYPE_VPAGETABLE ||
behav != MADV_INVAL)) {
continue;
}
pindex = OFF_TO_IDX(current->ba.offset);
delta = atop(current->ba.end - current->ba.start);
useStart = current->ba.start;
if (current->ba.start < start) {
pindex += atop(start - current->ba.start);
delta -= atop(start - current->ba.start);
useStart = start;
}
if (current->ba.end > end)
delta -= atop(current->ba.end - end);
if ((vm_spindex_t)delta <= 0)
continue;
if (behav == MADV_INVAL) {
struct vm_map_ilock ilock;
KASSERT(useStart >= VM_MIN_USER_ADDRESS &&
useStart + ptoa(delta) <=
VM_MAX_USER_ADDRESS,
("Bad range %016jx-%016jx (%016jx)",
useStart, useStart + ptoa(delta),
delta));
vm_map_interlock(map, &ilock,
useStart,
useStart + ptoa(delta));
pmap_remove(map->pmap,
useStart,
useStart + ptoa(delta));
vm_map_deinterlock(map, &ilock);
} else {
vm_object_madvise(current->ba.object,
pindex, delta, behav);
}
if (behav == MADV_WILLNEED) {
pmap_object_init_pt(
map->pmap, current,
useStart,
(delta << PAGE_SHIFT),
COWF_PREFAULT_MADVISE
);
}
}
vm_map_unlock_read(map);
}
vm_map_entry_release(count);
return(error);
}
int
vm_map_inherit(vm_map_t map, vm_offset_t start, vm_offset_t end,
vm_inherit_t new_inheritance)
{
vm_map_entry_t entry;
vm_map_entry_t temp_entry;
int count;
switch (new_inheritance) {
case VM_INHERIT_NONE:
case VM_INHERIT_COPY:
case VM_INHERIT_SHARE:
break;
default:
return (KERN_INVALID_ARGUMENT);
}
count = vm_map_entry_reserve(MAP_RESERVE_COUNT);
vm_map_lock(map);
VM_MAP_RANGE_CHECK(map, start, end);
if (vm_map_lookup_entry(map, start, &temp_entry)) {
entry = temp_entry;
vm_map_clip_start(map, entry, start, &count);
} else if (temp_entry) {
entry = vm_map_rb_tree_RB_NEXT(temp_entry);
} else {
entry = RB_MIN(vm_map_rb_tree, &map->rb_root);
}
while (entry && entry->ba.start < end) {
vm_map_clip_end(map, entry, end, &count);
entry->inheritance = new_inheritance;
vm_map_simplify_entry(map, entry, &count);
entry = vm_map_rb_tree_RB_NEXT(entry);
}
vm_map_unlock(map);
vm_map_entry_release(count);
return (KERN_SUCCESS);
}
int
vm_map_user_wiring(vm_map_t map, vm_offset_t start, vm_offset_t real_end,
boolean_t new_pageable)
{
vm_map_entry_t entry;
vm_map_entry_t start_entry;
vm_offset_t end;
int rv = KERN_SUCCESS;
int count;
count = vm_map_entry_reserve(MAP_RESERVE_COUNT);
vm_map_lock(map);
VM_MAP_RANGE_CHECK(map, start, real_end);
end = real_end;
start_entry = vm_map_clip_range(map, start, end, &count,
MAP_CLIP_NO_HOLES);
if (start_entry == NULL) {
vm_map_unlock(map);
vm_map_entry_release(count);
return (KERN_INVALID_ADDRESS);
}
if (new_pageable == 0) {
entry = start_entry;
while (entry && entry->ba.start < end) {
vm_offset_t save_start;
vm_offset_t save_end;
if (entry->eflags & MAP_ENTRY_USER_WIRED) {
entry = vm_map_rb_tree_RB_NEXT(entry);
continue;
}
if (entry->wired_count != 0) {
entry->wired_count++;
entry->eflags |= MAP_ENTRY_USER_WIRED;
entry = vm_map_rb_tree_RB_NEXT(entry);
continue;
}
if (entry->maptype == VM_MAPTYPE_NORMAL ||
entry->maptype == VM_MAPTYPE_VPAGETABLE) {
int copyflag = entry->eflags &
MAP_ENTRY_NEEDS_COPY;
if (copyflag && ((entry->protection &
VM_PROT_WRITE) != 0)) {
vm_map_entry_shadow(entry);
} else if (entry->ba.object == NULL &&
!map->system_map) {
vm_map_entry_allocate_object(entry);
}
}
entry->wired_count++;
entry->eflags |= MAP_ENTRY_USER_WIRED;
save_start = entry->ba.start;
save_end = entry->ba.end;
rv = vm_fault_wire(map, entry, TRUE, 0);
if (rv) {
CLIP_CHECK_BACK(entry, save_start);
for (;;) {
KASSERT(entry->wired_count >= 1,
("bad wired_count on entry"));
entry->eflags &= ~MAP_ENTRY_USER_WIRED;
--entry->wired_count;
if (entry->ba.end == save_end)
break;
entry = vm_map_rb_tree_RB_NEXT(entry);
KASSERT(entry,
("bad entry clip during backout"));
}
end = save_start;
break;
}
entry = vm_map_rb_tree_RB_NEXT(entry);
}
if (rv)
new_pageable = 1;
CLIP_CHECK_BACK(start_entry, start);
}
if (new_pageable) {
entry = start_entry;
while (entry && entry->ba.start < end) {
if ((entry->eflags & MAP_ENTRY_USER_WIRED) == 0) {
rv = KERN_INVALID_ARGUMENT;
goto done;
}
KASSERT(entry->wired_count != 0,
("wired count was 0 with USER_WIRED set! %p",
entry));
entry = vm_map_rb_tree_RB_NEXT(entry);
}
entry = start_entry;
while (entry && entry->ba.start < end) {
KASSERT(entry->eflags & MAP_ENTRY_USER_WIRED,
("expected USER_WIRED on entry %p", entry));
entry->eflags &= ~MAP_ENTRY_USER_WIRED;
vm_fault_unwire(map, entry);
entry = vm_map_rb_tree_RB_NEXT(entry);
}
}
done:
vm_map_unclip_range(map, start_entry, start, real_end, &count,
MAP_CLIP_NO_HOLES);
vm_map_unlock(map);
vm_map_entry_release(count);
return (rv);
}
int
vm_map_kernel_wiring(vm_map_t map, vm_offset_t start,
vm_offset_t real_end, int kmflags)
{
vm_map_entry_t entry;
vm_map_entry_t start_entry;
vm_offset_t end;
int rv = KERN_SUCCESS;
int count;
if (kmflags & KM_KRESERVE)
count = vm_map_entry_kreserve(MAP_RESERVE_COUNT);
else
count = vm_map_entry_reserve(MAP_RESERVE_COUNT);
vm_map_lock(map);
VM_MAP_RANGE_CHECK(map, start, real_end);
end = real_end;
start_entry = vm_map_clip_range(map, start, end, &count,
MAP_CLIP_NO_HOLES);
if (start_entry == NULL) {
vm_map_unlock(map);
rv = KERN_INVALID_ADDRESS;
goto failure;
}
if ((kmflags & KM_PAGEABLE) == 0) {
entry = start_entry;
while (entry && entry->ba.start < end) {
if (entry->wired_count) {
entry->wired_count++;
entry = vm_map_rb_tree_RB_NEXT(entry);
continue;
}
if (entry->maptype == VM_MAPTYPE_NORMAL ||
entry->maptype == VM_MAPTYPE_VPAGETABLE) {
int copyflag = entry->eflags &
MAP_ENTRY_NEEDS_COPY;
if (copyflag && ((entry->protection &
VM_PROT_WRITE) != 0)) {
vm_map_entry_shadow(entry);
} else if (entry->ba.object == NULL &&
!map->system_map) {
vm_map_entry_allocate_object(entry);
}
}
entry->wired_count++;
entry = vm_map_rb_tree_RB_NEXT(entry);
}
entry = start_entry;
while (entry && entry->ba.start < end) {
vm_offset_t save_start = entry->ba.start;
vm_offset_t save_end = entry->ba.end;
if (entry->wired_count == 1)
rv = vm_fault_wire(map, entry, FALSE, kmflags);
if (rv) {
CLIP_CHECK_BACK(entry, save_start);
for (;;) {
KASSERT(entry->wired_count == 1,
("wired_count changed unexpectedly"));
entry->wired_count = 0;
if (entry->ba.end == save_end)
break;
entry = vm_map_rb_tree_RB_NEXT(entry);
KASSERT(entry,
("bad entry clip during backout"));
}
end = save_start;
break;
}
CLIP_CHECK_FWD(entry, save_end);
entry = vm_map_rb_tree_RB_NEXT(entry);
}
if (rv)
kmflags |= KM_PAGEABLE;
CLIP_CHECK_BACK(start_entry, start);
}
if (kmflags & KM_PAGEABLE) {
entry = start_entry;
while (entry && entry->ba.start < end) {
if (entry->wired_count == 0) {
rv = KERN_INVALID_ARGUMENT;
goto done;
}
entry = vm_map_rb_tree_RB_NEXT(entry);
}
entry = start_entry;
while (entry && entry->ba.start < end) {
vm_fault_unwire(map, entry);
entry = vm_map_rb_tree_RB_NEXT(entry);
}
}
done:
vm_map_unclip_range(map, start_entry, start, real_end,
&count, MAP_CLIP_NO_HOLES);
vm_map_unlock(map);
failure:
if (kmflags & KM_KRESERVE)
vm_map_entry_krelease(count);
else
vm_map_entry_release(count);
return (rv);
}
void
vm_map_set_wired_quick(vm_map_t map, vm_offset_t addr, vm_size_t size,
int *countp)
{
vm_map_entry_t scan;
vm_map_entry_t entry;
entry = vm_map_clip_range(map, addr, addr + size,
countp, MAP_CLIP_NO_HOLES);
scan = entry;
while (scan && scan->ba.start < addr + size) {
KKASSERT(scan->wired_count == 0);
++scan->wired_count;
scan = vm_map_rb_tree_RB_NEXT(scan);
}
vm_map_unclip_range(map, entry, addr, addr + size,
countp, MAP_CLIP_NO_HOLES);
}
int
vm_map_clean(vm_map_t map, vm_offset_t start, vm_offset_t end,
boolean_t syncio, boolean_t invalidate)
{
vm_map_entry_t current;
vm_map_entry_t next;
vm_map_entry_t entry;
vm_map_backing_t ba;
vm_size_t size;
vm_object_t object;
vm_ooffset_t offset;
vm_map_lock_read(map);
VM_MAP_RANGE_CHECK(map, start, end);
if (!vm_map_lookup_entry(map, start, &entry)) {
vm_map_unlock_read(map);
return (KERN_INVALID_ADDRESS);
}
lwkt_gettoken(&map->token);
current = entry;
while (current && current->ba.start < end) {
if (current->maptype == VM_MAPTYPE_SUBMAP) {
lwkt_reltoken(&map->token);
vm_map_unlock_read(map);
return (KERN_INVALID_ARGUMENT);
}
next = vm_map_rb_tree_RB_NEXT(current);
if (end > current->ba.end &&
(next == NULL ||
current->ba.end != next->ba.start)) {
lwkt_reltoken(&map->token);
vm_map_unlock_read(map);
return (KERN_INVALID_ADDRESS);
}
current = next;
}
if (invalidate)
pmap_remove(vm_map_pmap(map), start, end);
current = entry;
while (current && current->ba.start < end) {
offset = current->ba.offset + (start - current->ba.start);
size = (end <= current->ba.end ? end : current->ba.end) - start;
switch(current->maptype) {
case VM_MAPTYPE_SUBMAP:
{
vm_map_t smap;
vm_map_entry_t tentry;
vm_size_t tsize;
smap = current->ba.sub_map;
vm_map_lock_read(smap);
vm_map_lookup_entry(smap, offset, &tentry);
if (tentry == NULL) {
tsize = vm_map_max(smap) - offset;
ba = NULL;
offset = 0 + (offset - vm_map_min(smap));
} else {
tsize = tentry->ba.end - offset;
ba = &tentry->ba;
offset = tentry->ba.offset +
(offset - tentry->ba.start);
}
vm_map_unlock_read(smap);
if (tsize < size)
size = tsize;
break;
}
case VM_MAPTYPE_NORMAL:
case VM_MAPTYPE_VPAGETABLE:
ba = ¤t->ba;
break;
default:
ba = NULL;
break;
}
if (ba) {
object = ba->object;
if (object)
vm_object_hold(object);
} else {
object = NULL;
}
if (ba) {
vm_object_t tobj;
tobj = object;
while (ba->backing_ba != NULL) {
offset -= ba->offset;
ba = ba->backing_ba;
offset += ba->offset;
tobj = ba->object;
if (tobj->size < OFF_TO_IDX(offset + size))
size = IDX_TO_OFF(tobj->size) - offset;
break;
}
if (object != tobj) {
if (object)
vm_object_drop(object);
object = tobj;
vm_object_hold(object);
}
}
if (object && (object->type == OBJT_VNODE) &&
(current->protection & VM_PROT_WRITE) &&
(object->flags & OBJ_NOMSYNC) == 0) {
int flags;
vm_object_reference_locked(object);
vn_lock(object->handle, LK_EXCLUSIVE | LK_RETRY);
flags = (syncio || invalidate) ? OBJPC_SYNC : 0;
flags |= invalidate ? OBJPC_INVAL : 0;
if (current->maptype == VM_MAPTYPE_NORMAL) {
vm_object_page_clean(object,
OFF_TO_IDX(offset),
OFF_TO_IDX(offset + size + PAGE_MASK),
flags);
}
vn_unlock(((struct vnode *)object->handle));
vm_object_deallocate_locked(object);
}
if (object && invalidate &&
((object->type == OBJT_VNODE) ||
(object->type == OBJT_DEVICE) ||
(object->type == OBJT_MGTDEVICE))) {
int clean_only =
((object->type == OBJT_DEVICE) ||
(object->type == OBJT_MGTDEVICE)) ? FALSE : TRUE;
vm_object_reference_locked(object);
if (current->maptype == VM_MAPTYPE_NORMAL) {
vm_object_page_remove(object,
OFF_TO_IDX(offset),
OFF_TO_IDX(offset + size + PAGE_MASK),
clean_only);
}
vm_object_deallocate_locked(object);
}
start += size;
if (object)
vm_object_drop(object);
current = vm_map_rb_tree_RB_NEXT(current);
}
lwkt_reltoken(&map->token);
vm_map_unlock_read(map);
return (KERN_SUCCESS);
}
static void
vm_map_entry_unwire_all(vm_map_t map, vm_map_entry_t entry)
{
if (entry->eflags & MAP_ENTRY_USER_WIRED) {
entry->eflags &= ~MAP_ENTRY_USER_WIRED;
vm_fault_unwire(map, entry);
}
if (entry->eflags & MAP_ENTRY_VPAGETABLE_WIRED) {
entry->eflags &= ~MAP_ENTRY_VPAGETABLE_WIRED;
vm_fault_unwire(map, entry);
}
while (entry->wired_count)
vm_fault_unwire(map, entry);
}
static void
vm_map_entry_delete(vm_map_t map, vm_map_entry_t entry, int *countp)
{
vm_map_entry_unlink(map, entry);
map->size -= entry->ba.end - entry->ba.start;
vm_map_entry_dispose(map, entry, countp);
}
int
vm_map_delete(vm_map_t map, vm_offset_t start, vm_offset_t end, int *countp)
{
vm_object_t object;
vm_map_entry_t entry;
vm_map_entry_t first_entry;
vm_offset_t hole_start;
ASSERT_VM_MAP_LOCKED(map);
lwkt_gettoken(&map->token);
again:
if (vm_map_lookup_entry(map, start, &first_entry)) {
entry = first_entry;
vm_map_clip_start(map, entry, start, countp);
hole_start = start;
} else {
if (first_entry) {
entry = vm_map_rb_tree_RB_NEXT(first_entry);
if (entry == NULL)
hole_start = first_entry->ba.start;
else
hole_start = first_entry->ba.end;
} else {
entry = RB_MIN(vm_map_rb_tree, &map->rb_root);
if (entry == NULL)
hole_start = vm_map_min(map);
else
hole_start = vm_map_max(map);
}
}
while (entry && entry->ba.start < end) {
vm_map_entry_t next;
vm_offset_t s, e;
vm_pindex_t offidxstart, offidxend, count;
if (entry->eflags & MAP_ENTRY_IN_TRANSITION) {
entry->eflags |= MAP_ENTRY_NEEDS_WAKEUP;
start = entry->ba.start;
++mycpu->gd_cnt.v_intrans_coll;
++mycpu->gd_cnt.v_intrans_wait;
vm_map_transition_wait(map, 1);
goto again;
}
vm_map_clip_end(map, entry, end, countp);
s = entry->ba.start;
e = entry->ba.end;
next = vm_map_rb_tree_RB_NEXT(entry);
offidxstart = OFF_TO_IDX(entry->ba.offset);
count = OFF_TO_IDX(e - s);
switch(entry->maptype) {
case VM_MAPTYPE_NORMAL:
case VM_MAPTYPE_VPAGETABLE:
case VM_MAPTYPE_SUBMAP:
object = entry->ba.object;
break;
default:
object = NULL;
break;
}
if (entry->wired_count)
vm_map_entry_unwire_all(map, entry);
offidxend = offidxstart + count;
if (object == kernel_object) {
pmap_remove(map->pmap, s, e);
vm_object_hold(object);
vm_object_page_remove(object, offidxstart,
offidxend, FALSE);
vm_object_drop(object);
} else if (object && object->type != OBJT_DEFAULT &&
object->type != OBJT_SWAP) {
vm_object_hold_shared(object);
pmap_remove(map->pmap, s, e);
vm_object_drop(object);
} else if (object) {
vm_object_hold(object);
pmap_remove(map->pmap, s, e);
if (object != NULL &&
object->ref_count != 1 &&
(object->flags & (OBJ_NOSPLIT|OBJ_ONEMAPPING)) ==
OBJ_ONEMAPPING &&
(object->type == OBJT_DEFAULT ||
object->type == OBJT_SWAP)) {
vm_object_page_remove(object, offidxstart,
offidxend, FALSE);
if (object->type == OBJT_SWAP) {
swap_pager_freespace(object,
offidxstart,
count);
}
if (offidxend >= object->size &&
offidxstart < object->size) {
object->size = offidxstart;
}
}
vm_object_drop(object);
} else if (entry->maptype == VM_MAPTYPE_UKSMAP) {
pmap_remove(map->pmap, s, e);
}
vm_map_entry_delete(map, entry, countp);
entry = next;
}
if (entry == NULL) {
vm_map_freehint_hole(map, hole_start,
vm_map_max(map) - hole_start);
} else {
vm_map_freehint_hole(map, hole_start,
entry->ba.start - hole_start);
}
lwkt_reltoken(&map->token);
return (KERN_SUCCESS);
}
int
vm_map_remove(vm_map_t map, vm_offset_t start, vm_offset_t end)
{
int result;
int count;
count = vm_map_entry_reserve(MAP_RESERVE_COUNT);
vm_map_lock(map);
VM_MAP_RANGE_CHECK(map, start, end);
result = vm_map_delete(map, start, end, &count);
vm_map_unlock(map);
vm_map_entry_release(count);
return (result);
}
boolean_t
vm_map_check_protection(vm_map_t map, vm_offset_t start, vm_offset_t end,
vm_prot_t protection, boolean_t have_lock)
{
vm_map_entry_t entry;
vm_map_entry_t tmp_entry;
boolean_t result;
if (have_lock == FALSE)
vm_map_lock_read(map);
if (!vm_map_lookup_entry(map, start, &tmp_entry)) {
if (have_lock == FALSE)
vm_map_unlock_read(map);
return (FALSE);
}
entry = tmp_entry;
result = TRUE;
while (start < end) {
if (entry == NULL) {
result = FALSE;
break;
}
if (start < entry->ba.start) {
result = FALSE;
break;
}
if ((entry->protection & protection) != protection) {
result = FALSE;
break;
}
start = entry->ba.end;
entry = vm_map_rb_tree_RB_NEXT(entry);
}
if (have_lock == FALSE)
vm_map_unlock_read(map);
return (result);
}
static
void
vm_map_backing_replicated(vm_map_t map, vm_map_entry_t entry, int flags)
{
vm_map_backing_t ba;
vm_map_backing_t nba;
vm_object_t object;
ba = &entry->ba;
for (;;) {
ba->pmap = map->pmap;
if (ba->map_object) {
switch(entry->maptype) {
case VM_MAPTYPE_VPAGETABLE:
case VM_MAPTYPE_NORMAL:
object = ba->object;
if (ba != &entry->ba ||
(flags & MAP_BACK_BASEOBJREFD) == 0) {
vm_object_reference_quick(object);
}
vm_map_backing_attach(entry, ba);
if ((flags & MAP_BACK_CLIPPED) == 0 &&
object->ref_count > 1) {
vm_object_clear_flag(object,
OBJ_ONEMAPPING);
}
break;
case VM_MAPTYPE_UKSMAP:
vm_map_backing_attach(entry, ba);
break;
default:
break;
}
}
if (ba->backing_ba == NULL)
break;
nba = kmalloc(sizeof(*nba), M_MAP_BACKING, M_INTWAIT);
*nba = *ba->backing_ba;
nba->offset += (ba->start - nba->start);
nba->start = ba->start;
nba->end = ba->end;
ba->backing_ba = nba;
ba = nba;
}
}
static
void
vm_map_backing_adjust_start(vm_map_entry_t entry, vm_ooffset_t start)
{
vm_map_backing_t ba;
if (entry->maptype == VM_MAPTYPE_NORMAL ||
entry->maptype == VM_MAPTYPE_VPAGETABLE) {
for (ba = &entry->ba; ba; ba = ba->backing_ba) {
if (ba->object) {
lockmgr(&ba->object->backing_lk, LK_EXCLUSIVE);
ba->offset += (start - ba->start);
ba->start = start;
lockmgr(&ba->object->backing_lk, LK_RELEASE);
} else {
ba->offset += (start - ba->start);
ba->start = start;
}
}
} else {
}
}
static
void
vm_map_backing_adjust_end(vm_map_entry_t entry, vm_ooffset_t end)
{
vm_map_backing_t ba;
if (entry->maptype == VM_MAPTYPE_NORMAL ||
entry->maptype == VM_MAPTYPE_VPAGETABLE) {
for (ba = &entry->ba; ba; ba = ba->backing_ba) {
if (ba->object) {
lockmgr(&ba->object->backing_lk, LK_EXCLUSIVE);
ba->end = end;
lockmgr(&ba->object->backing_lk, LK_RELEASE);
} else {
ba->end = end;
}
}
}
}
static void
vm_map_copy_entry(vm_map_t src_map, vm_map_t dst_map,
vm_map_entry_t src_entry, vm_map_entry_t dst_entry)
{
vm_object_t obj;
KKASSERT(dst_entry->maptype == VM_MAPTYPE_NORMAL ||
dst_entry->maptype == VM_MAPTYPE_VPAGETABLE);
if (src_entry->wired_count) {
if (dst_entry->ba.map_object != NULL) {
vm_map_backing_detach(dst_entry, &dst_entry->ba);
dst_entry->ba.map_object = NULL;
vm_map_entry_dispose_ba(dst_entry,
dst_entry->ba.backing_ba);
dst_entry->ba.backing_ba = NULL;
dst_entry->ba.backing_count = 0;
}
vm_fault_copy_entry(dst_map, src_map, dst_entry, src_entry);
} else {
if ((src_entry->eflags & MAP_ENTRY_NEEDS_COPY) == 0) {
pmap_protect(src_map->pmap,
src_entry->ba.start,
src_entry->ba.end,
src_entry->protection & ~VM_PROT_WRITE);
}
obj = src_entry->ba.object;
if (obj) {
src_entry->eflags |= (MAP_ENTRY_COW |
MAP_ENTRY_NEEDS_COPY);
dst_entry->eflags |= (MAP_ENTRY_COW |
MAP_ENTRY_NEEDS_COPY);
KKASSERT(dst_entry->ba.offset == src_entry->ba.offset);
} else {
dst_entry->ba.offset = 0;
}
pmap_copy(dst_map->pmap, src_map->pmap,
dst_entry->ba.start,
dst_entry->ba.end - dst_entry->ba.start,
src_entry->ba.start);
}
}
static void vmspace_fork_normal_entry(vm_map_t old_map, vm_map_t new_map,
vm_map_entry_t old_entry, int *countp);
static void vmspace_fork_uksmap_entry(struct proc *p2, struct lwp *lp2,
vm_map_t old_map, vm_map_t new_map,
vm_map_entry_t old_entry, int *countp);
struct vmspace *
vmspace_fork(struct vmspace *vm1, struct proc *p2, struct lwp *lp2)
{
struct vmspace *vm2;
vm_map_t old_map = &vm1->vm_map;
vm_map_t new_map;
vm_map_entry_t old_entry;
int count;
lwkt_gettoken(&vm1->vm_map.token);
vm_map_lock(old_map);
vm2 = vmspace_alloc(vm_map_min(old_map), vm_map_max(old_map));
lwkt_gettoken(&vm2->vm_map.token);
bcopy(&vm1->vm_startcopy, &vm2->vm_startcopy,
(caddr_t)&vm1->vm_endcopy - (caddr_t)&vm1->vm_startcopy);
new_map = &vm2->vm_map;
new_map->timestamp = 1;
vm_map_lock(new_map);
count = old_map->nentries;
count = vm_map_entry_reserve(count + MAP_RESERVE_COUNT);
RB_FOREACH(old_entry, vm_map_rb_tree, &old_map->rb_root) {
switch(old_entry->maptype) {
case VM_MAPTYPE_SUBMAP:
panic("vm_map_fork: encountered a submap");
break;
case VM_MAPTYPE_UKSMAP:
vmspace_fork_uksmap_entry(p2, lp2,
old_map, new_map,
old_entry, &count);
break;
case VM_MAPTYPE_NORMAL:
case VM_MAPTYPE_VPAGETABLE:
vmspace_fork_normal_entry(old_map, new_map,
old_entry, &count);
break;
default:
break;
}
}
new_map->size = old_map->size;
vm_map_unlock(new_map);
vm_map_unlock(old_map);
vm_map_entry_release(count);
lwkt_reltoken(&vm2->vm_map.token);
lwkt_reltoken(&vm1->vm_map.token);
return (vm2);
}
static
void
vmspace_fork_normal_entry(vm_map_t old_map, vm_map_t new_map,
vm_map_entry_t old_entry, int *countp)
{
vm_map_entry_t new_entry;
vm_map_backing_t ba;
vm_object_t object;
object = old_entry->ba.object;
if (old_entry->ba.backing_ba &&
(old_entry->ba.backing_count >= vm_map_backing_limit ||
(vm_map_backing_shadow_test && object &&
object->size == object->resident_page_count))) {
if (old_entry->eflags & MAP_ENTRY_NEEDS_COPY)
vm_map_entry_shadow(old_entry);
if (object == NULL)
vm_map_entry_allocate_object(old_entry);
if (vm_fault_collapse(old_map, old_entry) == KERN_SUCCESS) {
ba = old_entry->ba.backing_ba;
old_entry->ba.backing_ba = NULL;
old_entry->ba.backing_count = 0;
vm_map_entry_dispose_ba(old_entry, ba);
}
}
object = NULL;
switch (old_entry->inheritance) {
case VM_INHERIT_NONE:
break;
case VM_INHERIT_SHARE:
if (old_entry->ba.object == NULL)
vm_map_entry_allocate_object(old_entry);
if (old_entry->eflags & MAP_ENTRY_NEEDS_COPY) {
vm_map_entry_shadow(old_entry);
} else if (old_entry->ba.object) {
object = old_entry->ba.object;
}
new_entry = vm_map_entry_create(countp);
*new_entry = *old_entry;
new_entry->eflags &= ~MAP_ENTRY_USER_WIRED;
if (new_entry->eflags & MAP_ENTRY_VPAGETABLE_WIRED)
new_entry->wired_count = 1;
else
new_entry->wired_count = 0;
vm_map_backing_replicated(new_map, new_entry, 0);
vm_map_entry_link(new_map, new_entry);
pmap_copy(new_map->pmap, old_map->pmap,
new_entry->ba.start,
(old_entry->ba.end - old_entry->ba.start),
old_entry->ba.start);
break;
case VM_INHERIT_COPY:
new_entry = vm_map_entry_create(countp);
*new_entry = *old_entry;
new_entry->eflags &= ~MAP_ENTRY_USER_WIRED;
if (new_entry->eflags & MAP_ENTRY_VPAGETABLE_WIRED)
new_entry->wired_count = 1;
else
new_entry->wired_count = 0;
vm_map_backing_replicated(new_map, new_entry, 0);
vm_map_entry_link(new_map, new_entry);
vm_map_copy_entry(old_map, new_map, old_entry, new_entry);
break;
}
}
static
void
vmspace_fork_uksmap_entry(struct proc *p2, struct lwp *lp2,
vm_map_t old_map, vm_map_t new_map,
vm_map_entry_t old_entry, int *countp)
{
vm_map_entry_t new_entry;
if (old_entry->aux.dev) {
switch(minor(old_entry->aux.dev)) {
case 5:
break;
case 6:
break;
case 7:
if (lp2 == NULL)
return;
if (old_entry->ba.aux_info == NULL)
return;
if (((struct lwp *)old_entry->ba.aux_info)->lwp_tid !=
lp2->lwp_tid)
return;
break;
}
}
new_entry = vm_map_entry_create(countp);
*new_entry = *old_entry;
new_entry->eflags &= ~MAP_ENTRY_USER_WIRED;
if (new_entry->eflags & MAP_ENTRY_VPAGETABLE_WIRED)
new_entry->wired_count = 1;
else
new_entry->wired_count = 0;
KKASSERT(new_entry->ba.backing_ba == NULL);
if (new_entry->aux.dev) {
switch(minor(new_entry->aux.dev)) {
case 5:
new_entry->ba.aux_info = p2;
break;
case 6:
new_entry->ba.aux_info = NULL;
break;
case 7:
new_entry->ba.aux_info = lp2;
break;
}
} else {
new_entry->ba.aux_info = NULL;
}
vm_map_backing_replicated(new_map, new_entry, 0);
vm_map_entry_link(new_map, new_entry);
}
int
vm_map_stack (vm_map_t map, vm_offset_t *addrbos, vm_size_t max_ssize,
int flags, vm_prot_t prot, vm_prot_t max, int cow)
{
vm_map_entry_t prev_entry;
vm_map_entry_t next;
vm_size_t init_ssize;
int rv;
int count;
vm_offset_t tmpaddr;
cow |= COWF_IS_STACK;
if (max_ssize < sgrowsiz)
init_ssize = max_ssize;
else
init_ssize = sgrowsiz;
count = vm_map_entry_reserve(MAP_RESERVE_COUNT);
vm_map_lock(map);
if ((flags & (MAP_FIXED | MAP_TRYFIXED)) == 0) {
if (vm_map_findspace(map, *addrbos, max_ssize, 1,
flags, &tmpaddr)) {
vm_map_unlock(map);
vm_map_entry_release(count);
return (KERN_NO_SPACE);
}
*addrbos = tmpaddr;
}
if (vm_map_lookup_entry(map, *addrbos, &prev_entry)) {
vm_map_unlock(map);
vm_map_entry_release(count);
return (KERN_NO_SPACE);
}
#if 0
if (map->size + init_ssize >
curproc->p_rlimit[RLIMIT_VMEM].rlim_cur) {
vm_map_unlock(map);
vm_map_entry_release(count);
return (KERN_NO_SPACE);
}
#endif
if (prev_entry)
next = vm_map_rb_tree_RB_NEXT(prev_entry);
else
next = RB_MIN(vm_map_rb_tree, &map->rb_root);
if (next && next->ba.start < *addrbos + max_ssize) {
vm_map_unlock(map);
vm_map_entry_release(count);
return (KERN_NO_SPACE);
}
rv = vm_map_insert(map, &count,
NULL, NULL,
0, NULL,
*addrbos + max_ssize - init_ssize,
*addrbos + max_ssize,
VM_MAPTYPE_NORMAL,
VM_SUBSYS_STACK, prot, max, cow);
if (rv == KERN_SUCCESS) {
if (prev_entry)
next = vm_map_rb_tree_RB_NEXT(prev_entry);
else
next = RB_MIN(vm_map_rb_tree, &map->rb_root);
if (prev_entry != NULL) {
vm_map_clip_end(map,
prev_entry,
*addrbos + max_ssize - init_ssize,
&count);
}
if (next->ba.end != *addrbos + max_ssize ||
next->ba.start != *addrbos + max_ssize - init_ssize){
panic ("Bad entry start/end for new stack entry");
} else {
next->aux.avail_ssize = max_ssize - init_ssize;
}
}
vm_map_unlock(map);
vm_map_entry_release(count);
return (rv);
}
int
vm_map_growstack (vm_map_t map, vm_offset_t addr)
{
vm_map_entry_t prev_entry;
vm_map_entry_t stack_entry;
vm_map_entry_t next;
struct vmspace *vm;
struct lwp *lp;
struct proc *p;
vm_offset_t end;
int grow_amount;
int rv = KERN_SUCCESS;
int is_procstack;
int use_read_lock = 1;
int count;
lp = curthread->td_lwp;
p = curthread->td_proc;
KKASSERT(lp != NULL);
vm = lp->lwp_vmspace;
if (map != &vm->vm_map) {
return KERN_FAILURE;
}
count = vm_map_entry_reserve(MAP_RESERVE_COUNT);
Retry:
if (use_read_lock)
vm_map_lock_read(map);
else
vm_map_lock(map);
if (vm_map_lookup_entry(map, addr, &prev_entry))
goto done;
if (prev_entry)
stack_entry = vm_map_rb_tree_RB_NEXT(prev_entry);
else
stack_entry = RB_MIN(vm_map_rb_tree, &map->rb_root);
if (stack_entry == NULL)
goto done;
if (prev_entry == NULL)
end = stack_entry->ba.start - stack_entry->aux.avail_ssize;
else
end = prev_entry->ba.end;
if (stack_entry->aux.avail_ssize < 1 ||
addr >= stack_entry->ba.start ||
addr < stack_entry->ba.start - stack_entry->aux.avail_ssize) {
goto done;
}
grow_amount = roundup (stack_entry->ba.start - addr, PAGE_SIZE);
if (grow_amount > stack_entry->aux.avail_ssize) {
rv = KERN_NO_SPACE;
goto done;
}
if (grow_amount > stack_entry->ba.start - end) {
if (use_read_lock && vm_map_lock_upgrade(map)) {
use_read_lock = 0;
goto Retry;
}
use_read_lock = 0;
stack_entry->aux.avail_ssize = stack_entry->ba.start - end;
rv = KERN_NO_SPACE;
goto done;
}
is_procstack = addr >= (vm_offset_t)vm->vm_maxsaddr;
if (is_procstack && (vm->vm_ssize + grow_amount >
p->p_rlimit[RLIMIT_STACK].rlim_cur)) {
rv = KERN_NO_SPACE;
goto done;
}
grow_amount = roundup (grow_amount, sgrowsiz);
if (grow_amount > stack_entry->aux.avail_ssize) {
grow_amount = stack_entry->aux.avail_ssize;
}
if (is_procstack && (vm->vm_ssize + grow_amount >
p->p_rlimit[RLIMIT_STACK].rlim_cur)) {
grow_amount = p->p_rlimit[RLIMIT_STACK].rlim_cur - vm->vm_ssize;
}
if (map->size + grow_amount > p->p_rlimit[RLIMIT_VMEM].rlim_cur) {
rv = KERN_NO_SPACE;
goto done;
}
if (use_read_lock && vm_map_lock_upgrade(map)) {
use_read_lock = 0;
goto Retry;
}
use_read_lock = 0;
addr = stack_entry->ba.start - grow_amount;
if (addr < end) {
stack_entry->aux.avail_ssize = stack_entry->ba.start - end;
addr = end;
}
rv = vm_map_insert(map, &count,
NULL, NULL,
0, NULL,
addr, stack_entry->ba.start,
VM_MAPTYPE_NORMAL,
VM_SUBSYS_STACK, VM_PROT_ALL, VM_PROT_ALL, 0);
if (rv == KERN_SUCCESS) {
if (prev_entry) {
vm_map_clip_end(map, prev_entry, addr, &count);
next = vm_map_rb_tree_RB_NEXT(prev_entry);
} else {
next = RB_MIN(vm_map_rb_tree, &map->rb_root);
}
if (next->ba.end != stack_entry->ba.start ||
next->ba.start != addr) {
panic ("Bad stack grow start/end in new stack entry");
} else {
next->aux.avail_ssize =
stack_entry->aux.avail_ssize -
(next->ba.end - next->ba.start);
if (is_procstack) {
vm->vm_ssize += next->ba.end -
next->ba.start;
}
}
if (map->flags & MAP_WIREFUTURE) {
vm_map_user_wiring(map,
next->ba.start,
next->ba.end,
FALSE);
}
}
done:
if (use_read_lock)
vm_map_unlock_read(map);
else
vm_map_unlock(map);
vm_map_entry_release(count);
return (rv);
}
void
vmspace_exec(struct proc *p, struct vmspace *vmcopy)
{
struct vmspace *oldvmspace = p->p_vmspace;
struct vmspace *newvmspace;
vm_map_t map = &p->p_vmspace->vm_map;
lwkt_gettoken(&oldvmspace->vm_map.token);
if (vmcopy) {
newvmspace = vmspace_fork(vmcopy, NULL, NULL);
lwkt_gettoken(&newvmspace->vm_map.token);
} else {
newvmspace = vmspace_alloc(vm_map_min(map), vm_map_max(map));
lwkt_gettoken(&newvmspace->vm_map.token);
bcopy(&oldvmspace->vm_startcopy, &newvmspace->vm_startcopy,
(caddr_t)&oldvmspace->vm_endcopy -
(caddr_t)&oldvmspace->vm_startcopy);
}
pmap_pinit2(vmspace_pmap(newvmspace));
pmap_replacevm(p, newvmspace, 0);
lwkt_reltoken(&newvmspace->vm_map.token);
lwkt_reltoken(&oldvmspace->vm_map.token);
vmspace_rel(oldvmspace);
}
void
vmspace_unshare(struct proc *p)
{
struct vmspace *oldvmspace = p->p_vmspace;
struct vmspace *newvmspace;
lwkt_gettoken(&oldvmspace->vm_map.token);
if (vmspace_getrefs(oldvmspace) == 1) {
lwkt_reltoken(&oldvmspace->vm_map.token);
return;
}
newvmspace = vmspace_fork(oldvmspace, NULL, NULL);
lwkt_gettoken(&newvmspace->vm_map.token);
pmap_pinit2(vmspace_pmap(newvmspace));
pmap_replacevm(p, newvmspace, 0);
lwkt_reltoken(&newvmspace->vm_map.token);
lwkt_reltoken(&oldvmspace->vm_map.token);
vmspace_rel(oldvmspace);
}
vm_offset_t
vm_map_hint(struct proc *p, vm_offset_t addr, vm_prot_t prot, int flags)
{
struct vmspace *vms = p->p_vmspace;
struct rlimit limit;
rlim_t dsiz;
if (kern_getrlimit(RLIMIT_DATA, &limit))
limit.rlim_cur = maxdsiz;
dsiz = limit.rlim_cur;
if ((flags & MAP_32BIT) && dsiz > PAGE_SIZE) {
dsiz = PAGE_SIZE;
}
if (!randomize_mmap || addr != 0) {
if (addr == 0 ||
(addr >= round_page((vm_offset_t)vms->vm_taddr) &&
addr < round_page((vm_offset_t)vms->vm_daddr + dsiz)))
{
addr = round_page((vm_offset_t)vms->vm_daddr + dsiz);
}
return addr;
}
addr = (vm_offset_t)vms->vm_daddr + dsiz;
if (dsiz)
addr += (karc4random64() & 0x7FFFFFFFFFFFFFFFLU) % dsiz;
return (round_page(addr));
}
int
vm_map_lookup(vm_map_t *var_map,
vm_offset_t vaddr,
vm_prot_t fault_typea,
vm_map_entry_t *out_entry,
struct vm_map_backing **bap,
vm_pindex_t *pindex,
vm_pindex_t *pcount,
vm_prot_t *out_prot,
int *wflags)
{
vm_map_entry_t entry;
vm_map_t map = *var_map;
vm_prot_t prot;
vm_prot_t fault_type = fault_typea;
int use_read_lock = 1;
int rv = KERN_SUCCESS;
int count;
thread_t td = curthread;
count = 0;
if (td->td_nest_count == 0) {
++td->td_nest_count;
count = vm_map_entry_reserve(MAP_RESERVE_COUNT);
--td->td_nest_count;
}
RetryLookup:
if (use_read_lock)
vm_map_lock_read(map);
else
vm_map_lock(map);
cpu_ccfence();
*out_entry = NULL;
*bap = NULL;
{
vm_map_entry_t tmp_entry;
if (!vm_map_lookup_entry(map, vaddr, &tmp_entry)) {
rv = KERN_INVALID_ADDRESS;
goto done;
}
entry = tmp_entry;
*out_entry = entry;
}
if (entry->maptype == VM_MAPTYPE_SUBMAP) {
vm_map_t old_map = map;
*var_map = map = entry->ba.sub_map;
if (use_read_lock)
vm_map_unlock_read(old_map);
else
vm_map_unlock(old_map);
use_read_lock = 1;
goto RetryLookup;
}
if (fault_type & VM_PROT_OVERRIDE_WRITE)
prot = entry->max_protection;
else
prot = entry->protection;
fault_type &= (VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE);
if ((fault_type & prot) != fault_type) {
rv = KERN_PROTECTION_FAILURE;
goto done;
}
if ((entry->eflags & MAP_ENTRY_USER_WIRED) &&
(entry->eflags & MAP_ENTRY_COW) &&
(fault_type & VM_PROT_WRITE) &&
(fault_typea & VM_PROT_OVERRIDE_WRITE) == 0) {
rv = KERN_PROTECTION_FAILURE;
goto done;
}
*wflags = 0;
if (entry->wired_count)
*wflags |= FW_WIRED;
if (curthread->td_lwp && curthread->td_lwp->lwp_vmspace &&
pmap_emulate_ad_bits(&curthread->td_lwp->lwp_vmspace->vm_pmap)) {
if ((prot & VM_PROT_WRITE) == 0)
fault_type |= VM_PROT_WRITE;
}
if (entry->maptype != VM_MAPTYPE_NORMAL &&
entry->maptype != VM_MAPTYPE_VPAGETABLE) {
*bap = NULL;
goto skip;
}
if (entry->eflags & MAP_ENTRY_NEEDS_COPY) {
if (fault_type & VM_PROT_WRITE) {
if (curthread->td_flags & TDF_NOFAULT) {
rv = KERN_FAILURE_NOFAULT;
goto done;
}
if (use_read_lock && vm_map_lock_upgrade(map)) {
use_read_lock = 0;
goto RetryLookup;
}
use_read_lock = 0;
vm_map_entry_shadow(entry);
*wflags |= FW_DIDCOW;
} else {
prot &= ~VM_PROT_WRITE;
}
}
if (entry->ba.object == NULL && !map->system_map) {
if (use_read_lock && vm_map_lock_upgrade(map)) {
use_read_lock = 0;
goto RetryLookup;
}
use_read_lock = 0;
if (map != kernel_map &&
entry->maptype == VM_MAPTYPE_NORMAL &&
((entry->ba.start ^ entry->ba.end) &
~MAP_ENTRY_PARTITION_MASK) &&
vm_map_partition_enable) {
if (entry->eflags & MAP_ENTRY_IN_TRANSITION) {
entry->eflags |= MAP_ENTRY_NEEDS_WAKEUP;
++mycpu->gd_cnt.v_intrans_coll;
++mycpu->gd_cnt.v_intrans_wait;
vm_map_transition_wait(map, 0);
goto RetryLookup;
}
vm_map_entry_partition(map, entry, vaddr, &count);
}
vm_map_entry_allocate_object(entry);
}
*bap = &entry->ba;
skip:
*pindex = OFF_TO_IDX((vaddr - entry->ba.start) + entry->ba.offset);
*pcount = OFF_TO_IDX(entry->ba.end - trunc_page(vaddr));
*out_prot = prot;
done:
if (rv == KERN_SUCCESS) {
if (use_read_lock == 0)
vm_map_lock_downgrade(map);
} else if (use_read_lock) {
vm_map_unlock_read(map);
} else {
vm_map_unlock(map);
}
if (count > 0)
vm_map_entry_release(count);
return (rv);
}
void
vm_map_lookup_done(vm_map_t map, vm_map_entry_t entry, int count)
{
vm_map_unlock_read(map);
if (count)
vm_map_entry_release(count);
}
static void
vm_map_entry_partition(vm_map_t map, vm_map_entry_t entry,
vm_offset_t vaddr, int *countp)
{
vaddr &= ~MAP_ENTRY_PARTITION_MASK;
vm_map_clip_start(map, entry, vaddr, countp);
vaddr += MAP_ENTRY_PARTITION_SIZE;
vm_map_clip_end(map, entry, vaddr, countp);
}
void
vm_map_interlock(vm_map_t map, struct vm_map_ilock *ilock,
vm_offset_t ran_beg, vm_offset_t ran_end)
{
struct vm_map_ilock *scan;
ilock->ran_beg = ran_beg;
ilock->ran_end = ran_end;
ilock->flags = 0;
spin_lock(&map->ilock_spin);
restart:
for (scan = map->ilock_base; scan; scan = scan->next) {
if (ran_end > scan->ran_beg && ran_beg < scan->ran_end) {
scan->flags |= ILOCK_WAITING;
ssleep(scan, &map->ilock_spin, 0, "ilock", 0);
goto restart;
}
}
ilock->next = map->ilock_base;
map->ilock_base = ilock;
spin_unlock(&map->ilock_spin);
}
void
vm_map_deinterlock(vm_map_t map, struct vm_map_ilock *ilock)
{
struct vm_map_ilock *scan;
struct vm_map_ilock **scanp;
spin_lock(&map->ilock_spin);
scanp = &map->ilock_base;
while ((scan = *scanp) != NULL) {
if (scan == ilock) {
*scanp = ilock->next;
spin_unlock(&map->ilock_spin);
if (ilock->flags & ILOCK_WAITING)
wakeup(ilock);
return;
}
scanp = &scan->next;
}
spin_unlock(&map->ilock_spin);
panic("vm_map_deinterlock: missing ilock!");
}
#include "opt_ddb.h"
#ifdef DDB
#include <ddb/ddb.h>
DB_SHOW_COMMAND(map, vm_map_print)
{
static int nlines;
vm_map_t map = (vm_map_t)addr;
boolean_t full = have_addr;
vm_map_entry_t entry;
db_iprintf("Task map %p: pmap=%p, nentries=%d, version=%u\n",
(void *)map,
(void *)map->pmap, map->nentries, map->timestamp);
nlines++;
if (!full && db_indent)
return;
db_indent += 2;
RB_FOREACH(entry, vm_map_rb_tree, &map->rb_root) {
db_iprintf("map entry %p: start=%p, end=%p\n",
(void *)entry,
(void *)entry->ba.start, (void *)entry->ba.end);
nlines++;
{
static char *inheritance_name[4] =
{"share", "copy", "none", "donate_copy"};
db_iprintf(" prot=%x/%x/%s",
entry->protection,
entry->max_protection,
inheritance_name[(int)(unsigned char)
entry->inheritance]);
if (entry->wired_count != 0)
db_printf(", wired");
}
switch(entry->maptype) {
case VM_MAPTYPE_SUBMAP:
db_printf(", share=%p, offset=0x%lx\n",
(void *)entry->ba.sub_map,
(long)entry->ba.offset);
nlines++;
db_indent += 2;
vm_map_print((db_expr_t)(intptr_t)entry->ba.sub_map,
full, 0, NULL);
db_indent -= 2;
break;
case VM_MAPTYPE_NORMAL:
db_printf(", object=%p, offset=0x%lx",
(void *)entry->ba.object,
(long)entry->ba.offset);
if (entry->eflags & MAP_ENTRY_COW)
db_printf(", copy (%s)",
((entry->eflags & MAP_ENTRY_NEEDS_COPY) ?
"needed" : "done"));
db_printf("\n");
nlines++;
if (entry->ba.object) {
db_indent += 2;
vm_object_print((db_expr_t)(intptr_t)
entry->ba.object,
full, 0, NULL);
nlines += 4;
db_indent -= 2;
}
break;
case VM_MAPTYPE_UKSMAP:
db_printf(", uksmap=%p, offset=0x%lx",
(void *)entry->ba.uksmap,
(long)entry->ba.offset);
if (entry->eflags & MAP_ENTRY_COW)
db_printf(", copy (%s)",
(entry->eflags & MAP_ENTRY_NEEDS_COPY) ? "needed" : "done");
db_printf("\n");
nlines++;
break;
default:
break;
}
}
db_indent -= 2;
if (db_indent == 0)
nlines = 0;
}
DB_SHOW_COMMAND(procvm, procvm)
{
struct proc *p;
if (have_addr) {
p = (struct proc *) addr;
} else {
p = curproc;
}
db_printf("p = %p, vmspace = %p, map = %p, pmap = %p\n",
(void *)p, (void *)p->p_vmspace, (void *)&p->p_vmspace->vm_map,
(void *)vmspace_pmap(p->p_vmspace));
vm_map_print((db_expr_t)(intptr_t)&p->p_vmspace->vm_map, 1, 0, NULL);
}
#endif