#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/thread.h>
#include <sys/vnode.h>
#include <sys/vmmeter.h>
#include <sys/mman.h>
#include <sys/mount.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/sysctl.h>
#include <sys/refcount.h>
#include <vm/vm.h>
#include <vm/vm_param.h>
#include <vm/pmap.h>
#include <vm/vm_map.h>
#include <vm/vm_object.h>
#include <vm/vm_page.h>
#include <vm/vm_pageout.h>
#include <vm/vm_pager.h>
#include <vm/swap_pager.h>
#include <vm/vm_kern.h>
#include <vm/vm_extern.h>
#include <vm/vm_zone.h>
#include <vm/vm_page2.h>
#include <machine/specialreg.h>
#define EASY_SCAN_FACTOR 8
static void vm_object_page_collect_flush(vm_object_t object, vm_page_t p,
int pagerflags);
static void vm_object_lock_init(vm_object_t);
static struct vm_object kernel_object_store;
struct vm_object *kernel_object = &kernel_object_store;
struct vm_object_hash vm_object_hash[VMOBJ_HSIZE];
static MALLOC_DEFINE_OBJ(M_VM_OBJECT, sizeof(struct vm_object),
"vm_object", "vm_object structures");
#define VMOBJ_HASH_PRIME1 66555444443333333ULL
#define VMOBJ_HASH_PRIME2 989042931893ULL
int vm_object_debug;
SYSCTL_INT(_vm, OID_AUTO, object_debug, CTLFLAG_RW, &vm_object_debug, 0, "");
static __inline
struct vm_object_hash *
vmobj_hash(vm_object_t obj)
{
uintptr_t hash1;
uintptr_t hash2;
hash1 = (uintptr_t)obj + ((uintptr_t)obj >> 18);
hash1 %= VMOBJ_HASH_PRIME1;
hash2 = ((uintptr_t)obj >> 8) + ((uintptr_t)obj >> 24);
hash2 %= VMOBJ_HASH_PRIME2;
return (&vm_object_hash[(hash1 ^ hash2) & VMOBJ_HMASK]);
}
#if defined(DEBUG_LOCKS)
#define vm_object_vndeallocate(obj, vpp) \
debugvm_object_vndeallocate(obj, vpp, __FILE__, __LINE__)
static void
debugvm_object_add(vm_object_t obj, char *file, int line, int addrem)
{
int i;
i = atomic_fetchadd_int(&obj->debug_index, 1);
i = i & (VMOBJ_DEBUG_ARRAY_SIZE - 1);
ksnprintf(obj->debug_hold_thrs[i],
sizeof(obj->debug_hold_thrs[i]),
"%c%d:(%d):%s",
(addrem == -1 ? '-' : (addrem == 1 ? '+' : '=')),
(curthread->td_proc ? curthread->td_proc->p_pid : -1),
obj->ref_count,
curthread->td_comm);
obj->debug_hold_file[i] = file;
obj->debug_hold_line[i] = line;
#if 0
if (strcmp(curthread->td_comm, "sshd") == 0) {
kprintf("%d %p refs=%d ar=%d file: %s/%d\n",
(curthread->td_proc ? curthread->td_proc->p_pid : -1),
obj, obj->ref_count, addrem, file, line);
}
#endif
}
#endif
static void
vm_object_lock_init(vm_object_t obj)
{
#if defined(DEBUG_LOCKS)
int i;
obj->debug_index = 0;
for (i = 0; i < VMOBJ_DEBUG_ARRAY_SIZE; i++) {
obj->debug_hold_thrs[i][0] = 0;
obj->debug_hold_file[i] = NULL;
obj->debug_hold_line[i] = 0;
}
#endif
}
void
vm_object_lock_swap(void)
{
lwkt_token_swap();
}
void
vm_object_lock(vm_object_t obj)
{
lwkt_gettoken(&obj->token);
}
static int
vm_object_lock_try(vm_object_t obj)
{
return(lwkt_trytoken(&obj->token));
}
void
vm_object_lock_shared(vm_object_t obj)
{
lwkt_gettoken_shared(&obj->token);
}
void
vm_object_unlock(vm_object_t obj)
{
lwkt_reltoken(&obj->token);
}
void
vm_object_upgrade(vm_object_t obj)
{
lwkt_reltoken(&obj->token);
lwkt_gettoken(&obj->token);
}
void
vm_object_downgrade(vm_object_t obj)
{
lwkt_reltoken(&obj->token);
lwkt_gettoken_shared(&obj->token);
}
static __inline void
vm_object_assert_held(vm_object_t obj)
{
ASSERT_LWKT_TOKEN_HELD(&obj->token);
}
int
vm_quickcolor(void)
{
globaldata_t gd = mycpu;
int pg_color;
pg_color = (int)(intptr_t)gd->gd_curthread >> 10;
pg_color += gd->gd_quick_color;
gd->gd_quick_color += PQ_PRIME2;
return pg_color;
}
void
VMOBJDEBUG(vm_object_hold)(vm_object_t obj VMOBJDBARGS)
{
KKASSERT(obj != NULL);
refcount_acquire(&obj->hold_count);
vm_object_lock(obj);
#if defined(DEBUG_LOCKS)
debugvm_object_add(obj, file, line, 1);
#endif
}
int
VMOBJDEBUG(vm_object_hold_try)(vm_object_t obj VMOBJDBARGS)
{
KKASSERT(obj != NULL);
refcount_acquire(&obj->hold_count);
if (vm_object_lock_try(obj) == 0) {
if (refcount_release(&obj->hold_count)) {
if (obj->ref_count == 0 && (obj->flags & OBJ_DEAD))
kfree_obj(obj, M_VM_OBJECT);
}
return(0);
}
#if defined(DEBUG_LOCKS)
debugvm_object_add(obj, file, line, 1);
#endif
return(1);
}
void
VMOBJDEBUG(vm_object_hold_shared)(vm_object_t obj VMOBJDBARGS)
{
KKASSERT(obj != NULL);
refcount_acquire(&obj->hold_count);
vm_object_lock_shared(obj);
#if defined(DEBUG_LOCKS)
debugvm_object_add(obj, file, line, 1);
#endif
}
void
VMOBJDEBUG(vm_object_drop)(vm_object_t obj VMOBJDBARGS)
{
if (obj == NULL)
return;
KKASSERT(obj->hold_count > 0);
if (refcount_release(&obj->hold_count)) {
#if defined(DEBUG_LOCKS)
debugvm_object_add(obj, file, line, -1);
#endif
if (obj->ref_count == 0 && (obj->flags & OBJ_DEAD)) {
vm_object_unlock(obj);
kfree_obj(obj, M_VM_OBJECT);
} else {
vm_object_unlock(obj);
}
} else {
#if defined(DEBUG_LOCKS)
debugvm_object_add(obj, file, line, -1);
#endif
vm_object_unlock(obj);
}
}
void
_vm_object_allocate(objtype_t type, vm_pindex_t size, vm_object_t object,
const char *ident)
{
struct vm_object_hash *hash;
RB_INIT(&object->rb_memq);
lwkt_token_init(&object->token, ident);
TAILQ_INIT(&object->backing_list);
lockinit(&object->backing_lk, "baclk", 0, 0);
object->type = type;
object->size = size;
object->ref_count = 1;
object->memattr = VM_MEMATTR_DEFAULT;
object->hold_count = 0;
object->flags = 0;
if ((object->type == OBJT_DEFAULT) || (object->type == OBJT_SWAP))
vm_object_set_flag(object, OBJ_ONEMAPPING);
object->paging_in_progress = 0;
object->resident_page_count = 0;
object->pg_color = vm_quickcolor();
object->handle = NULL;
atomic_add_int(&object->generation, 1);
object->swblock_count = 0;
RB_INIT(&object->swblock_root);
vm_object_lock_init(object);
pmap_object_init(object);
vm_object_hold(object);
hash = vmobj_hash(object);
lwkt_gettoken(&hash->token);
TAILQ_INSERT_TAIL(&hash->list, object, object_entry);
lwkt_reltoken(&hash->token);
}
void
vm_object_init(vm_object_t object, vm_pindex_t size)
{
_vm_object_allocate(OBJT_DEFAULT, size, object, "vmobj");
vm_object_drop(object);
}
void
vm_object_init1(void)
{
int i;
for (i = 0; i < VMOBJ_HSIZE; ++i) {
TAILQ_INIT(&vm_object_hash[i].list);
lwkt_token_init(&vm_object_hash[i].token, "vmobjlst");
}
_vm_object_allocate(OBJT_DEFAULT, OFF_TO_IDX(KvaEnd),
kernel_object, "kobj");
vm_object_drop(kernel_object);
}
void
vm_object_init2(void)
{
kmalloc_obj_set_unlimited(M_VM_OBJECT);
}
vm_object_t
vm_object_allocate(objtype_t type, vm_pindex_t size)
{
vm_object_t obj;
obj = kmalloc_obj(sizeof(*obj), M_VM_OBJECT, M_INTWAIT|M_ZERO);
_vm_object_allocate(type, size, obj, "vmobj");
vm_object_drop(obj);
return (obj);
}
vm_object_t
vm_object_allocate_hold(objtype_t type, vm_pindex_t size)
{
vm_object_t obj;
obj = kmalloc_obj(sizeof(*obj), M_VM_OBJECT, M_INTWAIT|M_ZERO);
_vm_object_allocate(type, size, obj, "vmobj");
return (obj);
}
void
VMOBJDEBUG(vm_object_reference_locked)(vm_object_t object VMOBJDBARGS)
{
KKASSERT(object != NULL);
ASSERT_LWKT_TOKEN_HELD(vm_object_token(object));
atomic_add_int(&object->ref_count, 1);
if (object->type == OBJT_VNODE) {
vref(object->handle);
}
#if defined(DEBUG_LOCKS)
debugvm_object_add(object, file, line, 1);
#endif
}
void
VMOBJDEBUG(vm_object_reference_quick)(vm_object_t object VMOBJDBARGS)
{
KKASSERT(object->type == OBJT_VNODE || object->ref_count > 0);
atomic_add_int(&object->ref_count, 1);
if (object->type == OBJT_VNODE)
vref(object->handle);
#if defined(DEBUG_LOCKS)
debugvm_object_add(object, file, line, 1);
#endif
}
static void
VMOBJDEBUG(vm_object_vndeallocate)(vm_object_t object, struct vnode **vpp
VMOBJDBARGS)
{
struct vnode *vp = (struct vnode *) object->handle;
int count;
KASSERT(object->type == OBJT_VNODE,
("vm_object_vndeallocate: not a vnode object"));
KASSERT(vp != NULL, ("vm_object_vndeallocate: missing vp"));
ASSERT_LWKT_TOKEN_HELD(vm_object_token(object));
#ifdef INVARIANTS
if (object->ref_count == 0) {
vprint("vm_object_vndeallocate", vp);
panic("vm_object_vndeallocate: bad object reference count");
}
#endif
count = object->ref_count;
cpu_ccfence();
for (;;) {
if (count == 1) {
vm_object_upgrade(object);
if (atomic_fcmpset_int(&object->ref_count, &count, 0)) {
vclrflags(vp, VTEXT);
break;
}
} else {
if (atomic_fcmpset_int(&object->ref_count,
&count, count - 1)) {
break;
}
}
cpu_pause();
}
#if defined(DEBUG_LOCKS)
debugvm_object_add(object, file, line, -1);
#endif
if (vpp)
*vpp = vp;
else
vrele(vp);
}
void
VMOBJDEBUG(vm_object_deallocate)(vm_object_t object VMOBJDBARGS)
{
struct vnode *vp;
int count;
if (object == NULL)
return;
count = object->ref_count;
cpu_ccfence();
for (;;) {
if (count <= 3 || (object->type == OBJT_VNODE && count <= 1)) {
#if defined(DEBUG_LOCKS)
debugvm_object_add(object, file, line, 0);
#endif
vm_object_hold(object);
vm_object_deallocate_locked(object);
vm_object_drop(object);
break;
}
if (object->type == OBJT_VNODE) {
vp = (struct vnode *)object->handle;
if (atomic_fcmpset_int(&object->ref_count,
&count, count - 1)) {
#if defined(DEBUG_LOCKS)
debugvm_object_add(object, file, line, -1);
#endif
vrele(vp);
break;
}
} else {
if (atomic_fcmpset_int(&object->ref_count,
&count, count - 1)) {
#if defined(DEBUG_LOCKS)
debugvm_object_add(object, file, line, -1);
#endif
break;
}
}
cpu_pause();
}
}
void
VMOBJDEBUG(vm_object_deallocate_locked)(vm_object_t object VMOBJDBARGS)
{
if (object == NULL)
return;
if (object->type == OBJT_VNODE) {
ASSERT_LWKT_TOKEN_HELD(&object->token);
vm_object_vndeallocate(object, NULL);
return;
}
ASSERT_LWKT_TOKEN_HELD_EXCL(&object->token);
if (object->ref_count == 0) {
panic("vm_object_deallocate: object deallocated "
"too many times: %d", object->type);
}
if (object->ref_count > 2) {
atomic_add_int(&object->ref_count, -1);
#if defined(DEBUG_LOCKS)
debugvm_object_add(object, file, line, -1);
#endif
return;
}
KKASSERT(object->ref_count != 0);
if (object->ref_count >= 2) {
atomic_add_int(&object->ref_count, -1);
#if defined(DEBUG_LOCKS)
debugvm_object_add(object, file, line, -1);
#endif
return;
}
atomic_add_int(&object->ref_count, -1);
if ((object->flags & OBJ_DEAD) == 0)
vm_object_terminate(object);
}
static int vm_object_terminate_callback(vm_page_t p, void *data);
void
vm_object_terminate(vm_object_t object)
{
struct rb_vm_page_scan_info info;
struct vm_object_hash *hash;
ASSERT_LWKT_TOKEN_HELD(vm_object_token(object));
KKASSERT((object->flags & OBJ_DEAD) == 0);
vm_object_set_flag(object, OBJ_DEAD);
vm_object_pip_wait(object, "objtrm1");
KASSERT(!object->paging_in_progress,
("vm_object_terminate: pageout in progress"));
if (object->type == OBJT_VNODE) {
struct vnode *vp;
vp = (struct vnode *) object->handle;
vinvalbuf(vp, V_SAVE, 0, 0);
vm_object_page_clean(object, 0, 0, OBJPC_SYNC);
vinvalbuf(vp, V_SAVE, 0, 0);
}
vm_object_pip_wait(object, "objtrm2");
if (object->ref_count != 0) {
panic("vm_object_terminate: object with references, "
"ref_count=%d", object->ref_count);
}
pmap_object_free(object);
info.count = 0;
info.object = object;
do {
info.error = 0;
vm_page_rb_tree_RB_SCAN(&object->rb_memq, NULL,
vm_object_terminate_callback, &info);
} while (info.error);
vm_pager_deallocate(object);
for (;;) {
if (RB_ROOT(&object->rb_memq) == NULL)
break;
kprintf("vm_object_terminate: Warning, object %p "
"still has %ld pages\n",
object, object->resident_page_count);
vm_page_rb_tree_RB_SCAN(&object->rb_memq, NULL,
vm_object_terminate_callback, &info);
}
KKASSERT(object->resident_page_count == 0);
hash = vmobj_hash(object);
lwkt_gettoken(&hash->token);
TAILQ_REMOVE(&hash->list, object, object_entry);
lwkt_reltoken(&hash->token);
if (object->ref_count != 0) {
panic("vm_object_terminate2: object with references, "
"ref_count=%d", object->ref_count);
}
}
static int
vm_object_terminate_callback(vm_page_t p, void *data)
{
struct rb_vm_page_scan_info *info = data;
vm_object_t object;
object = p->object;
KKASSERT(object == info->object);
if (vm_page_busy_try(p, TRUE)) {
vm_page_sleep_busy(p, TRUE, "vmotrm");
info->error = 1;
return 0;
}
if (object != p->object) {
kprintf("vm_object_terminate: Warning: Encountered "
"busied page %p on queue %d\n", p, p->queue);
vm_page_wakeup(p);
info->error = 1;
} else if (p->wire_count == 0) {
if (pmap_mapped_sync(p) & (PG_MAPPED | PG_WRITEABLE))
vm_page_protect(p, VM_PROT_NONE);
vm_page_free(p);
mycpu->gd_cnt.v_pfree++;
} else {
if (p->queue != PQ_NONE) {
kprintf("vm_object_terminate: Warning: Encountered "
"wired page %p on queue %d\n", p, p->queue);
if (vm_object_debug > 0) {
--vm_object_debug;
print_backtrace(15);
}
}
if (pmap_mapped_sync(p) & (PG_MAPPED | PG_WRITEABLE))
vm_page_protect(p, VM_PROT_NONE);
vm_page_remove(p);
vm_page_wakeup(p);
}
if ((++info->count & 63) == 0)
lwkt_user_yield();
return(0);
}
static int vm_object_page_clean_pass1(struct vm_page *p, void *data);
static int vm_object_page_clean_pass2(struct vm_page *p, void *data);
void
vm_object_page_clean(vm_object_t object, vm_pindex_t start, vm_pindex_t end,
int flags)
{
struct rb_vm_page_scan_info info;
struct vnode *vp;
int wholescan;
int pagerflags;
int generation;
vm_object_hold(object);
if (object->type != OBJT_VNODE ||
(object->flags & OBJ_MIGHTBEDIRTY) == 0) {
vm_object_drop(object);
return;
}
pagerflags = (flags & (OBJPC_SYNC | OBJPC_INVAL)) ?
OBJPC_SYNC : OBJPC_CLUSTER_OK;
pagerflags |= (flags & OBJPC_INVAL) ? OBJPC_INVAL : 0;
vp = object->handle;
vm_object_set_flag(object, OBJ_CLEANING);
info.start_pindex = start;
if (end == 0) {
info.end_pindex = object->size - 1;
} else {
info.end_pindex = end - 1;
}
wholescan = (start == 0 && info.end_pindex == object->size - 1);
info.limit = flags;
info.pagerflags = pagerflags;
info.object = object;
if (wholescan) {
info.error = 0;
info.count = 0;
vm_page_rb_tree_RB_SCAN(&object->rb_memq, rb_vm_page_scancmp,
vm_object_page_clean_pass1, &info);
if (info.error == 0) {
vm_object_clear_flag(object,
OBJ_WRITEABLE|OBJ_MIGHTBEDIRTY);
if (object->type == OBJT_VNODE &&
(vp = (struct vnode *)object->handle) != NULL) {
vclrobjdirty(vp);
}
}
}
do {
info.error = 0;
info.count = 0;
generation = object->generation;
vm_page_rb_tree_RB_SCAN(&object->rb_memq, rb_vm_page_scancmp,
vm_object_page_clean_pass2, &info);
} while (info.error || generation != object->generation);
vm_object_clear_flag(object, OBJ_CLEANING);
vm_object_drop(object);
}
static
int
vm_object_page_clean_pass1(struct vm_page *p, void *data)
{
struct rb_vm_page_scan_info *info = data;
KKASSERT(p->object == info->object);
vm_page_flag_set(p, PG_CLEANCHK);
if ((info->limit & OBJPC_NOSYNC) && (p->flags & PG_NOSYNC)) {
info->error = 1;
} else if (vm_page_busy_try(p, FALSE)) {
info->error = 1;
} else {
KKASSERT(p->object == info->object);
vm_page_protect(p, VM_PROT_READ);
vm_page_wakeup(p);
}
if ((++info->count & 63) == 0)
lwkt_user_yield();
return(0);
}
static
int
vm_object_page_clean_pass2(struct vm_page *p, void *data)
{
struct rb_vm_page_scan_info *info = data;
int generation;
KKASSERT(p->object == info->object);
if ((p->flags & PG_CLEANCHK) == 0)
goto done;
generation = info->object->generation;
if (vm_page_busy_try(p, TRUE)) {
vm_page_sleep_busy(p, TRUE, "vpcwai");
info->error = 1;
goto done;
}
KKASSERT(p->object == info->object &&
info->object->generation == generation);
if (p->valid == 0 || (p->queue - p->pc) == PQ_CACHE) {
KKASSERT((p->dirty & p->valid) == 0 &&
(p->flags & PG_NEED_COMMIT) == 0);
vm_page_wakeup(p);
goto done;
}
vm_page_test_dirty(p);
if ((p->dirty & p->valid) == 0 && (p->flags & PG_NEED_COMMIT) == 0) {
vm_page_flag_clear(p, PG_CLEANCHK);
vm_page_wakeup(p);
goto done;
}
if ((info->limit & OBJPC_NOSYNC) && (p->flags & PG_NOSYNC)) {
vm_page_flag_clear(p, PG_CLEANCHK);
vm_page_wakeup(p);
goto done;
}
vm_object_page_collect_flush(info->object, p, info->pagerflags);
done:
if ((++info->count & 63) == 0)
lwkt_user_yield();
return(0);
}
static void
vm_object_page_collect_flush(vm_object_t object, vm_page_t p, int pagerflags)
{
int error;
int is;
int ib;
int i;
int page_base;
vm_pindex_t pi;
vm_page_t ma[BLIST_MAX_ALLOC];
ASSERT_LWKT_TOKEN_HELD(vm_object_token(object));
pi = p->pindex;
page_base = pi % BLIST_MAX_ALLOC;
ma[page_base] = p;
ib = page_base - 1;
is = page_base + 1;
while (ib >= 0) {
vm_page_t tp;
tp = vm_page_lookup_busy_try(object, pi - page_base + ib,
TRUE, &error);
if (error)
break;
if (tp == NULL)
break;
if ((pagerflags & OBJPC_IGNORE_CLEANCHK) == 0 &&
(tp->flags & PG_CLEANCHK) == 0) {
vm_page_wakeup(tp);
break;
}
if ((tp->queue - tp->pc) == PQ_CACHE) {
vm_page_flag_clear(tp, PG_CLEANCHK);
vm_page_wakeup(tp);
break;
}
vm_page_test_dirty(tp);
if ((tp->dirty & tp->valid) == 0 &&
(tp->flags & PG_NEED_COMMIT) == 0) {
vm_page_flag_clear(tp, PG_CLEANCHK);
vm_page_wakeup(tp);
break;
}
ma[ib] = tp;
--ib;
}
++ib;
while (is < BLIST_MAX_ALLOC &&
pi - page_base + is < object->size) {
vm_page_t tp;
tp = vm_page_lookup_busy_try(object, pi - page_base + is,
TRUE, &error);
if (error)
break;
if (tp == NULL)
break;
if ((pagerflags & OBJPC_IGNORE_CLEANCHK) == 0 &&
(tp->flags & PG_CLEANCHK) == 0) {
vm_page_wakeup(tp);
break;
}
if ((tp->queue - tp->pc) == PQ_CACHE) {
vm_page_flag_clear(tp, PG_CLEANCHK);
vm_page_wakeup(tp);
break;
}
vm_page_test_dirty(tp);
if ((tp->dirty & tp->valid) == 0 &&
(tp->flags & PG_NEED_COMMIT) == 0) {
vm_page_flag_clear(tp, PG_CLEANCHK);
vm_page_wakeup(tp);
break;
}
ma[is] = tp;
++is;
}
for (i = ib; i < is; ++i) {
vm_page_flag_clear(ma[i], PG_CLEANCHK);
vm_page_hold(ma[i]);
}
vm_pageout_flush(&ma[ib], is - ib, pagerflags);
for (i = ib; i < is; ++i)
vm_page_unhold(ma[i]);
}
void
vm_object_madvise(vm_object_t object, vm_pindex_t pindex,
vm_pindex_t count, int advise)
{
vm_pindex_t end;
vm_page_t m;
int error;
if (object == NULL)
return;
end = pindex + count;
vm_object_hold(object);
for (; pindex < end; pindex += 1) {
relookup:
if (advise == MADV_FREE) {
if ((object->type != OBJT_DEFAULT &&
object->type != OBJT_SWAP) ||
(object->flags & OBJ_ONEMAPPING) == 0) {
continue;
}
}
m = vm_page_lookup_busy_try(object, pindex, TRUE, &error);
if (error) {
vm_page_sleep_busy(m, TRUE, "madvpo");
goto relookup;
}
if (m == NULL) {
if (advise == MADV_FREE && object->type == OBJT_SWAP)
swap_pager_freespace(object, pindex, 1);
continue;
}
if (m->wire_count ||
(m->flags & (PG_FICTITIOUS | PG_UNQUEUED |
PG_NEED_COMMIT)) ||
m->valid != VM_PAGE_BITS_ALL
) {
vm_page_wakeup(m);
continue;
}
if (advise == MADV_WILLNEED) {
vm_page_activate(m);
} else if (advise == MADV_DONTNEED) {
vm_page_dontneed(m);
} else if (advise == MADV_FREE) {
pmap_clear_modify(m);
m->dirty = 0;
m->act_count = 0;
vm_page_dontneed(m);
if (object->type == OBJT_SWAP)
swap_pager_freespace(object, pindex, 1);
}
vm_page_wakeup(m);
}
vm_object_drop(object);
}
static int vm_object_page_remove_callback(vm_page_t p, void *data);
void
vm_object_page_remove(vm_object_t object, vm_pindex_t start, vm_pindex_t end,
boolean_t clean_only)
{
struct rb_vm_page_scan_info info;
int all;
vm_object_hold(object);
if (object == NULL ||
(object->type != OBJT_MGTDEVICE &&
object->resident_page_count == 0 && object->swblock_count == 0)) {
vm_object_drop(object);
return;
}
KASSERT(object->type != OBJT_PHYS,
("attempt to remove pages from a physical object"));
vm_object_pip_add(object, 1);
info.object = object;
info.start_pindex = start;
if (end == 0 || end == (vm_pindex_t)-1) {
info.end_pindex = (vm_pindex_t)-1;
end = object->size;
} else {
info.end_pindex = end - 1;
}
info.limit = clean_only;
info.count = 0;
all = (start == 0 && info.end_pindex >= object->size - 1);
{
vm_map_backing_t ba;
vm_pindex_t sba, eba;
vm_offset_t sva, eva;
lockmgr(&object->backing_lk, LK_EXCLUSIVE);
TAILQ_FOREACH(ba, &object->backing_list, entry) {
sba = OFF_TO_IDX(ba->offset);
eba = sba + OFF_TO_IDX(ba->end - ba->start);
if (sba < start)
sba = start;
if (eba > end)
eba = end;
if (sba < eba) {
sva = ba->start + IDX_TO_OFF(sba) - ba->offset;
eva = sva + IDX_TO_OFF(eba - sba);
#if 0
kprintf("VM_OBJECT_PAGE_REMOVE "
"%p[%016jx] %016jx-%016jx\n",
ba->pmap, ba->start, sva, eva);
#endif
pmap_remove(ba->pmap, sva, eva);
}
}
lockmgr(&object->backing_lk, LK_RELEASE);
}
do {
info.error = 0;
vm_page_rb_tree_RB_SCAN(&object->rb_memq, rb_vm_page_scancmp,
vm_object_page_remove_callback, &info);
} while (info.error);
if (object->type != OBJT_SWAP || clean_only == FALSE) {
if (all)
swap_pager_freespace_all(object);
else
swap_pager_freespace(object, info.start_pindex,
info.end_pindex - info.start_pindex + 1);
}
vm_object_pip_wakeup(object);
vm_object_drop(object);
}
static int
vm_object_page_remove_callback(vm_page_t p, void *data)
{
struct rb_vm_page_scan_info *info = data;
if (info->object != p->object ||
p->pindex < info->start_pindex ||
p->pindex > info->end_pindex) {
kprintf("vm_object_page_remove_callbackA: obj/pg race %p/%p\n",
info->object, p);
return(0);
}
if (vm_page_busy_try(p, TRUE)) {
vm_page_sleep_busy(p, TRUE, "vmopar");
info->error = 1;
return(0);
}
if (info->object != p->object) {
kprintf("vm_object_page_remove_callbackB: obj/pg race %p/%p\n",
info->object, p);
vm_page_wakeup(p);
return(0);
}
if (p->wire_count != 0) {
vm_page_protect(p, VM_PROT_NONE);
if (info->limit == 0)
p->valid = 0;
vm_page_wakeup(p);
goto done;
}
if (info->limit && p->valid) {
vm_page_test_dirty(p);
if ((p->valid & p->dirty) || (p->flags & PG_NEED_COMMIT)) {
vm_page_wakeup(p);
goto done;
}
}
vm_page_protect(p, VM_PROT_NONE);
if (info->limit && p->valid) {
vm_page_test_dirty(p);
if ((p->valid & p->dirty) || (p->flags & PG_NEED_COMMIT)) {
vm_page_wakeup(p);
goto done;
}
}
vm_page_free(p);
done:
if ((++info->count & 63) == 0)
lwkt_user_yield();
return(0);
}
boolean_t
vm_object_coalesce(vm_object_t prev_object, vm_pindex_t prev_pindex,
vm_size_t prev_size, vm_size_t next_size)
{
vm_pindex_t next_pindex;
if (prev_object == NULL)
return (TRUE);
vm_object_hold(prev_object);
if (prev_object->type != OBJT_DEFAULT &&
prev_object->type != OBJT_SWAP) {
vm_object_drop(prev_object);
return (FALSE);
}
#if 0
vm_object_collapse(prev_object, NULL);
#endif
#if 0
if (prev_object->backing_object != NULL) {
vm_object_chain_release(prev_object);
vm_object_drop(prev_object);
return (FALSE);
}
#endif
prev_size >>= PAGE_SHIFT;
next_size >>= PAGE_SHIFT;
next_pindex = prev_pindex + prev_size;
if (prev_object->ref_count > 1 &&
prev_object->size != next_pindex) {
vm_object_drop(prev_object);
return (FALSE);
}
if (next_pindex < prev_object->size) {
vm_object_page_remove(prev_object,
next_pindex,
next_pindex + next_size, FALSE);
if (prev_object->type == OBJT_SWAP)
swap_pager_freespace(prev_object,
next_pindex, next_size);
}
if (next_pindex + next_size > prev_object->size)
prev_object->size = next_pindex + next_size;
vm_object_drop(prev_object);
return (TRUE);
}
void
vm_object_set_writeable_dirty(vm_object_t object)
{
struct vnode *vp;
if ((object->flags & (OBJ_WRITEABLE|OBJ_MIGHTBEDIRTY)) !=
(OBJ_WRITEABLE|OBJ_MIGHTBEDIRTY)) {
vm_object_set_flag(object, OBJ_WRITEABLE|OBJ_MIGHTBEDIRTY);
}
if (object->type == OBJT_VNODE &&
(vp = (struct vnode *)object->handle) != NULL) {
if ((vp->v_flag & VOBJDIRTY) == 0) {
if (vp->v_mount &&
(vp->v_mount->mnt_kern_flag & MNTK_THR_SYNC)) {
vsetobjdirty(vp);
} else {
vsetflags(vp, VOBJDIRTY);
}
}
}
}
#include "opt_ddb.h"
#ifdef DDB
#include <sys/cons.h>
#include <ddb/ddb.h>
static int _vm_object_in_map (vm_map_t map, vm_object_t object,
vm_map_entry_t entry);
static int vm_object_in_map (vm_object_t object);
static int
_vm_object_in_map(vm_map_t map, vm_object_t object, vm_map_entry_t entry)
{
vm_map_backing_t ba;
vm_map_t tmpm;
vm_map_entry_t tmpe;
int entcount;
if (map == NULL)
return 0;
if (entry == NULL) {
tmpe = RB_MIN(vm_map_rb_tree, &map->rb_root);
entcount = map->nentries;
while (entcount-- && tmpe) {
if( _vm_object_in_map(map, object, tmpe)) {
return 1;
}
tmpe = vm_map_rb_tree_RB_NEXT(tmpe);
}
return (0);
}
switch(entry->maptype) {
case VM_MAPTYPE_SUBMAP:
tmpm = entry->ba.sub_map;
tmpe = RB_MIN(vm_map_rb_tree, &tmpm->rb_root);
entcount = tmpm->nentries;
while (entcount-- && tmpe) {
if( _vm_object_in_map(tmpm, object, tmpe)) {
return 1;
}
tmpe = vm_map_rb_tree_RB_NEXT(tmpe);
}
break;
case VM_MAPTYPE_NORMAL:
ba = &entry->ba;
while (ba) {
if (ba->object == object)
return TRUE;
ba = ba->backing_ba;
}
break;
default:
break;
}
return 0;
}
static int vm_object_in_map_callback(struct proc *p, void *data);
struct vm_object_in_map_info {
vm_object_t object;
int rv;
};
static int
vm_object_in_map(vm_object_t object)
{
struct vm_object_in_map_info info;
info.rv = 0;
info.object = object;
allproc_scan(vm_object_in_map_callback, &info, 0);
if (info.rv)
return 1;
if( _vm_object_in_map(kernel_map, object, 0))
return 1;
if( _vm_object_in_map(pager_map, object, 0))
return 1;
if( _vm_object_in_map(buffer_map, object, 0))
return 1;
return 0;
}
static int
vm_object_in_map_callback(struct proc *p, void *data)
{
struct vm_object_in_map_info *info = data;
if (p->p_vmspace) {
if (_vm_object_in_map(&p->p_vmspace->vm_map, info->object, 0)) {
info->rv = 1;
return -1;
}
}
return (0);
}
DB_SHOW_COMMAND(vmochk, vm_object_check)
{
struct vm_object_hash *hash;
vm_object_t object;
int n;
for (n = 0; n < VMOBJ_HSIZE; ++n) {
hash = &vm_object_hash[n];
for (object = TAILQ_FIRST(&hash->list);
object != NULL;
object = TAILQ_NEXT(object, object_entry)) {
if (object->type == OBJT_MARKER)
continue;
if (object->handle != NULL ||
(object->type != OBJT_DEFAULT &&
object->type != OBJT_SWAP)) {
continue;
}
if (object->ref_count == 0) {
db_printf("vmochk: internal obj has "
"zero ref count: %ld\n",
(long)object->size);
}
if (vm_object_in_map(object))
continue;
db_printf("vmochk: internal obj is not in a map: "
"ref: %d, size: %lu: 0x%lx\n",
object->ref_count, (u_long)object->size,
(u_long)object->size);
}
}
}
DB_SHOW_COMMAND(object, vm_object_print_static)
{
vm_object_t object = (vm_object_t)addr;
boolean_t full = have_addr;
vm_page_t p;
#define count was_count
int count;
if (object == NULL)
return;
db_iprintf(
"Object %p: type=%d, size=0x%lx, res=%ld, ref=%d, flags=0x%x\n",
object, (int)object->type, (u_long)object->size,
object->resident_page_count, object->ref_count, object->flags);
db_iprintf("\n");
if (!full)
return;
db_indent += 2;
count = 0;
RB_FOREACH(p, vm_page_rb_tree, &object->rb_memq) {
if (count == 0)
db_iprintf("memory:=");
else if (count == 6) {
db_printf("\n");
db_iprintf(" ...");
count = 0;
} else
db_printf(",");
count++;
db_printf("(off=0x%lx,page=0x%lx)",
(u_long) p->pindex, (u_long) VM_PAGE_TO_PHYS(p));
}
if (count != 0)
db_printf("\n");
db_indent -= 2;
}
#undef count
void
vm_object_print( long addr,
boolean_t have_addr,
long count,
char *modif)
{
vm_object_print_static(addr, have_addr, count, modif);
}
DB_SHOW_COMMAND(vmopag, vm_object_print_pages)
{
struct vm_object_hash *hash;
vm_object_t object;
int nl = 0;
int c;
int n;
for (n = 0; n < VMOBJ_HSIZE; ++n) {
hash = &vm_object_hash[n];
for (object = TAILQ_FIRST(&hash->list);
object != NULL;
object = TAILQ_NEXT(object, object_entry)) {
vm_pindex_t idx, fidx;
vm_pindex_t osize;
vm_paddr_t pa = -1, padiff;
int rcount;
vm_page_t m;
if (object->type == OBJT_MARKER)
continue;
db_printf("new object: %p\n", (void *)object);
if ( nl > 18) {
c = cngetc();
if (c != ' ')
return;
nl = 0;
}
nl++;
rcount = 0;
fidx = 0;
osize = object->size;
if (osize > 128)
osize = 128;
for (idx = 0; idx < osize; idx++) {
m = vm_page_lookup(object, idx);
if (m == NULL) {
if (rcount) {
db_printf(" index(%ld)run(%d)pa(0x%lx)\n",
(long)fidx, rcount, (long)pa);
if ( nl > 18) {
c = cngetc();
if (c != ' ')
return;
nl = 0;
}
nl++;
rcount = 0;
}
continue;
}
if (rcount &&
(VM_PAGE_TO_PHYS(m) == pa + rcount * PAGE_SIZE)) {
++rcount;
continue;
}
if (rcount) {
padiff = pa + rcount * PAGE_SIZE - VM_PAGE_TO_PHYS(m);
padiff >>= PAGE_SHIFT;
padiff &= PQ_L2_MASK;
if (padiff == 0) {
pa = VM_PAGE_TO_PHYS(m) - rcount * PAGE_SIZE;
++rcount;
continue;
}
db_printf(" index(%ld)run(%d)pa(0x%lx)",
(long)fidx, rcount, (long)pa);
db_printf("pd(%ld)\n", (long)padiff);
if ( nl > 18) {
c = cngetc();
if (c != ' ')
return;
nl = 0;
}
nl++;
}
fidx = idx;
pa = VM_PAGE_TO_PHYS(m);
rcount = 1;
}
if (rcount) {
db_printf(" index(%ld)run(%d)pa(0x%lx)\n",
(long)fidx, rcount, (long)pa);
if ( nl > 18) {
c = cngetc();
if (c != ' ')
return;
nl = 0;
}
nl++;
}
}
}
}
#endif