summaryrefslogtreecommitdiffstats
path: root/sys/vm/vm_map.c
diff options
context:
space:
mode:
authorkib <kib@FreeBSD.org>2012-02-23 21:07:16 +0000
committerkib <kib@FreeBSD.org>2012-02-23 21:07:16 +0000
commitf315a59476bc5812458ae81c8b4eb4c37f8a3aea (patch)
tree9d31f51a13c9699dab2dae074d2eac6a044b601d /sys/vm/vm_map.c
parentdc536a5db9b7b0b09ddaf1d12a42331848272300 (diff)
downloadFreeBSD-src-f315a59476bc5812458ae81c8b4eb4c37f8a3aea.zip
FreeBSD-src-f315a59476bc5812458ae81c8b4eb4c37f8a3aea.tar.gz
Account the writeable shared mappings backed by file in the vnode
v_writecount. Keep the amount of the virtual address space used by the mappings in the new vm_object un_pager.vnp.writemappings counter. The vnode v_writecount is incremented when writemappings gets non-zero value, and decremented when writemappings is returned to zero. Writeable shared vnode-backed mappings are accounted for in vm_mmap(), and vm_map_insert() is instructed to set MAP_ENTRY_VN_WRITECNT flag on the created map entry. During deferred map entry deallocation, vm_map_process_deferred() checks for MAP_ENTRY_VN_WRITECOUNT and decrements writemappings for the vm object. Now, the writeable mount cannot be demoted to read-only while writeable shared mappings of the vnodes from the mount point exist. Also, execve(2) fails for such files with ETXTBUSY, as it should be. Noted by: tegge Reviewed by: tegge (long time ago, early version), alc Tested by: pho MFC after: 3 weeks
Diffstat (limited to 'sys/vm/vm_map.c')
-rw-r--r--sys/vm/vm_map.c77
1 files changed, 73 insertions, 4 deletions
diff --git a/sys/vm/vm_map.c b/sys/vm/vm_map.c
index 0c98d56..7d228cb 100644
--- a/sys/vm/vm_map.c
+++ b/sys/vm/vm_map.c
@@ -91,6 +91,7 @@ __FBSDID("$FreeBSD$");
#include <vm/vm_pager.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>
@@ -475,11 +476,23 @@ vm_map_process_deferred(void)
{
struct thread *td;
vm_map_entry_t entry;
+ vm_object_t object;
td = curthread;
-
while ((entry = td->td_map_def_user) != NULL) {
td->td_map_def_user = entry->next;
+ if ((entry->eflags & MAP_ENTRY_VN_WRITECNT) != 0) {
+ /*
+ * Decrement the object's writemappings and
+ * possibly the vnode's v_writecount.
+ */
+ KASSERT((entry->eflags & MAP_ENTRY_IS_SUB_MAP) == 0,
+ ("Submap with writecount"));
+ object = entry->object.vm_object;
+ KASSERT(object != NULL, ("No object for writecount"));
+ vnode_pager_release_writecount(object, entry->start,
+ entry->end);
+ }
vm_map_entry_deallocate(entry, FALSE);
}
}
@@ -1174,6 +1187,8 @@ vm_map_insert(vm_map_t map, vm_object_t object, vm_ooffset_t offset,
protoeflags |= MAP_ENTRY_NOSYNC;
if (cow & MAP_DISABLE_COREDUMP)
protoeflags |= MAP_ENTRY_NOCOREDUMP;
+ if (cow & MAP_VN_WRITECOUNT)
+ protoeflags |= MAP_ENTRY_VN_WRITECNT;
if (cow & MAP_INHERIT_SHARE)
inheritance = VM_INHERIT_SHARE;
else
@@ -1516,6 +1531,11 @@ vm_map_simplify_entry(vm_map_t map, vm_map_entry_t entry)
* references. Thus, the map lock can be kept
* without causing a lock-order reversal with
* the vnode lock.
+ *
+ * Since we count the number of virtual page
+ * mappings in object->un_pager.vnp.writemappings,
+ * the writemappings value should not be adjusted
+ * when the entry is disposed of.
*/
if (prev->object.vm_object)
vm_object_deallocate(prev->object.vm_object);
@@ -1627,6 +1647,13 @@ _vm_map_clip_start(vm_map_t map, vm_map_entry_t entry, vm_offset_t start)
if ((entry->eflags & MAP_ENTRY_IS_SUB_MAP) == 0) {
vm_object_reference(new_entry->object.vm_object);
+ /*
+ * The object->un_pager.vnp.writemappings for the
+ * object of MAP_ENTRY_VN_WRITECNT type entry shall be
+ * kept as is here. The virtual pages are
+ * re-distributed among the clipped entries, so the sum is
+ * left the same.
+ */
}
}
@@ -2900,6 +2927,7 @@ vm_map_copy_entry(
vm_ooffset_t *fork_charge)
{
vm_object_t src_object;
+ vm_map_entry_t fake_entry;
vm_offset_t size;
struct ucred *cred;
int charged;
@@ -2965,6 +2993,27 @@ vm_map_copy_entry(
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_VN_WRITECNT) {
+ /*
+ * MAP_ENTRY_VN_WRITECNT cannot
+ * indicate write reference from
+ * src_entry, since the entry is
+ * marked as needs copy. Allocate a
+ * fake entry that is used to
+ * decrement object->un_pager.vnp.writecount
+ * at the appropriate time. Attach
+ * fake_entry to the deferred list.
+ */
+ fake_entry = vm_map_entry_create(dst_map);
+ fake_entry->eflags = MAP_ENTRY_VN_WRITECNT;
+ src_entry->eflags &= ~MAP_ENTRY_VN_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->next = curthread->td_map_def_user;
+ curthread->td_map_def_user = fake_entry;
+ }
} else {
dst_entry->object.vm_object = NULL;
dst_entry->offset = 0;
@@ -3043,6 +3092,7 @@ vmspace_fork(struct vmspace *vm1, vm_ooffset_t *fork_charge)
vm_map_lock(old_map);
if (old_map->busy)
vm_map_wait_busy(old_map);
+ new_map = NULL; /* silence gcc */
vm2 = vmspace_alloc(old_map->min_offset, old_map->max_offset);
if (vm2 == NULL)
goto unlock_and_return;
@@ -3122,6 +3172,16 @@ vmspace_fork(struct vmspace *vm1, vm_ooffset_t *fork_charge)
new_entry->eflags &= ~(MAP_ENTRY_USER_WIRED |
MAP_ENTRY_IN_TRANSITION);
new_entry->wired_count = 0;
+ if (new_entry->eflags & MAP_ENTRY_VN_WRITECNT) {
+ object = new_entry->object.vm_object;
+ KASSERT(((struct vnode *)object->handle)->
+ v_writecount > 0,
+ ("vmspace_fork: v_writecount"));
+ KASSERT(object->un_pager.vnp.writemappings > 0,
+ ("vmspace_fork: vnp.writecount"));
+ vnode_pager_update_writecount(object,
+ new_entry->start, new_entry->end);
+ }
/*
* Insert the entry into the new map -- we know we're
@@ -3146,8 +3206,11 @@ vmspace_fork(struct vmspace *vm1, vm_ooffset_t *fork_charge)
*/
new_entry = vm_map_entry_create(new_map);
*new_entry = *old_entry;
+ /*
+ * Copied entry is COW over the old object.
+ */
new_entry->eflags &= ~(MAP_ENTRY_USER_WIRED |
- MAP_ENTRY_IN_TRANSITION);
+ MAP_ENTRY_IN_TRANSITION | MAP_ENTRY_VN_WRITECNT);
new_entry->wired_count = 0;
new_entry->object.vm_object = NULL;
new_entry->cred = NULL;
@@ -3161,9 +3224,15 @@ vmspace_fork(struct vmspace *vm1, vm_ooffset_t *fork_charge)
old_entry = old_entry->next;
}
unlock_and_return:
- vm_map_unlock(old_map);
+ /*
+ * Use inlined vm_map_unlock() to postpone handling the deferred
+ * map entries, which cannot be done until both old_map and
+ * new_map locks are released.
+ */
+ sx_xunlock(&old_map->lock);
if (vm2 != NULL)
- vm_map_unlock(new_map);
+ sx_xunlock(&new_map->lock);
+ vm_map_process_deferred();
return (vm2);
}
OpenPOWER on IntegriCloud