#include <sys/param.h>
#include <sys/systm.h>
#include <sys/elf.h>
#include <sys/kernel.h>
#include <sys/ktr.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/proc.h>
#include <sys/vmmeter.h>
#include <sys/mman.h>
#include <sys/vnode.h>
#include <sys/racct.h>
#include <sys/resourcevar.h>
#include <sys/rwlock.h>
#include <sys/file.h>
#include <sys/sysctl.h>
#include <sys/sysent.h>
#include <sys/shm.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_pageout.h>
#include <vm/vm_object.h>
#include <vm/vm_pager.h>
#include <vm/vm_radix.h>
#include <vm/vm_kern.h>
#include <vm/vm_extern.h>
#include <vm/vnode_pager.h>
#include <vm/swap_pager.h>
#include <vm/uma.h>
static struct mtx map_sleep_mtx;
static uma_zone_t mapentzone;
static uma_zone_t kmapentzone;
static uma_zone_t vmspace_zone;
static int vmspace_zinit(void *mem, int size, int flags);
static void _vm_map_init(vm_map_t map, pmap_t pmap, vm_offset_t min,
vm_offset_t max);
static void vm_map_entry_deallocate(vm_map_entry_t entry, boolean_t system_map);
static void vm_map_entry_dispose(vm_map_t map, vm_map_entry_t entry);
static void vm_map_entry_unwire(vm_map_t map, vm_map_entry_t entry);
static int vm_map_growstack(vm_map_t map, vm_offset_t addr,
vm_map_entry_t gap_entry);
static void vm_map_pmap_enter(vm_map_t map, vm_offset_t addr, vm_prot_t prot,
vm_object_t object, vm_pindex_t pindex, vm_size_t size, int flags);
#ifdef INVARIANTS
static void vmspace_zdtor(void *mem, int size, void *arg);
#endif
static int vm_map_stack_locked(vm_map_t map, vm_offset_t addrbos,
vm_size_t max_ssize, vm_size_t growsize, vm_prot_t prot, vm_prot_t max,
int cow);
static void vm_map_wire_entry_failure(vm_map_t map, vm_map_entry_t entry,
vm_offset_t failed_addr);
#define CONTAINS_BITS(set, bits) ((~(set) & (bits)) == 0)
#define ENTRY_CHARGED(e) ((e)->cred != NULL || \
((e)->object.vm_object != NULL && (e)->object.vm_object->cred != NULL && \
!((e)->eflags & MAP_ENTRY_NEEDS_COPY)))
#define PROC_VMSPACE_LOCK(p) do { } while (0)
#define PROC_VMSPACE_UNLOCK(p) do { } while (0)
#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; \
}
#ifndef UMA_USE_DMAP
static void *
kmapent_alloc(uma_zone_t zone, vm_size_t bytes, int domain, uint8_t *pflag,
int wait)
{
vm_offset_t addr;
int error, locked;
*pflag = UMA_SLAB_PRIV;
if (!(locked = vm_map_locked(kernel_map)))
vm_map_lock(kernel_map);
addr = vm_map_findspace(kernel_map, vm_map_min(kernel_map), bytes);
if (addr + bytes < addr || addr + bytes > vm_map_max(kernel_map))
panic("%s: kernel map is exhausted", __func__);
error = vm_map_insert(kernel_map, NULL, 0, addr, addr + bytes,
VM_PROT_RW, VM_PROT_RW, MAP_NOFAULT);
if (error != KERN_SUCCESS)
panic("%s: vm_map_insert() failed: %d", __func__, error);
if (!locked)
vm_map_unlock(kernel_map);
error = kmem_back_domain(domain, kernel_object, addr, bytes, M_NOWAIT |
M_USE_RESERVE | (wait & M_ZERO));
if (error == KERN_SUCCESS) {
return ((void *)addr);
} else {
if (!locked)
vm_map_lock(kernel_map);
vm_map_delete(kernel_map, addr, bytes);
if (!locked)
vm_map_unlock(kernel_map);
return (NULL);
}
}
static void
kmapent_free(void *item, vm_size_t size, uint8_t pflag)
{
vm_offset_t addr;
int error __diagused;
if ((pflag & UMA_SLAB_PRIV) == 0)
return;
addr = (vm_offset_t)item;
kmem_unback(kernel_object, addr, size);
error = vm_map_remove(kernel_map, addr, addr + size);
KASSERT(error == KERN_SUCCESS,
("%s: vm_map_remove failed: %d", __func__, error));
}
#define KMAPENT_RESERVE 1
#endif
void
vm_map_startup(void)
{
mtx_init(&map_sleep_mtx, "vm map sleep mutex", NULL, MTX_DEF);
kmapentzone = uma_zcreate("KMAP ENTRY", sizeof(struct vm_map_entry),
NULL, NULL, NULL, NULL, UMA_ALIGN_PTR,
UMA_ZONE_VM | UMA_ZONE_NOBUCKET);
#ifndef UMA_USE_DMAP
uma_zone_reserve(kmapentzone, KMAPENT_RESERVE + 1);
uma_prealloc(kmapentzone, KMAPENT_RESERVE + 1);
uma_zone_set_allocf(kmapentzone, kmapent_alloc);
uma_zone_set_freef(kmapentzone, kmapent_free);
#endif
mapentzone = uma_zcreate("MAP ENTRY", sizeof(struct vm_map_entry),
NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0);
vmspace_zone = uma_zcreate("VMSPACE", sizeof(struct vmspace), NULL,
#ifdef INVARIANTS
vmspace_zdtor,
#else
NULL,
#endif
vmspace_zinit, NULL, UMA_ALIGN_PTR, UMA_ZONE_NOFREE);
}
static int
vmspace_zinit(void *mem, int size, int flags)
{
struct vmspace *vm;
vm_map_t map;
vm = (struct vmspace *)mem;
map = &vm->vm_map;
memset(map, 0, sizeof(*map));
sx_init(&map->lock, "vm map (user)");
PMAP_LOCK_INIT(vmspace_pmap(vm));
return (0);
}
#ifdef INVARIANTS
static void
vmspace_zdtor(void *mem, int size, void *arg)
{
struct vmspace *vm;
vm = (struct vmspace *)mem;
KASSERT(vm->vm_map.nentries == 0,
("vmspace %p nentries == %d on free", vm, vm->vm_map.nentries));
KASSERT(vm->vm_map.size == 0,
("vmspace %p size == %ju on free", vm, (uintmax_t)vm->vm_map.size));
}
#endif
struct vmspace *
vmspace_alloc(vm_offset_t min, vm_offset_t max, pmap_pinit_t pinit)
{
struct vmspace *vm;
vm = uma_zalloc(vmspace_zone, M_WAITOK);
KASSERT(vm->vm_map.pmap == NULL, ("vm_map.pmap must be NULL"));
if (!pinit(vmspace_pmap(vm))) {
uma_zfree(vmspace_zone, vm);
return (NULL);
}
CTR1(KTR_VM, "vmspace_alloc: %p", vm);
_vm_map_init(&vm->vm_map, vmspace_pmap(vm), min, max);
refcount_init(&vm->vm_refcnt, 1);
vm->vm_shm = NULL;
vm->vm_swrss = 0;
vm->vm_tsize = 0;
vm->vm_dsize = 0;
vm->vm_ssize = 0;
vm->vm_taddr = 0;
vm->vm_daddr = 0;
vm->vm_maxsaddr = 0;
return (vm);
}
#ifdef RACCT
static void
vmspace_container_reset(struct proc *p)
{
PROC_LOCK(p);
racct_set(p, RACCT_DATA, 0);
racct_set(p, RACCT_STACK, 0);
racct_set(p, RACCT_RSS, 0);
racct_set(p, RACCT_MEMLOCK, 0);
racct_set(p, RACCT_VMEM, 0);
PROC_UNLOCK(p);
}
#endif
static inline void
vmspace_dofree(struct vmspace *vm)
{
CTR1(KTR_VM, "vmspace_free: %p", vm);
shmexit(vm);
(void)vm_map_remove(&vm->vm_map, vm_map_min(&vm->vm_map),
vm_map_max(&vm->vm_map));
pmap_release(vmspace_pmap(vm));
vm->vm_map.pmap = NULL;
uma_zfree(vmspace_zone, vm);
}
void
vmspace_free(struct vmspace *vm)
{
WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL,
"vmspace_free() called");
if (refcount_release(&vm->vm_refcnt))
vmspace_dofree(vm);
}
void
vmspace_exitfree(struct proc *p)
{
struct vmspace *vm;
PROC_VMSPACE_LOCK(p);
vm = p->p_vmspace;
p->p_vmspace = NULL;
PROC_VMSPACE_UNLOCK(p);
KASSERT(vm == &vmspace0, ("vmspace_exitfree: wrong vmspace"));
vmspace_free(vm);
}
void
vmspace_exit(struct thread *td)
{
struct vmspace *vm;
struct proc *p;
bool released;
p = td->td_proc;
vm = p->p_vmspace;
refcount_acquire(&vmspace0.vm_refcnt);
if (!(released = refcount_release_if_last(&vm->vm_refcnt))) {
if (p->p_vmspace != &vmspace0) {
PROC_VMSPACE_LOCK(p);
p->p_vmspace = &vmspace0;
PROC_VMSPACE_UNLOCK(p);
pmap_activate(td);
}
released = refcount_release(&vm->vm_refcnt);
}
if (released) {
if (p->p_vmspace != vm) {
PROC_VMSPACE_LOCK(p);
p->p_vmspace = vm;
PROC_VMSPACE_UNLOCK(p);
pmap_activate(td);
}
pmap_remove_pages(vmspace_pmap(vm));
PROC_VMSPACE_LOCK(p);
p->p_vmspace = &vmspace0;
PROC_VMSPACE_UNLOCK(p);
pmap_activate(td);
vmspace_dofree(vm);
}
#ifdef RACCT
if (racct_enable)
vmspace_container_reset(p);
#endif
}
struct vmspace *
vmspace_acquire_ref(struct proc *p)
{
struct vmspace *vm;
PROC_VMSPACE_LOCK(p);
vm = p->p_vmspace;
if (vm == NULL || !refcount_acquire_if_not_zero(&vm->vm_refcnt)) {
PROC_VMSPACE_UNLOCK(p);
return (NULL);
}
if (vm != p->p_vmspace) {
PROC_VMSPACE_UNLOCK(p);
vmspace_free(vm);
return (NULL);
}
PROC_VMSPACE_UNLOCK(p);
return (vm);
}
void
vmspace_switch_aio(struct vmspace *newvm)
{
struct vmspace *oldvm;
KASSERT(refcount_load(&newvm->vm_refcnt) > 0,
("vmspace_switch_aio: newvm unreferenced"));
oldvm = curproc->p_vmspace;
if (oldvm == newvm)
return;
curproc->p_vmspace = newvm;
refcount_acquire(&newvm->vm_refcnt);
pmap_activate(curthread);
vmspace_free(oldvm);
}
void
_vm_map_lock(vm_map_t map, const char *file, int line)
{
if (vm_map_is_system(map))
mtx_lock_flags_(&map->system_mtx, 0, file, line);
else
sx_xlock_(&map->lock, file, line);
map->timestamp++;
}
void
vm_map_entry_set_vnode_text(vm_map_entry_t entry, bool add)
{
vm_object_t object;
struct vnode *vp;
bool vp_held;
if ((entry->eflags & MAP_ENTRY_VN_EXEC) == 0)
return;
KASSERT((entry->eflags & MAP_ENTRY_IS_SUB_MAP) == 0,
("Submap with execs"));
object = entry->object.vm_object;
KASSERT(object != NULL, ("No object for text, entry %p", entry));
if ((object->flags & OBJ_ANON) != 0)
object = object->handle;
else
KASSERT(object->backing_object == NULL,
("non-anon object %p shadows", object));
KASSERT(object != NULL, ("No content object for text, entry %p obj %p",
entry, entry->object.vm_object));
vm_pager_getvp(object, &vp, &vp_held);
if (vp != NULL) {
if (add) {
VOP_SET_TEXT_CHECKED(vp);
} else {
vn_lock(vp, LK_SHARED | LK_RETRY);
VOP_UNSET_TEXT_CHECKED(vp);
VOP_UNLOCK(vp);
}
if (vp_held)
vdrop(vp);
}
}
#define defer_next right
static void
vm_map_process_deferred(void)
{
struct thread *td;
vm_map_entry_t entry, next;
vm_object_t object;
td = curthread;
entry = td->td_map_def_user;
td->td_map_def_user = NULL;
while (entry != NULL) {
next = entry->defer_next;
MPASS((entry->eflags & (MAP_ENTRY_WRITECNT |
MAP_ENTRY_VN_EXEC)) != (MAP_ENTRY_WRITECNT |
MAP_ENTRY_VN_EXEC));
if ((entry->eflags & MAP_ENTRY_WRITECNT) != 0) {
KASSERT((entry->eflags & MAP_ENTRY_IS_SUB_MAP) == 0,
("Submap with writecount"));
object = entry->object.vm_object;
KASSERT(object != NULL, ("No object for writecount"));
vm_pager_release_writecount(object, entry->start,
entry->end);
}
vm_map_entry_set_vnode_text(entry, false);
vm_map_entry_deallocate(entry, FALSE);
entry = next;
}
}
#ifdef INVARIANTS
static void
_vm_map_assert_locked(vm_map_t map, const char *file, int line)
{
if (vm_map_is_system(map))
mtx_assert_(&map->system_mtx, MA_OWNED, file, line);
else
sx_assert_(&map->lock, SA_XLOCKED, file, line);
}
#define VM_MAP_ASSERT_LOCKED(map) \
_vm_map_assert_locked(map, LOCK_FILE, LOCK_LINE)
enum { VMMAP_CHECK_NONE, VMMAP_CHECK_UNLOCK, VMMAP_CHECK_ALL };
#ifdef DIAGNOSTIC
static int enable_vmmap_check = VMMAP_CHECK_UNLOCK;
#else
static int enable_vmmap_check = VMMAP_CHECK_NONE;
#endif
SYSCTL_INT(_debug, OID_AUTO, vmmap_check, CTLFLAG_RWTUN,
&enable_vmmap_check, 0, "Enable vm map consistency checking");
static void _vm_map_assert_consistent(vm_map_t map, int check);
#define VM_MAP_ASSERT_CONSISTENT(map) \
_vm_map_assert_consistent(map, VMMAP_CHECK_ALL)
#ifdef DIAGNOSTIC
#define VM_MAP_UNLOCK_CONSISTENT(map) do { \
if (map->nupdates > map->nentries) { \
_vm_map_assert_consistent(map, VMMAP_CHECK_UNLOCK); \
map->nupdates = 0; \
} \
} while (0)
#else
#define VM_MAP_UNLOCK_CONSISTENT(map)
#endif
#else
#define VM_MAP_ASSERT_LOCKED(map)
#define VM_MAP_ASSERT_CONSISTENT(map)
#define VM_MAP_UNLOCK_CONSISTENT(map)
#endif
void
_vm_map_unlock(vm_map_t map, const char *file, int line)
{
VM_MAP_UNLOCK_CONSISTENT(map);
if (vm_map_is_system(map)) {
#ifndef UMA_USE_DMAP
if (map == kernel_map && (map->flags & MAP_REPLENISH) != 0) {
uma_prealloc(kmapentzone, 1);
map->flags &= ~MAP_REPLENISH;
}
#endif
mtx_unlock_flags_(&map->system_mtx, 0, file, line);
} else {
sx_xunlock_(&map->lock, file, line);
vm_map_process_deferred();
}
}
void
_vm_map_lock_read(vm_map_t map, const char *file, int line)
{
if (vm_map_is_system(map))
mtx_lock_flags_(&map->system_mtx, 0, file, line);
else
sx_slock_(&map->lock, file, line);
}
void
_vm_map_unlock_read(vm_map_t map, const char *file, int line)
{
if (vm_map_is_system(map)) {
KASSERT((map->flags & MAP_REPLENISH) == 0,
("%s: MAP_REPLENISH leaked", __func__));
mtx_unlock_flags_(&map->system_mtx, 0, file, line);
} else {
sx_sunlock_(&map->lock, file, line);
vm_map_process_deferred();
}
}
int
_vm_map_trylock(vm_map_t map, const char *file, int line)
{
int error;
error = vm_map_is_system(map) ?
!mtx_trylock_flags_(&map->system_mtx, 0, file, line) :
!sx_try_xlock_(&map->lock, file, line);
if (error == 0)
map->timestamp++;
return (error == 0);
}
int
_vm_map_trylock_read(vm_map_t map, const char *file, int line)
{
int error;
error = vm_map_is_system(map) ?
!mtx_trylock_flags_(&map->system_mtx, 0, file, line) :
!sx_try_slock_(&map->lock, file, line);
return (error == 0);
}
int
_vm_map_lock_upgrade(vm_map_t map, const char *file, int line)
{
unsigned int last_timestamp;
if (vm_map_is_system(map)) {
mtx_assert_(&map->system_mtx, MA_OWNED, file, line);
} else {
if (!sx_try_upgrade_(&map->lock, file, line)) {
last_timestamp = map->timestamp;
sx_sunlock_(&map->lock, file, line);
vm_map_process_deferred();
sx_xlock_(&map->lock, file, line);
if (last_timestamp != map->timestamp) {
sx_xunlock_(&map->lock, file, line);
return (1);
}
}
}
map->timestamp++;
return (0);
}
void
_vm_map_lock_downgrade(vm_map_t map, const char *file, int line)
{
if (vm_map_is_system(map)) {
KASSERT((map->flags & MAP_REPLENISH) == 0,
("%s: MAP_REPLENISH leaked", __func__));
mtx_assert_(&map->system_mtx, MA_OWNED, file, line);
} else {
VM_MAP_UNLOCK_CONSISTENT(map);
sx_downgrade_(&map->lock, file, line);
}
}
int
vm_map_locked(vm_map_t map)
{
if (vm_map_is_system(map))
return (mtx_owned(&map->system_mtx));
return (sx_xlocked(&map->lock));
}
int
_vm_map_unlock_and_wait(vm_map_t map, int timo, const char *file, int line)
{
VM_MAP_UNLOCK_CONSISTENT(map);
mtx_lock(&map_sleep_mtx);
if (vm_map_is_system(map)) {
KASSERT((map->flags & MAP_REPLENISH) == 0,
("%s: MAP_REPLENISH leaked", __func__));
mtx_unlock_flags_(&map->system_mtx, 0, file, line);
} else {
sx_xunlock_(&map->lock, file, line);
}
return (msleep(&map->root, &map_sleep_mtx, PDROP | PVM, "vmmaps",
timo));
}
void
vm_map_wakeup(vm_map_t map)
{
mtx_lock(&map_sleep_mtx);
mtx_unlock(&map_sleep_mtx);
wakeup(&map->root);
}
void
vm_map_busy(vm_map_t map)
{
VM_MAP_ASSERT_LOCKED(map);
map->busy++;
}
void
vm_map_unbusy(vm_map_t map)
{
VM_MAP_ASSERT_LOCKED(map);
KASSERT(map->busy, ("vm_map_unbusy: not busy"));
if (--map->busy == 0 && (map->flags & MAP_BUSY_WAKEUP)) {
vm_map_modflags(map, 0, MAP_BUSY_WAKEUP);
wakeup(&map->busy);
}
}
void
vm_map_wait_busy(vm_map_t map)
{
VM_MAP_ASSERT_LOCKED(map);
while (map->busy) {
vm_map_modflags(map, MAP_BUSY_WAKEUP, 0);
if (vm_map_is_system(map))
msleep(&map->busy, &map->system_mtx, 0, "mbusy", 0);
else
sx_sleep(&map->busy, &map->lock, 0, "mbusy", 0);
}
map->timestamp++;
}
long
vmspace_resident_count(struct vmspace *vmspace)
{
return pmap_resident_count(vmspace_pmap(vmspace));
}
static void
_vm_map_init(vm_map_t map, pmap_t pmap, vm_offset_t min, vm_offset_t max)
{
map->header.eflags = MAP_ENTRY_HEADER;
map->pmap = pmap;
map->header.end = min;
map->header.start = max;
map->flags = 0;
map->header.left = map->header.right = &map->header;
map->root = NULL;
map->timestamp = 0;
map->busy = 0;
map->anon_loc = 0;
#ifdef DIAGNOSTIC
map->nupdates = 0;
#endif
}
void
vm_map_init(vm_map_t map, pmap_t pmap, vm_offset_t min, vm_offset_t max)
{
_vm_map_init(map, pmap, min, max);
sx_init(&map->lock, "vm map (user)");
}
void
vm_map_init_system(vm_map_t map, pmap_t pmap, vm_offset_t min, vm_offset_t max)
{
_vm_map_init(map, pmap, min, max);
vm_map_modflags(map, MAP_SYSTEM_MAP, 0);
mtx_init(&map->system_mtx, "vm map (system)", NULL, MTX_DEF |
MTX_DUPOK);
}
static void
vm_map_entry_dispose(vm_map_t map, vm_map_entry_t entry)
{
uma_zfree(vm_map_is_system(map) ? kmapentzone : mapentzone, entry);
}
static vm_map_entry_t
vm_map_entry_create(vm_map_t map)
{
vm_map_entry_t new_entry;
#ifndef UMA_USE_DMAP
if (map == kernel_map) {
VM_MAP_ASSERT_LOCKED(map);
new_entry = uma_zalloc(kmapentzone, M_NOWAIT | M_NOVM);
if (new_entry == NULL) {
new_entry = uma_zalloc(kmapentzone,
M_NOWAIT | M_NOVM | M_USE_RESERVE);
kernel_map->flags |= MAP_REPLENISH;
}
} else
#endif
if (vm_map_is_system(map)) {
new_entry = uma_zalloc(kmapentzone, M_NOWAIT);
} else {
new_entry = uma_zalloc(mapentzone, M_WAITOK);
}
KASSERT(new_entry != NULL,
("vm_map_entry_create: kernel resources exhausted"));
return (new_entry);
}
static inline void
vm_map_entry_set_behavior(vm_map_entry_t entry, u_char behavior)
{
entry->eflags = (entry->eflags & ~MAP_ENTRY_BEHAV_MASK) |
(behavior & MAP_ENTRY_BEHAV_MASK);
}
static inline vm_size_t
vm_map_entry_max_free_left(vm_map_entry_t root, vm_map_entry_t left_ancestor)
{
return (root->left != left_ancestor ?
root->left->max_free : root->start - left_ancestor->end);
}
static inline vm_size_t
vm_map_entry_max_free_right(vm_map_entry_t root, vm_map_entry_t right_ancestor)
{
return (root->right != right_ancestor ?
root->right->max_free : right_ancestor->start - root->end);
}
static inline vm_map_entry_t
vm_map_entry_pred(vm_map_entry_t entry)
{
vm_map_entry_t prior;
prior = entry->left;
if (prior->right->start < entry->start) {
do
prior = prior->right;
while (prior->right != entry);
}
return (prior);
}
static inline vm_size_t
vm_size_max(vm_size_t a, vm_size_t b)
{
return (a > b ? a : b);
}
#define SPLAY_LEFT_STEP(root, y, llist, rlist, test) do { \
vm_map_entry_t z; \
vm_size_t max_free; \
\
\
y = root->left; \
max_free = root->max_free; \
KASSERT(max_free == vm_size_max( \
vm_map_entry_max_free_left(root, llist), \
vm_map_entry_max_free_right(root, rlist)), \
("%s: max_free invariant fails", __func__)); \
if (max_free - 1 < vm_map_entry_max_free_left(root, llist)) \
max_free = vm_map_entry_max_free_right(root, rlist); \
if (y != llist && (test)) { \
\
z = y->right; \
if (z != root) { \
root->left = z; \
y->right = root; \
if (max_free < y->max_free) \
root->max_free = max_free = \
vm_size_max(max_free, z->max_free); \
} else if (max_free < y->max_free) \
root->max_free = max_free = \
vm_size_max(max_free, root->start - y->end);\
root = y; \
y = root->left; \
} \
\
root->max_free = max_free; \
KASSERT(max_free == vm_map_entry_max_free_right(root, rlist), \
("%s: max_free not copied from right", __func__)); \
root->left = rlist; \
rlist = root; \
root = y != llist ? y : NULL; \
} while (0)
#define SPLAY_RIGHT_STEP(root, y, llist, rlist, test) do { \
vm_map_entry_t z; \
vm_size_t max_free; \
\
\
y = root->right; \
max_free = root->max_free; \
KASSERT(max_free == vm_size_max( \
vm_map_entry_max_free_left(root, llist), \
vm_map_entry_max_free_right(root, rlist)), \
("%s: max_free invariant fails", __func__)); \
if (max_free - 1 < vm_map_entry_max_free_right(root, rlist)) \
max_free = vm_map_entry_max_free_left(root, llist); \
if (y != rlist && (test)) { \
\
z = y->left; \
if (z != root) { \
root->right = z; \
y->left = root; \
if (max_free < y->max_free) \
root->max_free = max_free = \
vm_size_max(max_free, z->max_free); \
} else if (max_free < y->max_free) \
root->max_free = max_free = \
vm_size_max(max_free, y->start - root->end);\
root = y; \
y = root->right; \
} \
\
root->max_free = max_free; \
KASSERT(max_free == vm_map_entry_max_free_left(root, llist), \
("%s: max_free not copied from left", __func__)); \
root->right = llist; \
llist = root; \
root = y != rlist ? y : NULL; \
} while (0)
static __always_inline vm_map_entry_t
vm_map_splay_split(vm_map_t map, vm_offset_t addr, vm_size_t length,
vm_map_entry_t *llist, vm_map_entry_t *rlist)
{
vm_map_entry_t left, right, root, y;
left = right = &map->header;
root = map->root;
while (root != NULL && root->max_free >= length) {
KASSERT(left->end <= root->start &&
root->end <= right->start,
("%s: root not within tree bounds", __func__));
if (addr < root->start) {
SPLAY_LEFT_STEP(root, y, left, right,
y->max_free >= length && addr < y->start);
} else if (addr >= root->end) {
SPLAY_RIGHT_STEP(root, y, left, right,
y->max_free >= length && addr >= y->end);
} else
break;
}
*llist = left;
*rlist = right;
return (root);
}
static __always_inline void
vm_map_splay_findnext(vm_map_entry_t root, vm_map_entry_t *rlist)
{
vm_map_entry_t hi, right, y;
right = *rlist;
hi = root->right == right ? NULL : root->right;
if (hi == NULL)
return;
do
SPLAY_LEFT_STEP(hi, y, root, right, true);
while (hi != NULL);
*rlist = right;
}
static __always_inline void
vm_map_splay_findprev(vm_map_entry_t root, vm_map_entry_t *llist)
{
vm_map_entry_t left, lo, y;
left = *llist;
lo = root->left == left ? NULL : root->left;
if (lo == NULL)
return;
do
SPLAY_RIGHT_STEP(lo, y, left, root, true);
while (lo != NULL);
*llist = left;
}
static inline void
vm_map_entry_swap(vm_map_entry_t *a, vm_map_entry_t *b)
{
vm_map_entry_t tmp;
tmp = *b;
*b = *a;
*a = tmp;
}
static vm_size_t
vm_map_splay_merge_left_walk(vm_map_entry_t header, vm_map_entry_t root,
vm_map_entry_t tail, vm_size_t max_free, vm_map_entry_t llist)
{
do {
llist->max_free = max_free =
vm_size_max(llist->max_free, max_free);
vm_map_entry_swap(&llist->right, &tail);
vm_map_entry_swap(&tail, &llist);
} while (llist != header);
root->left = tail;
return (max_free);
}
static inline vm_size_t
vm_map_splay_merge_pred(vm_map_entry_t header, vm_map_entry_t root,
vm_map_entry_t llist)
{
vm_size_t max_free;
max_free = root->start - llist->end;
if (llist != header) {
max_free = vm_map_splay_merge_left_walk(header, root,
root, max_free, llist);
} else {
root->left = header;
header->right = root;
}
return (max_free);
}
static inline vm_size_t
vm_map_splay_merge_left(vm_map_entry_t header, vm_map_entry_t root,
vm_map_entry_t llist)
{
vm_size_t max_free;
max_free = vm_map_entry_max_free_left(root, llist);
if (llist != header) {
max_free = vm_map_splay_merge_left_walk(header, root,
root->left == llist ? root : root->left,
max_free, llist);
}
return (max_free);
}
static vm_size_t
vm_map_splay_merge_right_walk(vm_map_entry_t header, vm_map_entry_t root,
vm_map_entry_t tail, vm_size_t max_free, vm_map_entry_t rlist)
{
do {
rlist->max_free = max_free =
vm_size_max(rlist->max_free, max_free);
vm_map_entry_swap(&rlist->left, &tail);
vm_map_entry_swap(&tail, &rlist);
} while (rlist != header);
root->right = tail;
return (max_free);
}
static inline vm_size_t
vm_map_splay_merge_succ(vm_map_entry_t header, vm_map_entry_t root,
vm_map_entry_t rlist)
{
vm_size_t max_free;
max_free = rlist->start - root->end;
if (rlist != header) {
max_free = vm_map_splay_merge_right_walk(header, root,
root, max_free, rlist);
} else {
root->right = header;
header->left = root;
}
return (max_free);
}
static inline vm_size_t
vm_map_splay_merge_right(vm_map_entry_t header, vm_map_entry_t root,
vm_map_entry_t rlist)
{
vm_size_t max_free;
max_free = vm_map_entry_max_free_right(root, rlist);
if (rlist != header) {
max_free = vm_map_splay_merge_right_walk(header, root,
root->right == rlist ? root : root->right,
max_free, rlist);
}
return (max_free);
}
static vm_map_entry_t
vm_map_splay(vm_map_t map, vm_offset_t addr)
{
vm_map_entry_t header, llist, rlist, root;
vm_size_t max_free_left, max_free_right;
header = &map->header;
root = vm_map_splay_split(map, addr, 0, &llist, &rlist);
if (root != NULL) {
max_free_left = vm_map_splay_merge_left(header, root, llist);
max_free_right = vm_map_splay_merge_right(header, root, rlist);
} else if (llist != header) {
root = llist;
llist = root->right;
max_free_left = vm_map_splay_merge_left(header, root, llist);
max_free_right = vm_map_splay_merge_succ(header, root, rlist);
} else if (rlist != header) {
root = rlist;
rlist = root->left;
max_free_left = vm_map_splay_merge_pred(header, root, llist);
max_free_right = vm_map_splay_merge_right(header, root, rlist);
} else {
return (NULL);
}
root->max_free = vm_size_max(max_free_left, max_free_right);
map->root = root;
VM_MAP_ASSERT_CONSISTENT(map);
return (root);
}
static void
vm_map_entry_link(vm_map_t map, vm_map_entry_t entry)
{
vm_map_entry_t header, llist, rlist, root;
vm_size_t max_free_left, max_free_right;
CTR3(KTR_VM,
"vm_map_entry_link: map %p, nentries %d, entry %p", map,
map->nentries, entry);
VM_MAP_ASSERT_LOCKED(map);
map->nentries++;
header = &map->header;
root = vm_map_splay_split(map, entry->start, 0, &llist, &rlist);
if (root == NULL) {
max_free_left = vm_map_splay_merge_pred(header, entry, llist);
max_free_right = vm_map_splay_merge_succ(header, entry, rlist);
} else if (entry->start == root->start) {
KASSERT(entry->end < root->end,
("%s: clip_start not within entry", __func__));
vm_map_splay_findprev(root, &llist);
if ((root->eflags & MAP_ENTRY_STACK_GAP) == 0)
root->offset += entry->end - root->start;
root->start = entry->end;
max_free_left = vm_map_splay_merge_pred(header, entry, llist);
max_free_right = root->max_free = vm_size_max(
vm_map_splay_merge_pred(entry, root, entry),
vm_map_splay_merge_right(header, root, rlist));
} else {
KASSERT(entry->end == root->end,
("%s: clip_start not within entry", __func__));
vm_map_splay_findnext(root, &rlist);
if ((entry->eflags & MAP_ENTRY_STACK_GAP) == 0)
entry->offset += entry->start - root->start;
root->end = entry->start;
max_free_left = root->max_free = vm_size_max(
vm_map_splay_merge_left(header, root, llist),
vm_map_splay_merge_succ(entry, root, entry));
max_free_right = vm_map_splay_merge_succ(header, entry, rlist);
}
entry->max_free = vm_size_max(max_free_left, max_free_right);
map->root = entry;
VM_MAP_ASSERT_CONSISTENT(map);
}
enum unlink_merge_type {
UNLINK_MERGE_NONE,
UNLINK_MERGE_NEXT
};
static void
vm_map_entry_unlink(vm_map_t map, vm_map_entry_t entry,
enum unlink_merge_type op)
{
vm_map_entry_t header, llist, rlist, root;
vm_size_t max_free_left, max_free_right;
VM_MAP_ASSERT_LOCKED(map);
header = &map->header;
root = vm_map_splay_split(map, entry->start, 0, &llist, &rlist);
KASSERT(root != NULL,
("vm_map_entry_unlink: unlink object not mapped"));
vm_map_splay_findprev(root, &llist);
vm_map_splay_findnext(root, &rlist);
if (op == UNLINK_MERGE_NEXT) {
rlist->start = root->start;
MPASS((rlist->eflags & MAP_ENTRY_STACK_GAP) == 0);
rlist->offset = root->offset;
}
if (llist != header) {
root = llist;
llist = root->right;
max_free_left = vm_map_splay_merge_left(header, root, llist);
max_free_right = vm_map_splay_merge_succ(header, root, rlist);
} else if (rlist != header) {
root = rlist;
rlist = root->left;
max_free_left = vm_map_splay_merge_pred(header, root, llist);
max_free_right = vm_map_splay_merge_right(header, root, rlist);
} else {
header->left = header->right = header;
root = NULL;
}
if (root != NULL)
root->max_free = vm_size_max(max_free_left, max_free_right);
map->root = root;
VM_MAP_ASSERT_CONSISTENT(map);
map->nentries--;
CTR3(KTR_VM, "vm_map_entry_unlink: map %p, nentries %d, entry %p", map,
map->nentries, entry);
}
static void
vm_map_entry_resize(vm_map_t map, vm_map_entry_t entry, vm_size_t grow_amount)
{
vm_map_entry_t header, llist, rlist, root;
VM_MAP_ASSERT_LOCKED(map);
header = &map->header;
root = vm_map_splay_split(map, entry->start, 0, &llist, &rlist);
KASSERT(root != NULL, ("%s: resize object not mapped", __func__));
vm_map_splay_findnext(root, &rlist);
entry->end += grow_amount;
root->max_free = vm_size_max(
vm_map_splay_merge_left(header, root, llist),
vm_map_splay_merge_succ(header, root, rlist));
map->root = root;
VM_MAP_ASSERT_CONSISTENT(map);
CTR4(KTR_VM, "%s: map %p, nentries %d, entry %p",
__func__, map, map->nentries, entry);
}
boolean_t
vm_map_lookup_entry(
vm_map_t map,
vm_offset_t address,
vm_map_entry_t *entry)
{
vm_map_entry_t cur, header, lbound, ubound;
boolean_t locked;
header = &map->header;
cur = map->root;
if (cur == NULL) {
*entry = header;
return (FALSE);
}
if (address >= cur->start && cur->end > address) {
*entry = cur;
return (TRUE);
}
if ((locked = vm_map_locked(map)) ||
sx_try_upgrade(&map->lock)) {
cur = vm_map_splay(map, address);
if (!locked) {
VM_MAP_UNLOCK_CONSISTENT(map);
sx_downgrade(&map->lock);
}
if (address < cur->start) {
*entry = header;
return (FALSE);
}
*entry = cur;
return (address < cur->end);
}
lbound = ubound = header;
for (;;) {
if (address < cur->start) {
ubound = cur;
cur = cur->left;
if (cur == lbound)
break;
} else if (cur->end <= address) {
lbound = cur;
cur = cur->right;
if (cur == ubound)
break;
} else {
*entry = cur;
return (TRUE);
}
}
*entry = lbound;
return (FALSE);
}
static int
vm_map_insert1(vm_map_t map, vm_object_t object, vm_ooffset_t offset,
vm_offset_t start, vm_offset_t end, vm_prot_t prot, vm_prot_t max, int cow,
vm_map_entry_t *res)
{
vm_map_entry_t new_entry, next_entry, prev_entry;
struct ucred *cred;
vm_eflags_t protoeflags;
vm_inherit_t inheritance;
u_long bdry;
u_int bidx;
VM_MAP_ASSERT_LOCKED(map);
KASSERT(object != kernel_object ||
(cow & MAP_COPY_ON_WRITE) == 0,
("vm_map_insert: kernel object and COW"));
KASSERT(object == NULL || (cow & MAP_NOFAULT) == 0 ||
(cow & MAP_SPLIT_BOUNDARY_MASK) != 0,
("vm_map_insert: paradoxical MAP_NOFAULT request, obj %p cow %#x",
object, cow));
KASSERT((prot & ~max) == 0,
("prot %#x is not subset of max_prot %#x", prot, max));
if (start == end || !vm_map_range_valid(map, start, end))
return (KERN_INVALID_ADDRESS);
if ((map->flags & MAP_WXORX) != 0 && (prot & (VM_PROT_WRITE |
VM_PROT_EXECUTE)) == (VM_PROT_WRITE | VM_PROT_EXECUTE))
return (KERN_PROTECTION_FAILURE);
if (vm_map_lookup_entry(map, start, &prev_entry))
return (KERN_NO_SPACE);
next_entry = vm_map_entry_succ(prev_entry);
if (next_entry->start < end)
return (KERN_NO_SPACE);
if ((cow & MAP_CREATE_GUARD) != 0 && (object != NULL ||
max != VM_PROT_NONE))
return (KERN_INVALID_ARGUMENT);
protoeflags = 0;
if (cow & MAP_COPY_ON_WRITE)
protoeflags |= MAP_ENTRY_COW | MAP_ENTRY_NEEDS_COPY;
if (cow & MAP_NOFAULT)
protoeflags |= MAP_ENTRY_NOFAULT;
if (cow & MAP_DISABLE_SYNCER)
protoeflags |= MAP_ENTRY_NOSYNC;
if (cow & MAP_DISABLE_COREDUMP)
protoeflags |= MAP_ENTRY_NOCOREDUMP;
if (cow & MAP_STACK_AREA)
protoeflags |= MAP_ENTRY_GROWS_DOWN;
if (cow & MAP_WRITECOUNT)
protoeflags |= MAP_ENTRY_WRITECNT;
if (cow & MAP_VN_EXEC)
protoeflags |= MAP_ENTRY_VN_EXEC;
if ((cow & MAP_CREATE_GUARD) != 0)
protoeflags |= MAP_ENTRY_GUARD;
if ((cow & MAP_CREATE_STACK_GAP) != 0)
protoeflags |= MAP_ENTRY_STACK_GAP;
if (cow & MAP_INHERIT_SHARE)
inheritance = VM_INHERIT_SHARE;
else
inheritance = VM_INHERIT_DEFAULT;
if ((cow & MAP_SPLIT_BOUNDARY_MASK) != 0) {
bidx = (cow & MAP_SPLIT_BOUNDARY_MASK) >>
MAP_SPLIT_BOUNDARY_SHIFT;
if (bidx >= MAXPAGESIZES)
return (KERN_INVALID_ARGUMENT);
bdry = pagesizes[bidx] - 1;
if ((start & bdry) != 0 || (end & bdry) != 0)
return (KERN_INVALID_ARGUMENT);
protoeflags |= bidx << MAP_ENTRY_SPLIT_BOUNDARY_SHIFT;
}
cred = NULL;
if ((cow & (MAP_ACC_NO_CHARGE | MAP_NOFAULT | MAP_CREATE_GUARD)) != 0)
goto charged;
if ((cow & MAP_ACC_CHARGED) || ((prot & VM_PROT_WRITE) &&
((protoeflags & MAP_ENTRY_NEEDS_COPY) || object == NULL))) {
if (!(cow & MAP_ACC_CHARGED) && !swap_reserve(end - start))
return (KERN_RESOURCE_SHORTAGE);
KASSERT(object == NULL ||
(protoeflags & MAP_ENTRY_NEEDS_COPY) != 0 ||
object->cred == NULL,
("overcommit: vm_map_insert o %p", object));
cred = curthread->td_ucred;
}
charged:
if (map == kernel_map && end > kernel_vm_end) {
int rv;
rv = pmap_growkernel(end);
if (rv != KERN_SUCCESS)
return (rv);
}
if (object != NULL) {
if ((object->flags & OBJ_ANON) != 0) {
VM_OBJECT_WLOCK(object);
if (object->ref_count > 1 || object->shadow_count != 0)
vm_object_clear_flag(object, OBJ_ONEMAPPING);
VM_OBJECT_WUNLOCK(object);
}
} else if ((prev_entry->eflags & ~MAP_ENTRY_USER_WIRED) ==
protoeflags &&
(cow & (MAP_STACK_AREA | MAP_VN_EXEC)) == 0 &&
prev_entry->end == start && (prev_entry->cred == cred ||
(prev_entry->object.vm_object != NULL &&
prev_entry->object.vm_object->cred == cred)) &&
vm_object_coalesce(prev_entry->object.vm_object,
prev_entry->offset,
(vm_size_t)(prev_entry->end - prev_entry->start),
(vm_size_t)(end - prev_entry->end), cred != NULL &&
(protoeflags & MAP_ENTRY_NEEDS_COPY) == 0)) {
if (prev_entry->inheritance == inheritance &&
prev_entry->protection == prot &&
prev_entry->max_protection == max &&
prev_entry->wired_count == 0) {
KASSERT((prev_entry->eflags & MAP_ENTRY_USER_WIRED) ==
0, ("prev_entry %p has incoherent wiring",
prev_entry));
if ((prev_entry->eflags & MAP_ENTRY_GUARD) == 0)
map->size += end - prev_entry->end;
vm_map_entry_resize(map, prev_entry,
end - prev_entry->end);
*res = vm_map_try_merge_entries(map, prev_entry,
next_entry);
return (KERN_SUCCESS);
}
object = prev_entry->object.vm_object;
offset = prev_entry->offset +
(prev_entry->end - prev_entry->start);
vm_object_reference(object);
if (cred != NULL && object != NULL && object->cred != NULL &&
!(prev_entry->eflags & MAP_ENTRY_NEEDS_COPY)) {
cred = NULL;
}
}
if (cred != NULL)
crhold(cred);
new_entry = vm_map_entry_create(map);
new_entry->start = start;
new_entry->end = end;
new_entry->cred = NULL;
new_entry->eflags = protoeflags;
new_entry->object.vm_object = object;
new_entry->offset = offset;
new_entry->inheritance = inheritance;
new_entry->protection = prot;
new_entry->max_protection = max;
new_entry->wired_count = 0;
new_entry->wiring_thread = NULL;
new_entry->read_ahead = VM_FAULT_READ_AHEAD_INIT;
new_entry->next_read = start;
KASSERT(cred == NULL || !ENTRY_CHARGED(new_entry),
("overcommit: vm_map_insert leaks vm_map %p", new_entry));
new_entry->cred = cred;
vm_map_entry_link(map, new_entry);
if ((new_entry->eflags & MAP_ENTRY_GUARD) == 0)
map->size += new_entry->end - new_entry->start;
vm_map_try_merge_entries(map, prev_entry, new_entry);
*res = vm_map_try_merge_entries(map, new_entry, next_entry);
if ((cow & (MAP_PREFAULT | MAP_PREFAULT_PARTIAL)) != 0) {
vm_map_pmap_enter(map, start, prot, object, OFF_TO_IDX(offset),
end - start, cow & MAP_PREFAULT_PARTIAL);
}
return (KERN_SUCCESS);
}
int
vm_map_insert(vm_map_t map, vm_object_t object, vm_ooffset_t offset,
vm_offset_t start, vm_offset_t end, vm_prot_t prot, vm_prot_t max, int cow)
{
vm_map_entry_t res;
return (vm_map_insert1(map, object, offset, start, end, prot, max,
cow, &res));
}
vm_offset_t
vm_map_findspace(vm_map_t map, vm_offset_t start, vm_size_t length)
{
vm_map_entry_t header, llist, rlist, root, y;
vm_size_t left_length, max_free_left, max_free_right;
vm_offset_t gap_end;
VM_MAP_ASSERT_LOCKED(map);
start = MAX(start, vm_map_min(map));
if (start >= vm_map_max(map) || length > vm_map_max(map) - start)
return (vm_map_max(map) - length + 1);
if (map->root == NULL)
return (start);
header = &map->header;
root = vm_map_splay_split(map, start, length, &llist, &rlist);
gap_end = rlist->start;
if (root != NULL) {
start = root->end;
if (root->right != rlist)
gap_end = start;
max_free_left = vm_map_splay_merge_left(header, root, llist);
max_free_right = vm_map_splay_merge_right(header, root, rlist);
} else if (rlist != header) {
root = rlist;
rlist = root->left;
max_free_left = vm_map_splay_merge_pred(header, root, llist);
max_free_right = vm_map_splay_merge_right(header, root, rlist);
} else {
root = llist;
llist = root->right;
max_free_left = vm_map_splay_merge_left(header, root, llist);
max_free_right = vm_map_splay_merge_succ(header, root, rlist);
}
root->max_free = vm_size_max(max_free_left, max_free_right);
map->root = root;
VM_MAP_ASSERT_CONSISTENT(map);
if (length <= gap_end - start)
return (start);
if (root->right == header || length > root->right->max_free)
return (vm_map_max(map) - length + 1);
llist = rlist = header;
for (left_length = 0;;
left_length = vm_map_entry_max_free_left(root, llist)) {
if (length <= left_length)
SPLAY_LEFT_STEP(root, y, llist, rlist,
length <= vm_map_entry_max_free_left(y, llist));
else
SPLAY_RIGHT_STEP(root, y, llist, rlist,
length > vm_map_entry_max_free_left(y, root));
if (root == NULL)
break;
}
root = llist;
llist = root->right;
max_free_left = vm_map_splay_merge_left(header, root, llist);
if (rlist == header) {
root->max_free = vm_size_max(max_free_left,
vm_map_splay_merge_succ(header, root, rlist));
} else {
y = rlist;
rlist = y->left;
y->max_free = vm_size_max(
vm_map_splay_merge_pred(root, y, root),
vm_map_splay_merge_right(header, y, rlist));
root->max_free = vm_size_max(max_free_left, y->max_free);
}
map->root = root;
VM_MAP_ASSERT_CONSISTENT(map);
return (root->end);
}
int
vm_map_fixed(vm_map_t map, vm_object_t object, vm_ooffset_t offset,
vm_offset_t start, vm_size_t length, vm_prot_t prot,
vm_prot_t max, int cow)
{
vm_offset_t end;
int result;
end = start + length;
KASSERT((cow & MAP_STACK_AREA) == 0 || object == NULL,
("vm_map_fixed: non-NULL backing object for stack"));
vm_map_lock(map);
VM_MAP_RANGE_CHECK(map, start, end);
if ((cow & MAP_CHECK_EXCL) == 0) {
result = vm_map_delete(map, start, end);
if (result != KERN_SUCCESS)
goto out;
}
if ((cow & MAP_STACK_AREA) != 0) {
result = vm_map_stack_locked(map, start, length, sgrowsiz,
prot, max, cow);
} else {
result = vm_map_insert(map, object, offset, start, end,
prot, max, cow);
}
out:
vm_map_unlock(map);
return (result);
}
#if VM_NRESERVLEVEL <= 1
static const int aslr_pages_rnd_64[2] = {0x1000, 0x10};
static const int aslr_pages_rnd_32[2] = {0x100, 0x4};
#elif VM_NRESERVLEVEL == 2
static const int aslr_pages_rnd_64[3] = {0x1000, 0x1000, 0x10};
static const int aslr_pages_rnd_32[3] = {0x100, 0x100, 0x4};
#else
#error "Unsupported VM_NRESERVLEVEL"
#endif
static int cluster_anon = 1;
SYSCTL_INT(_vm, OID_AUTO, cluster_anon, CTLFLAG_RW,
&cluster_anon, 0,
"Cluster anonymous mappings: 0 = no, 1 = yes if no hint, 2 = always");
static bool
clustering_anon_allowed(vm_offset_t addr, int cow)
{
switch (cluster_anon) {
case 0:
return (false);
case 1:
return (addr == 0 || (cow & MAP_NO_HINT) != 0);
case 2:
default:
return (true);
}
}
static long aslr_restarts;
SYSCTL_LONG(_vm, OID_AUTO, aslr_restarts, CTLFLAG_RD,
&aslr_restarts, 0,
"Number of aslr failures");
static int
vm_map_alignspace(vm_map_t map, vm_object_t object, vm_ooffset_t offset,
vm_offset_t *addr, vm_size_t length, vm_offset_t max_addr,
vm_offset_t alignment)
{
vm_offset_t aligned_addr, free_addr;
VM_MAP_ASSERT_LOCKED(map);
free_addr = *addr;
KASSERT(free_addr == vm_map_findspace(map, free_addr, length),
("caller failed to provide space %#jx at address %p",
(uintmax_t)length, (void *)free_addr));
for (;;) {
if (alignment == 0)
pmap_align_superpage(object, offset, addr, length);
else
*addr = roundup2(*addr, alignment);
aligned_addr = *addr;
if (aligned_addr == free_addr) {
return (KERN_SUCCESS);
}
if (aligned_addr < free_addr)
return (KERN_NO_SPACE);
*addr = vm_map_findspace(map, aligned_addr, length);
if (*addr + length > vm_map_max(map) ||
(max_addr != 0 && *addr + length > max_addr))
return (KERN_NO_SPACE);
free_addr = *addr;
if (free_addr == aligned_addr) {
return (KERN_SUCCESS);
}
}
}
int
vm_map_find_aligned(vm_map_t map, vm_offset_t *addr, vm_size_t length,
vm_offset_t max_addr, vm_offset_t alignment)
{
*addr = vm_map_findspace(map, *addr, length);
if (*addr + length > vm_map_max(map) ||
(max_addr != 0 && *addr + length > max_addr))
return (KERN_NO_SPACE);
return (vm_map_alignspace(map, NULL, 0, addr, length, max_addr,
alignment));
}
int
vm_map_find(vm_map_t map, vm_object_t object, vm_ooffset_t offset,
vm_offset_t *addr,
vm_size_t length, vm_offset_t max_addr, int find_space,
vm_prot_t prot, vm_prot_t max, int cow)
{
int rv;
vm_map_lock(map);
rv = vm_map_find_locked(map, object, offset, addr, length, max_addr,
find_space, prot, max, cow);
vm_map_unlock(map);
return (rv);
}
int
vm_map_find_locked(vm_map_t map, vm_object_t object, vm_ooffset_t offset,
vm_offset_t *addr,
vm_size_t length, vm_offset_t max_addr, int find_space,
vm_prot_t prot, vm_prot_t max, int cow)
{
vm_offset_t alignment, curr_min_addr, min_addr;
int gap, pidx, rv, try;
bool cluster, en_aslr, update_anon;
KASSERT((cow & MAP_STACK_AREA) == 0 || object == NULL,
("non-NULL backing object for stack"));
MPASS((cow & MAP_REMAP) == 0 || (find_space == VMFS_NO_SPACE &&
(cow & MAP_STACK_AREA) == 0));
if (find_space == VMFS_OPTIMAL_SPACE && (object == NULL ||
(object->flags & OBJ_COLORED) == 0))
find_space = VMFS_ANY_SPACE;
if (find_space >> 8 != 0) {
KASSERT((find_space & 0xff) == 0, ("bad VMFS flags"));
alignment = (vm_offset_t)1 << (find_space >> 8);
} else
alignment = 0;
en_aslr = (map->flags & MAP_ASLR) != 0;
update_anon = cluster = clustering_anon_allowed(*addr, cow) &&
(map->flags & MAP_IS_SUB_MAP) == 0 && max_addr == 0 &&
find_space != VMFS_NO_SPACE && object == NULL &&
(cow & (MAP_INHERIT_SHARE | MAP_STACK_AREA)) == 0 &&
prot != PROT_NONE;
curr_min_addr = min_addr = *addr;
if (en_aslr && min_addr == 0 && !cluster &&
find_space != VMFS_NO_SPACE &&
(map->flags & MAP_ASLR_IGNSTART) != 0)
curr_min_addr = min_addr = vm_map_min(map);
try = 0;
if (cluster) {
curr_min_addr = map->anon_loc;
if (curr_min_addr == 0)
cluster = false;
}
if (find_space != VMFS_NO_SPACE) {
KASSERT(find_space == VMFS_ANY_SPACE ||
find_space == VMFS_OPTIMAL_SPACE ||
find_space == VMFS_SUPER_SPACE ||
alignment != 0, ("unexpected VMFS flag"));
again:
try++;
MPASS(try <= 2);
if (try == 2) {
curr_min_addr = (map->flags & MAP_ASLR_IGNSTART) != 0 ?
vm_map_min(map) : min_addr;
atomic_add_long(&aslr_restarts, 1);
}
if (try == 1 && en_aslr && !cluster) {
pidx = 0;
#if VM_NRESERVLEVEL > 0
if ((find_space == VMFS_SUPER_SPACE ||
find_space == VMFS_OPTIMAL_SPACE) &&
pagesizes[VM_NRESERVLEVEL] != 0) {
pidx = VM_NRESERVLEVEL;
while (pidx > 0 && length < pagesizes[pidx])
pidx--;
}
#endif
gap = vm_map_max(map) > MAP_32BIT_MAX_ADDR &&
(max_addr == 0 || max_addr > MAP_32BIT_MAX_ADDR) ?
aslr_pages_rnd_64[pidx] : aslr_pages_rnd_32[pidx];
*addr = vm_map_findspace(map, curr_min_addr,
length + gap * pagesizes[pidx]);
if (*addr + length + gap * pagesizes[pidx] >
vm_map_max(map))
goto again;
*addr += (arc4random() % gap) * pagesizes[pidx];
if (max_addr != 0 && *addr + length > max_addr)
goto again;
} else {
*addr = vm_map_findspace(map, curr_min_addr, length);
if (*addr + length > vm_map_max(map) ||
(max_addr != 0 && *addr + length > max_addr)) {
if (cluster) {
cluster = false;
MPASS(try == 1);
goto again;
}
return (KERN_NO_SPACE);
}
}
if (find_space != VMFS_ANY_SPACE &&
(rv = vm_map_alignspace(map, object, offset, addr, length,
max_addr, alignment)) != KERN_SUCCESS) {
if (find_space == VMFS_OPTIMAL_SPACE) {
find_space = VMFS_ANY_SPACE;
curr_min_addr = min_addr;
cluster = update_anon;
try = 0;
goto again;
}
return (rv);
}
} else if ((cow & MAP_REMAP) != 0) {
if (!vm_map_range_valid(map, *addr, *addr + length))
return (KERN_INVALID_ADDRESS);
rv = vm_map_delete(map, *addr, *addr + length);
if (rv != KERN_SUCCESS)
return (rv);
}
if ((cow & MAP_STACK_AREA) != 0) {
rv = vm_map_stack_locked(map, *addr, length, sgrowsiz, prot,
max, cow);
} else {
rv = vm_map_insert(map, object, offset, *addr, *addr + length,
prot, max, cow);
}
if (update_anon && rv == KERN_SUCCESS && (map->anon_loc == 0 ||
*addr < map->anon_loc))
map->anon_loc = *addr;
return (rv);
}
int
vm_map_find_min(vm_map_t map, vm_object_t object, vm_ooffset_t offset,
vm_offset_t *addr, vm_size_t length, vm_offset_t default_addr,
vm_offset_t max_addr, int find_space, vm_prot_t prot, vm_prot_t max,
int cow)
{
vm_offset_t hint;
int rv;
hint = *addr;
if (hint == 0) {
cow |= MAP_NO_HINT;
*addr = hint = default_addr;
}
for (;;) {
rv = vm_map_find(map, object, offset, addr, length, max_addr,
find_space, prot, max, cow);
if (rv == KERN_SUCCESS || default_addr >= hint)
return (rv);
*addr = hint = default_addr;
}
}
#define MAP_ENTRY_NOMERGE_MASK (MAP_ENTRY_GROWS_DOWN | \
MAP_ENTRY_IN_TRANSITION | MAP_ENTRY_IS_SUB_MAP | MAP_ENTRY_VN_EXEC | \
MAP_ENTRY_STACK_GAP)
static bool
vm_map_mergeable_neighbors(vm_map_entry_t prev, vm_map_entry_t entry)
{
KASSERT((prev->eflags & MAP_ENTRY_NOMERGE_MASK) == 0 ||
(entry->eflags & MAP_ENTRY_NOMERGE_MASK) == 0,
("vm_map_mergeable_neighbors: neither %p nor %p are mergeable",
prev, entry));
return (prev->end == entry->start &&
prev->object.vm_object == entry->object.vm_object &&
(prev->object.vm_object == NULL ||
prev->offset + (prev->end - prev->start) == entry->offset) &&
prev->eflags == entry->eflags &&
prev->protection == entry->protection &&
prev->max_protection == entry->max_protection &&
prev->inheritance == entry->inheritance &&
prev->wired_count == entry->wired_count &&
prev->cred == entry->cred);
}
static void
vm_map_merged_neighbor_dispose(vm_map_t map, vm_map_entry_t entry)
{
if (entry->object.vm_object != NULL)
vm_object_deallocate(entry->object.vm_object);
if (entry->cred != NULL)
crfree(entry->cred);
vm_map_entry_dispose(map, entry);
}
vm_map_entry_t
vm_map_try_merge_entries(vm_map_t map, vm_map_entry_t prev_entry,
vm_map_entry_t entry)
{
VM_MAP_ASSERT_LOCKED(map);
if ((entry->eflags & MAP_ENTRY_NOMERGE_MASK) == 0 &&
vm_map_mergeable_neighbors(prev_entry, entry)) {
vm_map_entry_unlink(map, prev_entry, UNLINK_MERGE_NEXT);
vm_map_merged_neighbor_dispose(map, prev_entry);
return (entry);
}
return (prev_entry);
}
static inline void
vm_map_entry_back(vm_map_entry_t entry)
{
vm_object_t object;
KASSERT(entry->object.vm_object == NULL,
("map entry %p has backing object", entry));
KASSERT((entry->eflags & MAP_ENTRY_IS_SUB_MAP) == 0,
("map entry %p is a submap", entry));
object = vm_object_allocate_anon(atop(entry->end - entry->start), NULL,
entry->cred, entry->end - entry->start);
entry->object.vm_object = object;
entry->offset = 0;
entry->cred = NULL;
}
static inline void
vm_map_entry_charge_object(vm_map_t map, vm_map_entry_t entry)
{
VM_MAP_ASSERT_LOCKED(map);
KASSERT((entry->eflags & MAP_ENTRY_IS_SUB_MAP) == 0,
("map entry %p is a submap", entry));
if (entry->object.vm_object == NULL && !vm_map_is_system(map) &&
(entry->eflags & MAP_ENTRY_GUARD) == 0)
vm_map_entry_back(entry);
else if (entry->object.vm_object != NULL &&
((entry->eflags & MAP_ENTRY_NEEDS_COPY) == 0) &&
entry->cred != NULL) {
VM_OBJECT_WLOCK(entry->object.vm_object);
KASSERT(entry->object.vm_object->cred == NULL,
("OVERCOMMIT: %s: both cred e %p", __func__, entry));
entry->object.vm_object->cred = entry->cred;
entry->object.vm_object->charge = entry->end - entry->start;
VM_OBJECT_WUNLOCK(entry->object.vm_object);
entry->cred = NULL;
}
}
static vm_map_entry_t
vm_map_entry_clone(vm_map_t map, vm_map_entry_t entry)
{
vm_map_entry_t new_entry;
VM_MAP_ASSERT_LOCKED(map);
vm_map_entry_charge_object(map, entry);
new_entry = vm_map_entry_create(map);
*new_entry = *entry;
if (new_entry->cred != NULL)
crhold(entry->cred);
if ((entry->eflags & MAP_ENTRY_IS_SUB_MAP) == 0) {
vm_object_reference(new_entry->object.vm_object);
vm_map_entry_set_vnode_text(new_entry, true);
}
return (new_entry);
}
static int
vm_map_clip_start(vm_map_t map, vm_map_entry_t entry, vm_offset_t startaddr)
{
vm_map_entry_t new_entry;
int bdry_idx;
if (!vm_map_is_system(map))
WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL,
"%s: map %p entry %p start 0x%jx", __func__, map, entry,
(uintmax_t)startaddr);
if (startaddr <= entry->start)
return (KERN_SUCCESS);
VM_MAP_ASSERT_LOCKED(map);
KASSERT(entry->end > startaddr && entry->start < startaddr,
("%s: invalid clip of entry %p", __func__, entry));
bdry_idx = MAP_ENTRY_SPLIT_BOUNDARY_INDEX(entry);
if (bdry_idx != 0) {
if ((startaddr & (pagesizes[bdry_idx] - 1)) != 0)
return (KERN_INVALID_ARGUMENT);
}
new_entry = vm_map_entry_clone(map, entry);
new_entry->end = startaddr;
vm_map_entry_link(map, new_entry);
return (KERN_SUCCESS);
}
static int
vm_map_lookup_clip_start(vm_map_t map, vm_offset_t start,
vm_map_entry_t *res_entry, vm_map_entry_t *prev_entry)
{
vm_map_entry_t entry;
int rv;
if (!vm_map_is_system(map))
WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL,
"%s: map %p start 0x%jx prev %p", __func__, map,
(uintmax_t)start, prev_entry);
if (vm_map_lookup_entry(map, start, prev_entry)) {
entry = *prev_entry;
rv = vm_map_clip_start(map, entry, start);
if (rv != KERN_SUCCESS)
return (rv);
*prev_entry = vm_map_entry_pred(entry);
} else
entry = vm_map_entry_succ(*prev_entry);
*res_entry = entry;
return (KERN_SUCCESS);
}
static int
vm_map_clip_end(vm_map_t map, vm_map_entry_t entry, vm_offset_t endaddr)
{
vm_map_entry_t new_entry;
int bdry_idx;
if (!vm_map_is_system(map))
WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL,
"%s: map %p entry %p end 0x%jx", __func__, map, entry,
(uintmax_t)endaddr);
if (endaddr >= entry->end)
return (KERN_SUCCESS);
VM_MAP_ASSERT_LOCKED(map);
KASSERT(entry->start < endaddr && entry->end > endaddr,
("%s: invalid clip of entry %p", __func__, entry));
bdry_idx = MAP_ENTRY_SPLIT_BOUNDARY_INDEX(entry);
if (bdry_idx != 0) {
if ((endaddr & (pagesizes[bdry_idx] - 1)) != 0)
return (KERN_INVALID_ARGUMENT);
}
new_entry = vm_map_entry_clone(map, entry);
new_entry->start = endaddr;
vm_map_entry_link(map, new_entry);
return (KERN_SUCCESS);
}
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;
result = KERN_INVALID_ARGUMENT;
vm_map_lock(submap);
submap->flags |= MAP_IS_SUB_MAP;
vm_map_unlock(submap);
vm_map_lock(map);
VM_MAP_RANGE_CHECK(map, start, end);
if (vm_map_lookup_entry(map, start, &entry) && entry->end >= end &&
(entry->eflags & MAP_ENTRY_COW) == 0 &&
entry->object.vm_object == NULL) {
result = vm_map_clip_start(map, entry, start);
if (result != KERN_SUCCESS)
goto unlock;
result = vm_map_clip_end(map, entry, end);
if (result != KERN_SUCCESS)
goto unlock;
entry->object.sub_map = submap;
entry->eflags |= MAP_ENTRY_IS_SUB_MAP;
result = KERN_SUCCESS;
}
unlock:
vm_map_unlock(map);
if (result != KERN_SUCCESS) {
vm_map_lock(submap);
submap->flags &= ~MAP_IS_SUB_MAP;
vm_map_unlock(submap);
}
return (result);
}
#define MAX_INIT_PT 96
static void
vm_map_pmap_enter(vm_map_t map, vm_offset_t addr, vm_prot_t prot,
vm_object_t object, vm_pindex_t pindex, vm_size_t size, int flags)
{
struct pctrie_iter pages;
vm_offset_t start;
vm_page_t p, p_start;
vm_pindex_t jump, mask, psize, threshold, tmpidx;
int psind;
if ((prot & (VM_PROT_READ | VM_PROT_EXECUTE)) == 0 || object == NULL)
return;
if (object->type == OBJT_DEVICE || object->type == OBJT_SG) {
VM_OBJECT_WLOCK(object);
if (object->type == OBJT_DEVICE || object->type == OBJT_SG) {
pmap_object_init_pt(map->pmap, addr, object, pindex,
size);
VM_OBJECT_WUNLOCK(object);
return;
}
VM_OBJECT_LOCK_DOWNGRADE(object);
} else
VM_OBJECT_RLOCK(object);
psize = atop(size);
if (psize + pindex > object->size) {
if (pindex >= object->size) {
VM_OBJECT_RUNLOCK(object);
return;
}
psize = object->size - pindex;
}
start = 0;
p_start = NULL;
threshold = MAX_INIT_PT;
vm_page_iter_limit_init(&pages, object, pindex + psize);
for (p = vm_radix_iter_lookup_ge(&pages, pindex); p != NULL;
p = vm_radix_iter_jump(&pages, jump)) {
tmpidx = p->pindex - pindex;
if (((flags & MAP_PREFAULT_MADVISE) != 0 &&
vm_page_count_severe()) ||
((flags & MAP_PREFAULT_PARTIAL) != 0 &&
tmpidx >= threshold)) {
psize = tmpidx;
break;
}
jump = 1;
if (vm_page_all_valid(p)) {
if (p_start == NULL) {
start = addr + ptoa(tmpidx);
p_start = p;
}
for (psind = p->psind; psind > 0; psind--) {
if (((addr + ptoa(tmpidx)) &
(pagesizes[psind] - 1)) == 0) {
mask = atop(pagesizes[psind]) - 1;
if (tmpidx + mask < psize &&
vm_page_ps_test(p, psind,
PS_ALL_VALID, NULL)) {
jump += mask;
threshold += mask;
break;
}
}
}
} else if (p_start != NULL) {
pmap_enter_object(map->pmap, start, addr +
ptoa(tmpidx), p_start, prot);
p_start = NULL;
}
}
if (p_start != NULL)
pmap_enter_object(map->pmap, start, addr + ptoa(psize),
p_start, prot);
VM_OBJECT_RUNLOCK(object);
}
static void
vm_map_protect_guard(vm_map_entry_t entry, vm_prot_t new_prot,
vm_prot_t new_maxprot, int flags)
{
vm_prot_t old_prot;
MPASS((entry->eflags & MAP_ENTRY_GUARD) != 0);
if ((entry->eflags & MAP_ENTRY_STACK_GAP) == 0)
return;
old_prot = PROT_EXTRACT(entry->offset);
if ((flags & VM_MAP_PROTECT_SET_MAXPROT) != 0) {
entry->offset = PROT_MAX(new_maxprot) |
(new_maxprot & old_prot);
}
if ((flags & VM_MAP_PROTECT_SET_PROT) != 0) {
entry->offset = new_prot | PROT_MAX(
PROT_MAX_EXTRACT(entry->offset));
}
}
int
vm_map_protect(vm_map_t map, vm_offset_t start, vm_offset_t end,
vm_prot_t new_prot, vm_prot_t new_maxprot, int flags)
{
vm_map_entry_t entry, first_entry, in_tran, prev_entry;
vm_object_t obj;
struct ucred *cred;
vm_offset_t orig_start;
vm_prot_t check_prot, max_prot, old_prot;
int rv;
if (start == end)
return (KERN_SUCCESS);
if (CONTAINS_BITS(flags, VM_MAP_PROTECT_SET_PROT |
VM_MAP_PROTECT_SET_MAXPROT) &&
!CONTAINS_BITS(new_maxprot, new_prot))
return (KERN_OUT_OF_BOUNDS);
orig_start = start;
again:
in_tran = NULL;
start = orig_start;
vm_map_lock(map);
if ((map->flags & MAP_WXORX) != 0 &&
(flags & VM_MAP_PROTECT_SET_PROT) != 0 &&
CONTAINS_BITS(new_prot, VM_PROT_WRITE | VM_PROT_EXECUTE)) {
vm_map_unlock(map);
return (KERN_PROTECTION_FAILURE);
}
vm_map_wait_busy(map);
VM_MAP_RANGE_CHECK(map, start, end);
if (!vm_map_lookup_entry(map, start, &first_entry))
first_entry = vm_map_entry_succ(first_entry);
if ((flags & VM_MAP_PROTECT_GROWSDOWN) != 0 &&
(first_entry->eflags & MAP_ENTRY_GROWS_DOWN) != 0) {
while (!CONTAINS_BITS(first_entry->eflags,
MAP_ENTRY_GUARD | MAP_ENTRY_STACK_GAP) &&
first_entry != vm_map_entry_first(map))
first_entry = vm_map_entry_pred(first_entry);
start = first_entry->start;
}
check_prot = 0;
if ((flags & VM_MAP_PROTECT_SET_PROT) != 0)
check_prot |= new_prot;
if ((flags & VM_MAP_PROTECT_SET_MAXPROT) != 0)
check_prot |= new_maxprot;
for (entry = first_entry; entry->start < end;
entry = vm_map_entry_succ(entry)) {
if ((entry->eflags & MAP_ENTRY_IS_SUB_MAP) != 0) {
vm_map_unlock(map);
return (KERN_INVALID_ARGUMENT);
}
if ((entry->eflags & (MAP_ENTRY_GUARD |
MAP_ENTRY_STACK_GAP)) == MAP_ENTRY_GUARD)
continue;
max_prot = (entry->eflags & MAP_ENTRY_STACK_GAP) != 0 ?
PROT_MAX_EXTRACT(entry->offset) : entry->max_protection;
if (!CONTAINS_BITS(max_prot, check_prot)) {
vm_map_unlock(map);
return (KERN_PROTECTION_FAILURE);
}
if ((entry->eflags & MAP_ENTRY_IN_TRANSITION) != 0)
in_tran = entry;
}
if (in_tran != NULL) {
in_tran->eflags |= MAP_ENTRY_NEEDS_WAKEUP;
vm_map_unlock_and_wait(map, 0);
goto again;
}
rv = vm_map_clip_start(map, first_entry, start);
if (rv != KERN_SUCCESS) {
vm_map_unlock(map);
return (rv);
}
for (entry = first_entry; entry->start < end;
entry = vm_map_entry_succ(entry)) {
rv = vm_map_clip_end(map, entry, end);
if (rv != KERN_SUCCESS) {
vm_map_unlock(map);
return (rv);
}
if ((flags & VM_MAP_PROTECT_SET_PROT) == 0 ||
((new_prot & ~entry->protection) & VM_PROT_WRITE) == 0 ||
ENTRY_CHARGED(entry) ||
(entry->eflags & MAP_ENTRY_GUARD) != 0)
continue;
cred = curthread->td_ucred;
obj = entry->object.vm_object;
if (obj == NULL ||
(entry->eflags & MAP_ENTRY_NEEDS_COPY) != 0) {
if (!swap_reserve(entry->end - entry->start)) {
rv = KERN_RESOURCE_SHORTAGE;
end = entry->end;
break;
}
crhold(cred);
entry->cred = cred;
continue;
}
VM_OBJECT_WLOCK(obj);
if ((obj->flags & OBJ_SWAP) == 0) {
VM_OBJECT_WUNLOCK(obj);
continue;
}
KASSERT(obj->charge == 0,
("vm_map_protect: object %p overcharged (entry %p)",
obj, entry));
if (!swap_reserve(ptoa(obj->size))) {
VM_OBJECT_WUNLOCK(obj);
rv = KERN_RESOURCE_SHORTAGE;
end = entry->end;
break;
}
crhold(cred);
obj->cred = cred;
obj->charge = ptoa(obj->size);
VM_OBJECT_WUNLOCK(obj);
}
for (prev_entry = vm_map_entry_pred(first_entry), entry = first_entry;
entry->start < end;
vm_map_try_merge_entries(map, prev_entry, entry),
prev_entry = entry, entry = vm_map_entry_succ(entry)) {
if (rv != KERN_SUCCESS)
continue;
if ((entry->eflags & MAP_ENTRY_GUARD) != 0) {
vm_map_protect_guard(entry, new_prot, new_maxprot,
flags);
continue;
}
old_prot = entry->protection;
if ((flags & VM_MAP_PROTECT_SET_MAXPROT) != 0) {
entry->max_protection = new_maxprot;
entry->protection = new_maxprot & old_prot;
}
if ((flags & VM_MAP_PROTECT_SET_PROT) != 0)
entry->protection = new_prot;
if ((entry->eflags & MAP_ENTRY_USER_WIRED) != 0 &&
(entry->protection & VM_PROT_WRITE) != 0 &&
(old_prot & VM_PROT_WRITE) == 0)
vm_fault_copy_entry(map, map, entry, entry, NULL);
if ((old_prot & ~entry->protection) != 0) {
#define MASK(entry) (((entry)->eflags & MAP_ENTRY_COW) ? ~VM_PROT_WRITE : \
VM_PROT_ALL)
pmap_protect(map->pmap, entry->start,
entry->end,
entry->protection & MASK(entry));
#undef MASK
}
}
vm_map_try_merge_entries(map, prev_entry, entry);
vm_map_unlock(map);
return (rv);
}
int
vm_map_madvise(
vm_map_t map,
vm_offset_t start,
vm_offset_t end,
int behav)
{
vm_map_entry_t entry, prev_entry;
int rv;
bool modify_map;
switch(behav) {
case MADV_NORMAL:
case MADV_SEQUENTIAL:
case MADV_RANDOM:
case MADV_NOSYNC:
case MADV_AUTOSYNC:
case MADV_NOCORE:
case MADV_CORE:
if (start == end)
return (0);
modify_map = true;
vm_map_lock(map);
break;
case MADV_WILLNEED:
case MADV_DONTNEED:
case MADV_FREE:
if (start == end)
return (0);
modify_map = false;
vm_map_lock_read(map);
break;
default:
return (EINVAL);
}
VM_MAP_RANGE_CHECK(map, start, end);
if (modify_map) {
rv = vm_map_lookup_clip_start(map, start, &entry, &prev_entry);
if (rv != KERN_SUCCESS) {
vm_map_unlock(map);
return (vm_mmap_to_errno(rv));
}
for (; entry->start < end; prev_entry = entry,
entry = vm_map_entry_succ(entry)) {
if ((entry->eflags & MAP_ENTRY_IS_SUB_MAP) != 0)
continue;
rv = vm_map_clip_end(map, entry, end);
if (rv != KERN_SUCCESS) {
vm_map_unlock(map);
return (vm_mmap_to_errno(rv));
}
switch (behav) {
case MADV_NORMAL:
vm_map_entry_set_behavior(entry,
MAP_ENTRY_BEHAV_NORMAL);
break;
case MADV_SEQUENTIAL:
vm_map_entry_set_behavior(entry,
MAP_ENTRY_BEHAV_SEQUENTIAL);
break;
case MADV_RANDOM:
vm_map_entry_set_behavior(entry,
MAP_ENTRY_BEHAV_RANDOM);
break;
case MADV_NOSYNC:
entry->eflags |= MAP_ENTRY_NOSYNC;
break;
case MADV_AUTOSYNC:
entry->eflags &= ~MAP_ENTRY_NOSYNC;
break;
case MADV_NOCORE:
entry->eflags |= MAP_ENTRY_NOCOREDUMP;
break;
case MADV_CORE:
entry->eflags &= ~MAP_ENTRY_NOCOREDUMP;
break;
default:
break;
}
vm_map_try_merge_entries(map, prev_entry, entry);
}
vm_map_try_merge_entries(map, prev_entry, entry);
vm_map_unlock(map);
} else {
vm_pindex_t pstart, pend;
if (!vm_map_lookup_entry(map, start, &entry))
entry = vm_map_entry_succ(entry);
for (; entry->start < end;
entry = vm_map_entry_succ(entry)) {
vm_offset_t useEnd, useStart;
if ((entry->eflags & (MAP_ENTRY_IS_SUB_MAP |
MAP_ENTRY_GUARD)) != 0)
continue;
if (behav == MADV_FREE &&
entry->object.vm_object != NULL &&
entry->object.vm_object->backing_object != NULL)
continue;
pstart = OFF_TO_IDX(entry->offset);
pend = pstart + atop(entry->end - entry->start);
useStart = entry->start;
useEnd = entry->end;
if (entry->start < start) {
pstart += atop(start - entry->start);
useStart = start;
}
if (entry->end > end) {
pend -= atop(entry->end - end);
useEnd = end;
}
if (pstart >= pend)
continue;
if (behav == MADV_DONTNEED || behav == MADV_FREE)
pmap_advise(map->pmap, useStart, useEnd,
behav);
vm_object_madvise(entry->object.vm_object, pstart,
pend, behav);
if (behav == MADV_WILLNEED &&
entry->wired_count == 0) {
vm_map_pmap_enter(map,
useStart,
entry->protection,
entry->object.vm_object,
pstart,
ptoa(pend - pstart),
MAP_PREFAULT_MADVISE
);
}
}
vm_map_unlock_read(map);
}
return (0);
}
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, lentry, prev_entry, start_entry;
int rv;
switch (new_inheritance) {
case VM_INHERIT_NONE:
case VM_INHERIT_COPY:
case VM_INHERIT_SHARE:
case VM_INHERIT_ZERO:
break;
default:
return (KERN_INVALID_ARGUMENT);
}
if (start == end)
return (KERN_SUCCESS);
vm_map_lock(map);
VM_MAP_RANGE_CHECK(map, start, end);
rv = vm_map_lookup_clip_start(map, start, &start_entry, &prev_entry);
if (rv != KERN_SUCCESS)
goto unlock;
if (vm_map_lookup_entry(map, end - 1, &lentry)) {
rv = vm_map_clip_end(map, lentry, end);
if (rv != KERN_SUCCESS)
goto unlock;
}
if (new_inheritance == VM_INHERIT_COPY) {
for (entry = start_entry; entry->start < end;
prev_entry = entry, entry = vm_map_entry_succ(entry)) {
if ((entry->eflags & MAP_ENTRY_SPLIT_BOUNDARY_MASK)
!= 0) {
rv = KERN_INVALID_ARGUMENT;
goto unlock;
}
}
}
for (entry = start_entry; entry->start < end; prev_entry = entry,
entry = vm_map_entry_succ(entry)) {
KASSERT(entry->end <= end, ("non-clipped entry %p end %jx %jx",
entry, (uintmax_t)entry->end, (uintmax_t)end));
if ((entry->eflags & MAP_ENTRY_GUARD) == 0 ||
new_inheritance != VM_INHERIT_ZERO)
entry->inheritance = new_inheritance;
vm_map_try_merge_entries(map, prev_entry, entry);
}
vm_map_try_merge_entries(map, prev_entry, entry);
unlock:
vm_map_unlock(map);
return (rv);
}
static vm_map_entry_t
vm_map_entry_in_transition(vm_map_t map, vm_offset_t in_start,
vm_offset_t *io_end, bool holes_ok, vm_map_entry_t in_entry)
{
vm_map_entry_t entry;
vm_offset_t start;
u_int last_timestamp;
VM_MAP_ASSERT_LOCKED(map);
KASSERT((in_entry->eflags & MAP_ENTRY_IN_TRANSITION) != 0,
("not in-tranition map entry %p", in_entry));
start = MAX(in_start, in_entry->start);
in_entry->eflags |= MAP_ENTRY_NEEDS_WAKEUP;
last_timestamp = map->timestamp;
if (vm_map_unlock_and_wait(map, 0)) {
}
vm_map_lock(map);
if (last_timestamp + 1 == map->timestamp)
return (in_entry);
if (!vm_map_lookup_entry(map, start, &entry)) {
if (!holes_ok) {
*io_end = start;
return (NULL);
}
entry = vm_map_entry_succ(entry);
}
return (entry);
}
int
vm_map_unwire(vm_map_t map, vm_offset_t start, vm_offset_t end,
int flags)
{
vm_map_entry_t entry, first_entry, next_entry, prev_entry;
int rv;
bool holes_ok, need_wakeup, user_unwire;
if (start == end)
return (KERN_SUCCESS);
holes_ok = (flags & VM_MAP_WIRE_HOLESOK) != 0;
user_unwire = (flags & VM_MAP_WIRE_USER) != 0;
vm_map_lock(map);
VM_MAP_RANGE_CHECK(map, start, end);
if (!vm_map_lookup_entry(map, start, &first_entry)) {
if (holes_ok)
first_entry = vm_map_entry_succ(first_entry);
else {
vm_map_unlock(map);
return (KERN_INVALID_ADDRESS);
}
}
rv = KERN_SUCCESS;
for (entry = first_entry; entry->start < end; entry = next_entry) {
if (entry->eflags & MAP_ENTRY_IN_TRANSITION) {
next_entry = vm_map_entry_in_transition(map, start,
&end, holes_ok, entry);
if (next_entry == NULL) {
if (entry == first_entry) {
vm_map_unlock(map);
return (KERN_INVALID_ADDRESS);
}
rv = KERN_INVALID_ADDRESS;
break;
}
first_entry = (entry == first_entry) ?
next_entry : NULL;
continue;
}
rv = vm_map_clip_start(map, entry, start);
if (rv != KERN_SUCCESS)
break;
rv = vm_map_clip_end(map, entry, end);
if (rv != KERN_SUCCESS)
break;
KASSERT((entry->eflags & MAP_ENTRY_IN_TRANSITION) == 0 &&
entry->wiring_thread == NULL,
("owned map entry %p", entry));
entry->eflags |= MAP_ENTRY_IN_TRANSITION;
entry->wiring_thread = curthread;
next_entry = vm_map_entry_succ(entry);
if (!holes_ok &&
entry->end < end && next_entry->start > entry->end) {
end = entry->end;
rv = KERN_INVALID_ADDRESS;
break;
}
if (!user_unwire &&
vm_map_entry_system_wired_count(entry) == 0) {
end = entry->end;
rv = KERN_INVALID_ARGUMENT;
break;
}
}
need_wakeup = false;
if (first_entry == NULL &&
!vm_map_lookup_entry(map, start, &first_entry)) {
KASSERT(holes_ok, ("vm_map_unwire: lookup failed"));
prev_entry = first_entry;
entry = vm_map_entry_succ(first_entry);
} else {
prev_entry = vm_map_entry_pred(first_entry);
entry = first_entry;
}
for (; entry->start < end;
prev_entry = entry, entry = vm_map_entry_succ(entry)) {
if ((entry->eflags & MAP_ENTRY_IN_TRANSITION) == 0 ||
entry->wiring_thread != curthread) {
KASSERT(holes_ok,
("vm_map_unwire: !HOLESOK and new/changed entry"));
continue;
}
if (rv == KERN_SUCCESS && (!user_unwire ||
(entry->eflags & MAP_ENTRY_USER_WIRED))) {
if (entry->wired_count == 1)
vm_map_entry_unwire(map, entry);
else
entry->wired_count--;
if (user_unwire)
entry->eflags &= ~MAP_ENTRY_USER_WIRED;
}
KASSERT((entry->eflags & MAP_ENTRY_IN_TRANSITION) != 0,
("vm_map_unwire: in-transition flag missing %p", entry));
KASSERT(entry->wiring_thread == curthread,
("vm_map_unwire: alien wire %p", entry));
entry->eflags &= ~MAP_ENTRY_IN_TRANSITION;
entry->wiring_thread = NULL;
if (entry->eflags & MAP_ENTRY_NEEDS_WAKEUP) {
entry->eflags &= ~MAP_ENTRY_NEEDS_WAKEUP;
need_wakeup = true;
}
vm_map_try_merge_entries(map, prev_entry, entry);
}
vm_map_try_merge_entries(map, prev_entry, entry);
vm_map_unlock(map);
if (need_wakeup)
vm_map_wakeup(map);
return (rv);
}
static void
vm_map_wire_user_count_sub(u_long npages)
{
atomic_subtract_long(&vm_user_wire_count, npages);
}
static bool
vm_map_wire_user_count_add(u_long npages)
{
u_long wired;
wired = vm_user_wire_count;
do {
if (npages + wired > vm_page_max_user_wired)
return (false);
} while (!atomic_fcmpset_long(&vm_user_wire_count, &wired,
npages + wired));
return (true);
}
static void
vm_map_wire_entry_failure(vm_map_t map, vm_map_entry_t entry,
vm_offset_t failed_addr)
{
VM_MAP_ASSERT_LOCKED(map);
KASSERT((entry->eflags & MAP_ENTRY_IN_TRANSITION) != 0 &&
entry->wired_count == 1,
("vm_map_wire_entry_failure: entry %p isn't being wired", entry));
KASSERT(failed_addr < entry->end,
("vm_map_wire_entry_failure: entry %p was fully wired", entry));
if (failed_addr > entry->start) {
pmap_unwire(map->pmap, entry->start, failed_addr);
vm_object_unwire(entry->object.vm_object, entry->offset,
failed_addr - entry->start, PQ_ACTIVE);
}
entry->wired_count = -1;
}
int
vm_map_wire(vm_map_t map, vm_offset_t start, vm_offset_t end, int flags)
{
int rv;
vm_map_lock(map);
rv = vm_map_wire_locked(map, start, end, flags);
vm_map_unlock(map);
return (rv);
}
int
vm_map_wire_locked(vm_map_t map, vm_offset_t start, vm_offset_t end, int flags)
{
vm_map_entry_t entry, first_entry, next_entry, prev_entry;
vm_offset_t faddr, saved_end, saved_start;
u_long incr, npages;
u_int bidx, last_timestamp;
int rv;
bool holes_ok, need_wakeup, user_wire;
vm_prot_t prot;
VM_MAP_ASSERT_LOCKED(map);
if (start == end)
return (KERN_SUCCESS);
prot = 0;
if (flags & VM_MAP_WIRE_WRITE)
prot |= VM_PROT_WRITE;
holes_ok = (flags & VM_MAP_WIRE_HOLESOK) != 0;
user_wire = (flags & VM_MAP_WIRE_USER) != 0;
VM_MAP_RANGE_CHECK(map, start, end);
if (!vm_map_lookup_entry(map, start, &first_entry)) {
if (holes_ok)
first_entry = vm_map_entry_succ(first_entry);
else
return (KERN_INVALID_ADDRESS);
}
for (entry = first_entry; entry->start < end; entry = next_entry) {
if (entry->eflags & MAP_ENTRY_IN_TRANSITION) {
next_entry = vm_map_entry_in_transition(map, start,
&end, holes_ok, entry);
if (next_entry == NULL) {
if (entry == first_entry)
return (KERN_INVALID_ADDRESS);
rv = KERN_INVALID_ADDRESS;
goto done;
}
first_entry = (entry == first_entry) ?
next_entry : NULL;
continue;
}
rv = vm_map_clip_start(map, entry, start);
if (rv != KERN_SUCCESS)
goto done;
rv = vm_map_clip_end(map, entry, end);
if (rv != KERN_SUCCESS)
goto done;
KASSERT((entry->eflags & MAP_ENTRY_IN_TRANSITION) == 0 &&
entry->wiring_thread == NULL,
("owned map entry %p", entry));
entry->eflags |= MAP_ENTRY_IN_TRANSITION;
entry->wiring_thread = curthread;
if ((entry->protection & (VM_PROT_READ | VM_PROT_EXECUTE)) == 0
|| (entry->protection & prot) != prot) {
entry->eflags |= MAP_ENTRY_WIRE_SKIPPED;
if (!holes_ok) {
end = entry->end;
rv = KERN_INVALID_ADDRESS;
goto done;
}
} else if (entry->wired_count == 0) {
entry->wired_count++;
npages = atop(entry->end - entry->start);
if (user_wire && !vm_map_wire_user_count_add(npages)) {
vm_map_wire_entry_failure(map, entry,
entry->start);
end = entry->end;
rv = KERN_RESOURCE_SHORTAGE;
goto done;
}
saved_start = entry->start;
saved_end = entry->end;
last_timestamp = map->timestamp;
bidx = MAP_ENTRY_SPLIT_BOUNDARY_INDEX(entry);
incr = pagesizes[bidx];
vm_map_busy(map);
vm_map_unlock(map);
for (faddr = saved_start; faddr < saved_end;
faddr += incr) {
rv = vm_fault(map, faddr, VM_PROT_NONE,
VM_FAULT_WIRE, NULL);
if (rv != KERN_SUCCESS)
break;
}
vm_map_lock(map);
vm_map_unbusy(map);
if (last_timestamp + 1 != map->timestamp) {
if (!vm_map_lookup_entry(map, saved_start,
&next_entry))
KASSERT(false,
("vm_map_wire: lookup failed"));
first_entry = (entry == first_entry) ?
next_entry : NULL;
for (entry = next_entry; entry->end < saved_end;
entry = vm_map_entry_succ(entry)) {
if (rv != KERN_SUCCESS &&
faddr < entry->end)
vm_map_wire_entry_failure(map,
entry, faddr);
}
}
if (rv != KERN_SUCCESS) {
vm_map_wire_entry_failure(map, entry, faddr);
if (user_wire)
vm_map_wire_user_count_sub(npages);
end = entry->end;
goto done;
}
} else if (!user_wire ||
(entry->eflags & MAP_ENTRY_USER_WIRED) == 0) {
entry->wired_count++;
}
next_entry = vm_map_entry_succ(entry);
if (!holes_ok &&
entry->end < end && next_entry->start > entry->end) {
end = entry->end;
rv = KERN_INVALID_ADDRESS;
goto done;
}
}
rv = KERN_SUCCESS;
done:
need_wakeup = false;
if (first_entry == NULL &&
!vm_map_lookup_entry(map, start, &first_entry)) {
KASSERT(holes_ok, ("vm_map_wire: lookup failed"));
prev_entry = first_entry;
entry = vm_map_entry_succ(first_entry);
} else {
prev_entry = vm_map_entry_pred(first_entry);
entry = first_entry;
}
for (; entry->start < end;
prev_entry = entry, entry = vm_map_entry_succ(entry)) {
if ((entry->eflags & MAP_ENTRY_IN_TRANSITION) == 0 ||
entry->wiring_thread != curthread) {
KASSERT(holes_ok || rv == KERN_INVALID_ARGUMENT,
("vm_map_wire: !HOLESOK and new/changed entry"));
continue;
}
if ((entry->eflags & MAP_ENTRY_WIRE_SKIPPED) != 0) {
} else if (rv == KERN_SUCCESS) {
if (user_wire)
entry->eflags |= MAP_ENTRY_USER_WIRED;
} else if (entry->wired_count == -1) {
entry->wired_count = 0;
} else if (!user_wire ||
(entry->eflags & MAP_ENTRY_USER_WIRED) == 0) {
if (entry->wired_count == 1) {
vm_map_entry_unwire(map, entry);
if (user_wire)
vm_map_wire_user_count_sub(
atop(entry->end - entry->start));
} else
entry->wired_count--;
}
KASSERT((entry->eflags & MAP_ENTRY_IN_TRANSITION) != 0,
("vm_map_wire: in-transition flag missing %p", entry));
KASSERT(entry->wiring_thread == curthread,
("vm_map_wire: alien wire %p", entry));
entry->eflags &= ~(MAP_ENTRY_IN_TRANSITION |
MAP_ENTRY_WIRE_SKIPPED);
entry->wiring_thread = NULL;
if (entry->eflags & MAP_ENTRY_NEEDS_WAKEUP) {
entry->eflags &= ~MAP_ENTRY_NEEDS_WAKEUP;
need_wakeup = true;
}
vm_map_try_merge_entries(map, prev_entry, entry);
}
vm_map_try_merge_entries(map, prev_entry, entry);
if (need_wakeup)
vm_map_wakeup(map);
return (rv);
}
int
vm_map_sync(
vm_map_t map,
vm_offset_t start,
vm_offset_t end,
boolean_t syncio,
boolean_t invalidate)
{
vm_map_entry_t entry, first_entry, next_entry;
vm_size_t size;
vm_object_t object;
vm_ooffset_t offset;
unsigned int last_timestamp;
int bdry_idx;
boolean_t failed;
vm_map_lock_read(map);
VM_MAP_RANGE_CHECK(map, start, end);
if (!vm_map_lookup_entry(map, start, &first_entry)) {
vm_map_unlock_read(map);
return (KERN_INVALID_ADDRESS);
} else if (start == end) {
start = first_entry->start;
end = first_entry->end;
}
for (entry = first_entry; entry->start < end; entry = next_entry) {
if (invalidate) {
if ((entry->eflags & MAP_ENTRY_USER_WIRED) != 0) {
vm_map_unlock_read(map);
return (KERN_INVALID_ARGUMENT);
}
bdry_idx = MAP_ENTRY_SPLIT_BOUNDARY_INDEX(entry);
if (bdry_idx != 0 &&
((start & (pagesizes[bdry_idx] - 1)) != 0 ||
(end & (pagesizes[bdry_idx] - 1)) != 0)) {
vm_map_unlock_read(map);
return (KERN_INVALID_ARGUMENT);
}
}
next_entry = vm_map_entry_succ(entry);
if (end > entry->end &&
entry->end != next_entry->start) {
vm_map_unlock_read(map);
return (KERN_INVALID_ADDRESS);
}
}
if (invalidate)
pmap_remove(map->pmap, start, end);
failed = FALSE;
for (entry = first_entry; entry->start < end;) {
offset = entry->offset + (start - entry->start);
size = (end <= entry->end ? end : entry->end) - start;
if ((entry->eflags & MAP_ENTRY_IS_SUB_MAP) != 0) {
vm_map_t smap;
vm_map_entry_t tentry;
vm_size_t tsize;
smap = entry->object.sub_map;
vm_map_lock_read(smap);
(void) vm_map_lookup_entry(smap, offset, &tentry);
tsize = tentry->end - offset;
if (tsize < size)
size = tsize;
object = tentry->object.vm_object;
offset = tentry->offset + (offset - tentry->start);
vm_map_unlock_read(smap);
} else {
object = entry->object.vm_object;
}
vm_object_reference(object);
last_timestamp = map->timestamp;
vm_map_unlock_read(map);
if (!vm_object_sync(object, offset, size, syncio, invalidate))
failed = TRUE;
start += size;
vm_object_deallocate(object);
vm_map_lock_read(map);
if (last_timestamp == map->timestamp ||
!vm_map_lookup_entry(map, start, &entry))
entry = vm_map_entry_succ(entry);
}
vm_map_unlock_read(map);
return (failed ? KERN_FAILURE : KERN_SUCCESS);
}
static void
vm_map_entry_unwire(vm_map_t map, vm_map_entry_t entry)
{
vm_size_t size;
VM_MAP_ASSERT_LOCKED(map);
KASSERT(entry->wired_count > 0,
("vm_map_entry_unwire: entry %p isn't wired", entry));
size = entry->end - entry->start;
if ((entry->eflags & MAP_ENTRY_USER_WIRED) != 0)
vm_map_wire_user_count_sub(atop(size));
pmap_unwire(map->pmap, entry->start, entry->end);
vm_object_unwire(entry->object.vm_object, entry->offset, size,
PQ_ACTIVE);
entry->wired_count = 0;
}
static void
vm_map_entry_deallocate(vm_map_entry_t entry, boolean_t system_map)
{
if ((entry->eflags & MAP_ENTRY_IS_SUB_MAP) == 0)
vm_object_deallocate(entry->object.vm_object);
uma_zfree(system_map ? kmapentzone : mapentzone, entry);
}
static void
vm_map_entry_delete(vm_map_t map, vm_map_entry_t entry)
{
vm_object_t object;
vm_pindex_t offidxstart, offidxend, size1;
vm_size_t size;
vm_map_entry_unlink(map, entry, UNLINK_MERGE_NONE);
object = entry->object.vm_object;
if ((entry->eflags & MAP_ENTRY_GUARD) != 0) {
MPASS(entry->cred == NULL);
MPASS((entry->eflags & MAP_ENTRY_IS_SUB_MAP) == 0);
MPASS(object == NULL);
vm_map_entry_deallocate(entry, vm_map_is_system(map));
return;
}
size = entry->end - entry->start;
map->size -= size;
if (entry->cred != NULL) {
swap_release_by_cred(size, entry->cred);
crfree(entry->cred);
}
if ((entry->eflags & MAP_ENTRY_IS_SUB_MAP) != 0 || object == NULL) {
entry->object.vm_object = NULL;
} else if ((object->flags & OBJ_ANON) != 0 ||
object == kernel_object) {
KASSERT(entry->cred == NULL || object->cred == NULL ||
(entry->eflags & MAP_ENTRY_NEEDS_COPY),
("OVERCOMMIT vm_map_entry_delete: both cred %p", entry));
offidxstart = OFF_TO_IDX(entry->offset);
offidxend = offidxstart + atop(size);
VM_OBJECT_WLOCK(object);
if (object->ref_count != 1 &&
((object->flags & OBJ_ONEMAPPING) != 0 ||
object == kernel_object)) {
vm_object_collapse(object);
vm_object_page_remove(object, offidxstart, offidxend,
OBJPR_NOTMAPPED);
if (offidxend >= object->size &&
offidxstart < object->size) {
size1 = object->size;
object->size = offidxstart;
if (object->cred != NULL) {
size1 -= object->size;
KASSERT(object->charge >= ptoa(size1),
("object %p charge < 0", object));
swap_release_by_cred(ptoa(size1),
object->cred);
object->charge -= ptoa(size1);
}
}
}
VM_OBJECT_WUNLOCK(object);
}
if (vm_map_is_system(map))
vm_map_entry_deallocate(entry, TRUE);
else {
entry->defer_next = curthread->td_map_def_user;
curthread->td_map_def_user = entry;
}
}
int
vm_map_delete(vm_map_t map, vm_offset_t start, vm_offset_t end)
{
vm_map_entry_t entry, next_entry, scratch_entry;
int rv;
VM_MAP_ASSERT_LOCKED(map);
if (start == end)
return (KERN_SUCCESS);
rv = vm_map_lookup_clip_start(map, start, &entry, &scratch_entry);
if (rv != KERN_SUCCESS)
return (rv);
for (; entry->start < end; entry = next_entry) {
if ((entry->eflags & MAP_ENTRY_IN_TRANSITION) != 0 ||
(vm_map_pmap(map) != kernel_pmap &&
vm_map_entry_system_wired_count(entry) != 0)) {
unsigned int last_timestamp;
vm_offset_t saved_start;
saved_start = entry->start;
entry->eflags |= MAP_ENTRY_NEEDS_WAKEUP;
last_timestamp = map->timestamp;
(void) vm_map_unlock_and_wait(map, 0);
vm_map_lock(map);
if (last_timestamp + 1 != map->timestamp) {
rv = vm_map_lookup_clip_start(map, saved_start,
&next_entry, &scratch_entry);
if (rv != KERN_SUCCESS)
break;
} else
next_entry = entry;
continue;
}
rv = vm_map_clip_end(map, entry, end);
if (rv != KERN_SUCCESS)
break;
next_entry = vm_map_entry_succ(entry);
if (entry->wired_count != 0)
vm_map_entry_unwire(map, entry);
if ((entry->eflags & MAP_ENTRY_IS_SUB_MAP) != 0 ||
entry->object.vm_object != NULL)
pmap_map_delete(map->pmap, entry->start, entry->end);
vm_map_entry_delete(map, entry);
}
return (rv);
}
int
vm_map_remove(vm_map_t map, vm_offset_t start, vm_offset_t end)
{
int result;
vm_map_lock(map);
VM_MAP_RANGE_CHECK(map, start, end);
result = vm_map_delete(map, start, end);
vm_map_unlock(map);
return (result);
}
boolean_t
vm_map_check_protection(vm_map_t map, vm_offset_t start, vm_offset_t end,
vm_prot_t protection)
{
vm_map_entry_t entry;
vm_map_entry_t tmp_entry;
if (!vm_map_lookup_entry(map, start, &tmp_entry))
return (FALSE);
entry = tmp_entry;
while (start < end) {
if (start < entry->start)
return (FALSE);
if ((entry->protection & protection) != protection)
return (FALSE);
start = entry->end;
entry = vm_map_entry_succ(entry);
}
return (TRUE);
}
static void
vm_map_copy_swap_object(vm_map_entry_t src_entry, vm_map_entry_t dst_entry,
vm_offset_t size, vm_ooffset_t *fork_charge)
{
vm_object_t src_object;
struct ucred *cred;
int charged;
src_object = src_entry->object.vm_object;
charged = ENTRY_CHARGED(src_entry);
if ((src_object->flags & OBJ_ANON) != 0) {
VM_OBJECT_WLOCK(src_object);
vm_object_collapse(src_object);
if ((src_object->flags & OBJ_ONEMAPPING) != 0) {
vm_object_split(src_entry);
src_object = src_entry->object.vm_object;
}
vm_object_reference_locked(src_object);
vm_object_clear_flag(src_object, OBJ_ONEMAPPING);
VM_OBJECT_WUNLOCK(src_object);
} else
vm_object_reference(src_object);
if (src_entry->cred != NULL &&
!(src_entry->eflags & MAP_ENTRY_NEEDS_COPY)) {
KASSERT(src_object->cred == NULL,
("OVERCOMMIT: vm_map_copy_anon_entry: cred %p",
src_object));
src_object->cred = src_entry->cred;
src_object->charge = size;
}
dst_entry->object.vm_object = src_object;
if (charged) {
cred = curthread->td_ucred;
crhold(cred);
dst_entry->cred = cred;
*fork_charge += size;
if (!(src_entry->eflags & MAP_ENTRY_NEEDS_COPY)) {
crhold(cred);
src_entry->cred = cred;
*fork_charge += size;
}
}
}
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_ooffset_t *fork_charge)
{
vm_object_t src_object;
vm_map_entry_t fake_entry;
vm_offset_t size;
VM_MAP_ASSERT_LOCKED(dst_map);
if ((dst_entry->eflags|src_entry->eflags) & MAP_ENTRY_IS_SUB_MAP)
return;
if (src_entry->wired_count == 0 ||
(src_entry->protection & VM_PROT_WRITE) == 0) {
if ((src_entry->eflags & MAP_ENTRY_NEEDS_COPY) == 0 &&
(src_entry->protection & VM_PROT_WRITE) != 0) {
pmap_protect(src_map->pmap,
src_entry->start,
src_entry->end,
src_entry->protection & ~VM_PROT_WRITE);
}
size = src_entry->end - src_entry->start;
if ((src_object = src_entry->object.vm_object) != NULL) {
if ((src_object->flags & OBJ_SWAP) != 0) {
vm_map_copy_swap_object(src_entry, dst_entry,
size, fork_charge);
src_object = src_entry->object.vm_object;
} else {
vm_object_reference(src_object);
dst_entry->object.vm_object = src_object;
}
src_entry->eflags |= MAP_ENTRY_COW |
MAP_ENTRY_NEEDS_COPY;
dst_entry->eflags |= MAP_ENTRY_COW |
MAP_ENTRY_NEEDS_COPY;
dst_entry->offset = src_entry->offset;
if (src_entry->eflags & MAP_ENTRY_WRITECNT) {
fake_entry = vm_map_entry_create(dst_map);
fake_entry->eflags = MAP_ENTRY_WRITECNT;
src_entry->eflags &= ~MAP_ENTRY_WRITECNT;
vm_object_reference(src_object);
fake_entry->object.vm_object = src_object;
fake_entry->start = src_entry->start;
fake_entry->end = src_entry->end;
fake_entry->defer_next =
curthread->td_map_def_user;
curthread->td_map_def_user = fake_entry;
}
pmap_copy(dst_map->pmap, src_map->pmap,
dst_entry->start, dst_entry->end - dst_entry->start,
src_entry->start);
} else {
dst_entry->object.vm_object = NULL;
if ((dst_entry->eflags & MAP_ENTRY_GUARD) == 0)
dst_entry->offset = 0;
if (src_entry->cred != NULL) {
dst_entry->cred = curthread->td_ucred;
crhold(dst_entry->cred);
*fork_charge += size;
}
}
} else {
vm_fault_copy_entry(dst_map, src_map, dst_entry, src_entry,
fork_charge);
}
}
static void
vmspace_map_entry_forked(const struct vmspace *vm1, struct vmspace *vm2,
vm_map_entry_t entry)
{
vm_size_t entrysize;
vm_offset_t newend;
if ((entry->eflags & MAP_ENTRY_GUARD) != 0)
return;
entrysize = entry->end - entry->start;
vm2->vm_map.size += entrysize;
if ((entry->eflags & MAP_ENTRY_GROWS_DOWN) != 0) {
vm2->vm_ssize += btoc(entrysize);
} else if (entry->start >= (vm_offset_t)vm1->vm_daddr &&
entry->start < (vm_offset_t)vm1->vm_daddr + ctob(vm1->vm_dsize)) {
newend = MIN(entry->end,
(vm_offset_t)vm1->vm_daddr + ctob(vm1->vm_dsize));
vm2->vm_dsize += btoc(newend - entry->start);
} else if (entry->start >= (vm_offset_t)vm1->vm_taddr &&
entry->start < (vm_offset_t)vm1->vm_taddr + ctob(vm1->vm_tsize)) {
newend = MIN(entry->end,
(vm_offset_t)vm1->vm_taddr + ctob(vm1->vm_tsize));
vm2->vm_tsize += btoc(newend - entry->start);
}
}
struct vmspace *
vmspace_fork(struct vmspace *vm1, vm_ooffset_t *fork_charge)
{
struct vmspace *vm2;
vm_map_t new_map, old_map;
vm_map_entry_t new_entry, old_entry;
vm_object_t object;
int error, locked __diagused;
vm_inherit_t inh;
old_map = &vm1->vm_map;
vm2 = vmspace_alloc(vm_map_min(old_map), vm_map_max(old_map),
pmap_pinit);
if (vm2 == NULL)
return (NULL);
vm2->vm_taddr = vm1->vm_taddr;
vm2->vm_daddr = vm1->vm_daddr;
vm2->vm_maxsaddr = vm1->vm_maxsaddr;
vm2->vm_stacktop = vm1->vm_stacktop;
vm2->vm_shp_base = vm1->vm_shp_base;
vm_map_lock(old_map);
if (old_map->busy)
vm_map_wait_busy(old_map);
new_map = &vm2->vm_map;
locked = vm_map_trylock(new_map);
KASSERT(locked, ("vmspace_fork: lock failed"));
error = pmap_vmspace_copy(new_map->pmap, old_map->pmap);
if (error != 0) {
sx_xunlock(&old_map->lock);
sx_xunlock(&new_map->lock);
vm_map_process_deferred();
vmspace_free(vm2);
return (NULL);
}
new_map->anon_loc = old_map->anon_loc;
new_map->flags |= old_map->flags & (MAP_ASLR | MAP_ASLR_IGNSTART |
MAP_ASLR_STACK | MAP_WXORX);
VM_MAP_ENTRY_FOREACH(old_entry, old_map) {
if ((old_entry->eflags & MAP_ENTRY_IS_SUB_MAP) != 0)
panic("vm_map_fork: encountered a submap");
inh = old_entry->inheritance;
if ((old_entry->eflags & MAP_ENTRY_GUARD) != 0 &&
inh != VM_INHERIT_NONE)
inh = VM_INHERIT_COPY;
switch (inh) {
case VM_INHERIT_NONE:
break;
case VM_INHERIT_SHARE:
object = old_entry->object.vm_object;
if (object == NULL) {
vm_map_entry_back(old_entry);
object = old_entry->object.vm_object;
}
vm_object_reference(object);
if (old_entry->eflags & MAP_ENTRY_NEEDS_COPY) {
vm_object_shadow(&old_entry->object.vm_object,
&old_entry->offset,
old_entry->end - old_entry->start,
old_entry->cred,
true);
old_entry->eflags &= ~MAP_ENTRY_NEEDS_COPY;
old_entry->cred = NULL;
vm_object_deallocate(object);
object = old_entry->object.vm_object;
} else {
VM_OBJECT_WLOCK(object);
vm_object_clear_flag(object, OBJ_ONEMAPPING);
if (old_entry->cred != NULL) {
KASSERT(object->cred == NULL,
("vmspace_fork both cred"));
object->cred = old_entry->cred;
object->charge = old_entry->end -
old_entry->start;
old_entry->cred = NULL;
}
if (old_entry->eflags & MAP_ENTRY_WRITECNT &&
object->type == OBJT_VNODE) {
KASSERT(((struct vnode *)object->
handle)->v_writecount > 0,
("vmspace_fork: v_writecount %p",
object));
KASSERT(object->un_pager.vnp.
writemappings > 0,
("vmspace_fork: vnp.writecount %p",
object));
}
VM_OBJECT_WUNLOCK(object);
}
new_entry = vm_map_entry_create(new_map);
*new_entry = *old_entry;
new_entry->eflags &= ~(MAP_ENTRY_USER_WIRED |
MAP_ENTRY_IN_TRANSITION);
new_entry->wiring_thread = NULL;
new_entry->wired_count = 0;
if (new_entry->eflags & MAP_ENTRY_WRITECNT) {
vm_pager_update_writecount(object,
new_entry->start, new_entry->end);
}
vm_map_entry_set_vnode_text(new_entry, true);
vm_map_entry_link(new_map, new_entry);
vmspace_map_entry_forked(vm1, vm2, new_entry);
pmap_copy(new_map->pmap, old_map->pmap,
new_entry->start,
(old_entry->end - old_entry->start),
old_entry->start);
break;
case VM_INHERIT_COPY:
new_entry = vm_map_entry_create(new_map);
*new_entry = *old_entry;
new_entry->eflags &= ~(MAP_ENTRY_USER_WIRED |
MAP_ENTRY_IN_TRANSITION | MAP_ENTRY_WRITECNT);
new_entry->wiring_thread = NULL;
new_entry->wired_count = 0;
new_entry->object.vm_object = NULL;
new_entry->cred = NULL;
vm_map_entry_link(new_map, new_entry);
vmspace_map_entry_forked(vm1, vm2, new_entry);
vm_map_copy_entry(old_map, new_map, old_entry,
new_entry, fork_charge);
vm_map_entry_set_vnode_text(new_entry, true);
break;
case VM_INHERIT_ZERO:
new_entry = vm_map_entry_create(new_map);
memset(new_entry, 0, sizeof(*new_entry));
new_entry->start = old_entry->start;
new_entry->end = old_entry->end;
new_entry->eflags = old_entry->eflags &
~(MAP_ENTRY_USER_WIRED | MAP_ENTRY_IN_TRANSITION |
MAP_ENTRY_WRITECNT | MAP_ENTRY_VN_EXEC |
MAP_ENTRY_SPLIT_BOUNDARY_MASK);
new_entry->protection = old_entry->protection;
new_entry->max_protection = old_entry->max_protection;
new_entry->inheritance = VM_INHERIT_ZERO;
vm_map_entry_link(new_map, new_entry);
vmspace_map_entry_forked(vm1, vm2, new_entry);
new_entry->cred = curthread->td_ucred;
crhold(new_entry->cred);
*fork_charge += (new_entry->end - new_entry->start);
break;
}
}
sx_xunlock(&old_map->lock);
sx_xunlock(&new_map->lock);
vm_map_process_deferred();
return (vm2);
}
int
vm_map_stack(vm_map_t map, vm_offset_t addrbos, vm_size_t max_ssize,
vm_prot_t prot, vm_prot_t max, int cow)
{
vm_size_t growsize, init_ssize;
rlim_t vmemlim;
int rv;
MPASS((map->flags & MAP_WIREFUTURE) == 0);
growsize = sgrowsiz;
init_ssize = (max_ssize < growsize) ? max_ssize : growsize;
vm_map_lock(map);
vmemlim = lim_cur(curthread, RLIMIT_VMEM);
if (map->size + init_ssize > vmemlim) {
rv = KERN_NO_SPACE;
goto out;
}
rv = vm_map_stack_locked(map, addrbos, max_ssize, growsize, prot,
max, cow);
out:
vm_map_unlock(map);
return (rv);
}
static int stack_guard_page = 1;
SYSCTL_INT(_security_bsd, OID_AUTO, stack_guard_page, CTLFLAG_RWTUN,
&stack_guard_page, 0,
"Specifies the number of guard pages for a stack that grows");
static int
vm_map_stack_locked(vm_map_t map, vm_offset_t addrbos, vm_size_t max_ssize,
vm_size_t growsize, vm_prot_t prot, vm_prot_t max, int cow)
{
vm_map_entry_t gap_entry, new_entry, prev_entry;
vm_offset_t bot, gap_bot, gap_top, top;
vm_size_t init_ssize, sgp;
int rv;
KASSERT((cow & MAP_STACK_AREA) != 0,
("New mapping is not a stack"));
if (max_ssize == 0 ||
!vm_map_range_valid(map, addrbos, addrbos + max_ssize))
return (KERN_INVALID_ADDRESS);
sgp = ((curproc->p_flag2 & P2_STKGAP_DISABLE) != 0 ||
(curproc->p_fctl0 & NT_FREEBSD_FCTL_STKGAP_DISABLE) != 0) ? 0 :
(vm_size_t)stack_guard_page * PAGE_SIZE;
if (sgp >= max_ssize)
return (KERN_INVALID_ARGUMENT);
init_ssize = growsize;
if (max_ssize < init_ssize + sgp)
init_ssize = max_ssize - sgp;
if (vm_map_lookup_entry(map, addrbos, &prev_entry))
return (KERN_NO_SPACE);
if (vm_map_entry_succ(prev_entry)->start < addrbos + max_ssize)
return (KERN_NO_SPACE);
bot = addrbos + max_ssize - init_ssize;
top = bot + init_ssize;
gap_bot = addrbos;
gap_top = bot;
rv = vm_map_insert1(map, NULL, 0, bot, top, prot, max, cow,
&new_entry);
if (rv != KERN_SUCCESS)
return (rv);
KASSERT(new_entry->end == top || new_entry->start == bot,
("Bad entry start/end for new stack entry"));
KASSERT((new_entry->eflags & MAP_ENTRY_GROWS_DOWN) != 0,
("new entry lacks MAP_ENTRY_GROWS_DOWN"));
if (gap_bot == gap_top)
return (KERN_SUCCESS);
rv = vm_map_insert1(map, NULL, 0, gap_bot, gap_top, VM_PROT_NONE,
VM_PROT_NONE, MAP_CREATE_GUARD | MAP_CREATE_STACK_GAP,
&gap_entry);
if (rv == KERN_SUCCESS) {
KASSERT((gap_entry->eflags & MAP_ENTRY_GUARD) != 0,
("entry %p not gap %#x", gap_entry, gap_entry->eflags));
KASSERT((gap_entry->eflags & MAP_ENTRY_STACK_GAP) != 0,
("entry %p not stack gap %#x", gap_entry,
gap_entry->eflags));
gap_entry->next_read = sgp;
gap_entry->offset = prot | PROT_MAX(max);
} else {
(void)vm_map_delete(map, bot, top);
}
return (rv);
}
static int
vm_map_growstack(vm_map_t map, vm_offset_t addr, vm_map_entry_t gap_entry)
{
vm_map_entry_t stack_entry;
struct proc *p;
struct vmspace *vm;
vm_offset_t gap_end, gap_start, grow_start;
vm_size_t grow_amount, guard, max_grow, sgp;
vm_prot_t prot, max;
rlim_t lmemlim, stacklim, vmemlim;
int rv, rv1 __diagused;
bool gap_deleted, is_procstack;
#ifdef notyet
uint64_t limit;
#endif
#ifdef RACCT
int error __diagused;
#endif
p = curproc;
vm = p->p_vmspace;
if (p != initproc && (map != &p->p_vmspace->vm_map ||
p->p_textvp == NULL))
return (KERN_FAILURE);
MPASS(!vm_map_is_system(map));
lmemlim = lim_cur(curthread, RLIMIT_MEMLOCK);
stacklim = lim_cur(curthread, RLIMIT_STACK);
vmemlim = lim_cur(curthread, RLIMIT_VMEM);
retry:
if (gap_entry == NULL && !vm_map_lookup_entry(map, addr, &gap_entry))
return (KERN_FAILURE);
if ((gap_entry->eflags & MAP_ENTRY_GUARD) == 0)
return (KERN_SUCCESS);
if ((gap_entry->eflags & MAP_ENTRY_STACK_GAP) != 0) {
stack_entry = vm_map_entry_succ(gap_entry);
if ((stack_entry->eflags & MAP_ENTRY_GROWS_DOWN) == 0 ||
stack_entry->start != gap_entry->end)
return (KERN_FAILURE);
grow_amount = round_page(stack_entry->start - addr);
} else {
return (KERN_FAILURE);
}
guard = ((curproc->p_flag2 & P2_STKGAP_DISABLE) != 0 ||
(curproc->p_fctl0 & NT_FREEBSD_FCTL_STKGAP_DISABLE) != 0) ? 0 :
gap_entry->next_read;
max_grow = gap_entry->end - gap_entry->start;
if (guard > max_grow)
return (KERN_NO_SPACE);
max_grow -= guard;
if (grow_amount > max_grow)
return (KERN_NO_SPACE);
is_procstack = addr >= (vm_offset_t)vm->vm_maxsaddr &&
addr < (vm_offset_t)vm->vm_stacktop;
if (is_procstack && (ctob(vm->vm_ssize) + grow_amount > stacklim))
return (KERN_NO_SPACE);
#ifdef RACCT
if (racct_enable) {
PROC_LOCK(p);
if (is_procstack && racct_set(p, RACCT_STACK,
ctob(vm->vm_ssize) + grow_amount)) {
PROC_UNLOCK(p);
return (KERN_NO_SPACE);
}
PROC_UNLOCK(p);
}
#endif
grow_amount = roundup(grow_amount, sgrowsiz);
if (grow_amount > max_grow)
grow_amount = max_grow;
if (is_procstack && (ctob(vm->vm_ssize) + grow_amount > stacklim)) {
grow_amount = trunc_page((vm_size_t)stacklim) -
ctob(vm->vm_ssize);
}
#ifdef notyet
PROC_LOCK(p);
limit = racct_get_available(p, RACCT_STACK);
PROC_UNLOCK(p);
if (is_procstack && (ctob(vm->vm_ssize) + grow_amount > limit))
grow_amount = limit - ctob(vm->vm_ssize);
#endif
if (!old_mlock && (map->flags & MAP_WIREFUTURE) != 0) {
if (ptoa(pmap_wired_count(map->pmap)) + grow_amount > lmemlim) {
rv = KERN_NO_SPACE;
goto out;
}
#ifdef RACCT
if (racct_enable) {
PROC_LOCK(p);
if (racct_set(p, RACCT_MEMLOCK,
ptoa(pmap_wired_count(map->pmap)) + grow_amount)) {
PROC_UNLOCK(p);
rv = KERN_NO_SPACE;
goto out;
}
PROC_UNLOCK(p);
}
#endif
}
if (map->size + grow_amount > vmemlim) {
rv = KERN_NO_SPACE;
goto out;
}
#ifdef RACCT
if (racct_enable) {
PROC_LOCK(p);
if (racct_set(p, RACCT_VMEM, map->size + grow_amount)) {
PROC_UNLOCK(p);
rv = KERN_NO_SPACE;
goto out;
}
PROC_UNLOCK(p);
}
#endif
if (vm_map_lock_upgrade(map)) {
gap_entry = NULL;
vm_map_lock_read(map);
goto retry;
}
prot = PROT_EXTRACT(gap_entry->offset);
max = PROT_MAX_EXTRACT(gap_entry->offset);
sgp = gap_entry->next_read;
grow_start = gap_entry->end - grow_amount;
if (gap_entry->start + grow_amount == gap_entry->end) {
gap_start = gap_entry->start;
gap_end = gap_entry->end;
vm_map_entry_delete(map, gap_entry);
gap_deleted = true;
} else {
MPASS(gap_entry->start < gap_entry->end - grow_amount);
vm_map_entry_resize(map, gap_entry, -grow_amount);
gap_deleted = false;
}
rv = vm_map_insert(map, NULL, 0, grow_start,
grow_start + grow_amount, prot, max, MAP_STACK_AREA);
if (rv != KERN_SUCCESS) {
if (gap_deleted) {
rv1 = vm_map_insert1(map, NULL, 0, gap_start,
gap_end, VM_PROT_NONE, VM_PROT_NONE,
MAP_CREATE_GUARD | MAP_CREATE_STACK_GAP,
&gap_entry);
MPASS(rv1 == KERN_SUCCESS);
gap_entry->next_read = sgp;
gap_entry->offset = prot | PROT_MAX(max);
} else {
vm_map_entry_resize(map, gap_entry,
grow_amount);
}
}
if (rv == KERN_SUCCESS && is_procstack)
vm->vm_ssize += btoc(grow_amount);
if (rv == KERN_SUCCESS && (map->flags & MAP_WIREFUTURE) != 0) {
rv = vm_map_wire_locked(map, grow_start,
grow_start + grow_amount,
VM_MAP_WIRE_USER | VM_MAP_WIRE_NOHOLES);
}
vm_map_lock_downgrade(map);
out:
#ifdef RACCT
if (racct_enable && rv != KERN_SUCCESS) {
PROC_LOCK(p);
error = racct_set(p, RACCT_VMEM, map->size);
KASSERT(error == 0, ("decreasing RACCT_VMEM failed"));
if (!old_mlock) {
error = racct_set(p, RACCT_MEMLOCK,
ptoa(pmap_wired_count(map->pmap)));
KASSERT(error == 0, ("decreasing RACCT_MEMLOCK failed"));
}
error = racct_set(p, RACCT_STACK, ctob(vm->vm_ssize));
KASSERT(error == 0, ("decreasing RACCT_STACK failed"));
PROC_UNLOCK(p);
}
#endif
return (rv);
}
int
vmspace_exec(struct proc *p, vm_offset_t minuser, vm_offset_t maxuser)
{
struct vmspace *oldvmspace = p->p_vmspace;
struct vmspace *newvmspace;
KASSERT((curthread->td_pflags & TDP_EXECVMSPC) == 0,
("vmspace_exec recursed"));
newvmspace = vmspace_alloc(minuser, maxuser, pmap_pinit);
if (newvmspace == NULL)
return (ENOMEM);
newvmspace->vm_swrss = oldvmspace->vm_swrss;
PROC_VMSPACE_LOCK(p);
p->p_vmspace = newvmspace;
PROC_VMSPACE_UNLOCK(p);
if (p == curthread->td_proc)
pmap_activate(curthread);
curthread->td_pflags |= TDP_EXECVMSPC;
return (0);
}
int
vmspace_unshare(struct proc *p)
{
struct vmspace *oldvmspace = p->p_vmspace;
struct vmspace *newvmspace;
vm_ooffset_t fork_charge;
if (refcount_load(&oldvmspace->vm_refcnt) == 1)
return (0);
fork_charge = 0;
newvmspace = vmspace_fork(oldvmspace, &fork_charge);
if (newvmspace == NULL)
return (ENOMEM);
if (!swap_reserve_by_cred(fork_charge, p->p_ucred)) {
vmspace_free(newvmspace);
return (ENOMEM);
}
PROC_VMSPACE_LOCK(p);
p->p_vmspace = newvmspace;
PROC_VMSPACE_UNLOCK(p);
if (p == curthread->td_proc)
pmap_activate(curthread);
vmspace_free(oldvmspace);
return (0);
}
int
vm_map_lookup(vm_map_t *var_map,
vm_offset_t vaddr,
vm_prot_t fault_typea,
vm_map_entry_t *out_entry,
vm_object_t *object,
vm_pindex_t *pindex,
vm_prot_t *out_prot,
boolean_t *wired)
{
vm_map_entry_t entry;
vm_map_t map = *var_map;
vm_prot_t prot;
vm_prot_t fault_type;
vm_object_t eobject;
vm_size_t size;
struct ucred *cred;
RetryLookup:
vm_map_lock_read(map);
RetryLookupLocked:
if (!vm_map_lookup_entry(map, vaddr, out_entry)) {
vm_map_unlock_read(map);
return (KERN_INVALID_ADDRESS);
}
entry = *out_entry;
if (entry->eflags & MAP_ENTRY_IS_SUB_MAP) {
vm_map_t old_map = map;
*var_map = map = entry->object.sub_map;
vm_map_unlock_read(old_map);
goto RetryLookup;
}
prot = entry->protection;
if ((fault_typea & VM_PROT_FAULT_LOOKUP) != 0) {
fault_typea &= ~VM_PROT_FAULT_LOOKUP;
if (prot == VM_PROT_NONE && map != kernel_map &&
(entry->eflags & MAP_ENTRY_GUARD) != 0 &&
(entry->eflags & MAP_ENTRY_STACK_GAP) != 0 &&
vm_map_growstack(map, vaddr, entry) == KERN_SUCCESS)
goto RetryLookupLocked;
}
fault_type = fault_typea & VM_PROT_ALL;
if ((fault_type & prot) != fault_type || prot == VM_PROT_NONE) {
vm_map_unlock_read(map);
return (KERN_PROTECTION_FAILURE);
}
KASSERT((prot & VM_PROT_WRITE) == 0 || (entry->eflags &
(MAP_ENTRY_USER_WIRED | MAP_ENTRY_NEEDS_COPY)) !=
(MAP_ENTRY_USER_WIRED | MAP_ENTRY_NEEDS_COPY),
("entry %p flags %x", entry, entry->eflags));
if ((fault_typea & VM_PROT_COPY) != 0 &&
(entry->max_protection & VM_PROT_WRITE) == 0 &&
(entry->eflags & MAP_ENTRY_COW) == 0) {
vm_map_unlock_read(map);
return (KERN_PROTECTION_FAILURE);
}
*wired = (entry->wired_count != 0);
if (*wired)
fault_type = entry->protection;
size = entry->end - entry->start;
if (entry->eflags & MAP_ENTRY_NEEDS_COPY) {
if ((fault_type & VM_PROT_WRITE) != 0 ||
(fault_typea & VM_PROT_COPY) != 0) {
if (vm_map_lock_upgrade(map))
goto RetryLookup;
if (entry->cred == NULL) {
cred = curthread->td_ucred;
crhold(cred);
if (!swap_reserve_by_cred(size, cred)) {
crfree(cred);
vm_map_unlock(map);
return (KERN_RESOURCE_SHORTAGE);
}
entry->cred = cred;
}
eobject = entry->object.vm_object;
vm_object_shadow(&entry->object.vm_object,
&entry->offset, size, entry->cred, false);
if (eobject == entry->object.vm_object) {
swap_release_by_cred(size, entry->cred);
crfree(entry->cred);
}
entry->cred = NULL;
entry->eflags &= ~MAP_ENTRY_NEEDS_COPY;
vm_map_lock_downgrade(map);
} else {
prot &= ~VM_PROT_WRITE;
}
}
if (entry->object.vm_object == NULL && !vm_map_is_system(map)) {
if (vm_map_lock_upgrade(map))
goto RetryLookup;
entry->object.vm_object = vm_object_allocate_anon(atop(size),
NULL, entry->cred, size);
entry->offset = 0;
entry->cred = NULL;
vm_map_lock_downgrade(map);
}
*pindex = OFF_TO_IDX((vaddr - entry->start) + entry->offset);
*object = entry->object.vm_object;
*out_prot = prot;
return (KERN_SUCCESS);
}
int
vm_map_lookup_locked(vm_map_t *var_map,
vm_offset_t vaddr,
vm_prot_t fault_typea,
vm_map_entry_t *out_entry,
vm_object_t *object,
vm_pindex_t *pindex,
vm_prot_t *out_prot,
boolean_t *wired)
{
vm_map_entry_t entry;
vm_map_t map = *var_map;
vm_prot_t prot;
vm_prot_t fault_type = fault_typea;
if (!vm_map_lookup_entry(map, vaddr, out_entry))
return (KERN_INVALID_ADDRESS);
entry = *out_entry;
if (entry->eflags & MAP_ENTRY_IS_SUB_MAP)
return (KERN_FAILURE);
prot = entry->protection;
fault_type &= VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE;
if ((fault_type & prot) != fault_type)
return (KERN_PROTECTION_FAILURE);
*wired = (entry->wired_count != 0);
if (*wired)
fault_type = entry->protection;
if (entry->eflags & MAP_ENTRY_NEEDS_COPY) {
if (fault_type & VM_PROT_WRITE)
return (KERN_FAILURE);
prot &= ~VM_PROT_WRITE;
}
if (entry->object.vm_object == NULL && !vm_map_is_system(map))
return (KERN_FAILURE);
*pindex = OFF_TO_IDX((vaddr - entry->start) + entry->offset);
*object = entry->object.vm_object;
*out_prot = prot;
return (KERN_SUCCESS);
}
void
vm_map_lookup_done(vm_map_t map, vm_map_entry_t entry)
{
vm_map_unlock_read(map);
}
vm_offset_t
vm_map_max_KBI(const struct vm_map *map)
{
return (vm_map_max(map));
}
vm_offset_t
vm_map_min_KBI(const struct vm_map *map)
{
return (vm_map_min(map));
}
pmap_t
vm_map_pmap_KBI(vm_map_t map)
{
return (map->pmap);
}
bool
vm_map_range_valid_KBI(vm_map_t map, vm_offset_t start, vm_offset_t end)
{
return (vm_map_range_valid(map, start, end));
}
#ifdef INVARIANTS
static void
_vm_map_assert_consistent(vm_map_t map, int check)
{
vm_map_entry_t entry, prev;
vm_map_entry_t cur, header, lbound, ubound;
vm_size_t max_left, max_right;
#ifdef DIAGNOSTIC
++map->nupdates;
#endif
if (enable_vmmap_check != check)
return;
header = prev = &map->header;
VM_MAP_ENTRY_FOREACH(entry, map) {
KASSERT(prev->end <= entry->start,
("map %p prev->end = %jx, start = %jx", map,
(uintmax_t)prev->end, (uintmax_t)entry->start));
KASSERT(entry->start < entry->end,
("map %p start = %jx, end = %jx", map,
(uintmax_t)entry->start, (uintmax_t)entry->end));
KASSERT(entry->left == header ||
entry->left->start < entry->start,
("map %p left->start = %jx, start = %jx", map,
(uintmax_t)entry->left->start, (uintmax_t)entry->start));
KASSERT(entry->right == header ||
entry->start < entry->right->start,
("map %p start = %jx, right->start = %jx", map,
(uintmax_t)entry->start, (uintmax_t)entry->right->start));
cur = map->root;
lbound = ubound = header;
for (;;) {
if (entry->start < cur->start) {
ubound = cur;
cur = cur->left;
KASSERT(cur != lbound,
("map %p cannot find %jx",
map, (uintmax_t)entry->start));
} else if (cur->end <= entry->start) {
lbound = cur;
cur = cur->right;
KASSERT(cur != ubound,
("map %p cannot find %jx",
map, (uintmax_t)entry->start));
} else {
KASSERT(cur == entry,
("map %p cannot find %jx",
map, (uintmax_t)entry->start));
break;
}
}
max_left = vm_map_entry_max_free_left(entry, lbound);
max_right = vm_map_entry_max_free_right(entry, ubound);
KASSERT(entry->max_free == vm_size_max(max_left, max_right),
("map %p max = %jx, max_left = %jx, max_right = %jx", map,
(uintmax_t)entry->max_free,
(uintmax_t)max_left, (uintmax_t)max_right));
prev = entry;
}
KASSERT(prev->end <= entry->start,
("map %p prev->end = %jx, start = %jx", map,
(uintmax_t)prev->end, (uintmax_t)entry->start));
}
#endif
#include "opt_ddb.h"
#ifdef DDB
#include <sys/kernel.h>
#include <ddb/ddb.h>
static void
vm_map_print(vm_map_t map)
{
vm_map_entry_t entry, prev;
db_iprintf("Task map %p: pmap=%p, nentries=%d, version=%u\n",
(void *)map,
(void *)map->pmap, map->nentries, map->timestamp);
db_indent += 2;
prev = &map->header;
VM_MAP_ENTRY_FOREACH(entry, map) {
db_iprintf("map entry %p: start=%p, end=%p, eflags=%#x, \n",
(void *)entry, (void *)entry->start, (void *)entry->end,
entry->eflags);
{
static const char * const 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");
}
if (entry->eflags & MAP_ENTRY_IS_SUB_MAP) {
db_printf(", share=%p, offset=0x%jx\n",
(void *)entry->object.sub_map,
(uintmax_t)entry->offset);
if (prev == &map->header ||
prev->object.sub_map !=
entry->object.sub_map) {
db_indent += 2;
vm_map_print((vm_map_t)entry->object.sub_map);
db_indent -= 2;
}
} else {
if (entry->cred != NULL)
db_printf(", ruid %d", entry->cred->cr_ruid);
db_printf(", object=%p, offset=0x%jx",
(void *)entry->object.vm_object,
(uintmax_t)entry->offset);
if (entry->object.vm_object && entry->object.vm_object->cred)
db_printf(", obj ruid %d charge %jx",
entry->object.vm_object->cred->cr_ruid,
(uintmax_t)entry->object.vm_object->charge);
if (entry->eflags & MAP_ENTRY_COW)
db_printf(", copy (%s)",
(entry->eflags & MAP_ENTRY_NEEDS_COPY) ? "needed" : "done");
db_printf("\n");
if (prev == &map->header ||
prev->object.vm_object !=
entry->object.vm_object) {
db_indent += 2;
vm_object_print((db_expr_t)(intptr_t)
entry->object.vm_object,
0, 0, (char *)0);
db_indent -= 2;
}
}
prev = entry;
}
db_indent -= 2;
}
DB_SHOW_COMMAND(map, map)
{
if (!have_addr) {
db_printf("usage: show map <addr>\n");
return;
}
vm_map_print((vm_map_t)addr);
}
DB_SHOW_COMMAND(procvm, procvm)
{
struct proc *p;
if (have_addr) {
p = db_lookup_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((vm_map_t)&p->p_vmspace->vm_map);
}
#endif