diff options
author | kib <kib@FreeBSD.org> | 2012-02-23 21:07:16 +0000 |
---|---|---|
committer | kib <kib@FreeBSD.org> | 2012-02-23 21:07:16 +0000 |
commit | f315a59476bc5812458ae81c8b4eb4c37f8a3aea (patch) | |
tree | 9d31f51a13c9699dab2dae074d2eac6a044b601d /sys/vm/vm_map.c | |
parent | dc536a5db9b7b0b09ddaf1d12a42331848272300 (diff) | |
download | FreeBSD-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.c | 77 |
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); } |