summaryrefslogtreecommitdiffstats
path: root/sys/vm
diff options
context:
space:
mode:
authorgreen <green@FreeBSD.org>2004-06-24 22:43:46 +0000
committergreen <green@FreeBSD.org>2004-06-24 22:43:46 +0000
commit4d32bd722dc911178dea899f447cafb400769806 (patch)
treebdf97fd80fce00db5dd8b96011e00a9f96731805 /sys/vm
parent7a9902cd18f46701f7f6aa84cb48f0f3c9b8950b (diff)
downloadFreeBSD-src-4d32bd722dc911178dea899f447cafb400769806.zip
FreeBSD-src-4d32bd722dc911178dea899f447cafb400769806.tar.gz
Correct the tracking of various bits of the process's vmspace and vm_map
when not propogated on fork (due to minherit(2)). Consistency checks otherwise fail when the vm_map is freed and it appears to have not been emptied completely, causing an INVARIANTS panic in vm_map_zdtor(). PR: kern/68017 Submitted by: Mark W. Krentel <krentel@dreamscape.com> Reviewed by: alc
Diffstat (limited to 'sys/vm')
-rw-r--r--sys/vm/vm_map.c47
1 files changed, 44 insertions, 3 deletions
diff --git a/sys/vm/vm_map.c b/sys/vm/vm_map.c
index d27df6f..df47991 100644
--- a/sys/vm/vm_map.c
+++ b/sys/vm/vm_map.c
@@ -2342,12 +2342,44 @@ vm_map_copy_entry(
}
/*
+ * vmspace_map_entry_forked:
+ * Update the newly-forked vmspace each time a map entry is inherited
+ * or copied. The values for vm_dsize and vm_tsize are approximate
+ * (and mostly-obsolete ideas in the face of mmap(2) et al.)
+ */
+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;
+
+ entrysize = entry->end - entry->start;
+ vm2->vm_map.size += entrysize;
+ if (entry->eflags & (MAP_ENTRY_GROWS_DOWN | MAP_ENTRY_GROWS_UP)) {
+ 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);
+ }
+}
+
+/*
* vmspace_fork:
* Create a new process vmspace structure and vm_map
* based on those of an existing process. The new map
* is based on the old map, according to the inheritance
* values on the regions in that map.
*
+ * XXX It might be worth coalescing the entries added to the new vmspace.
+ *
* The source map must not be locked.
*/
struct vmspace *
@@ -2366,8 +2398,16 @@ vmspace_fork(struct vmspace *vm1)
old_map->infork = 1;
vm2 = vmspace_alloc(old_map->min_offset, old_map->max_offset);
- bcopy(&vm1->vm_startcopy, &vm2->vm_startcopy,
- (caddr_t) &vm1->vm_endcopy - (caddr_t) &vm1->vm_startcopy);
+ /*
+ * Using vm_{start,end}copy is invalid for more fields than
+ * it is valid here. For the most part, initialize size
+ * fields to zero and update them as map entries are copied
+ */
+ bzero(&vm2->vm_startcopy,
+ (caddr_t)&vm2->vm_endcopy - (caddr_t)&vm2->vm_startcopy);
+ vm2->vm_taddr = vm1->vm_taddr;
+ vm2->vm_daddr = vm1->vm_daddr;
+ vm2->vm_maxsaddr = vm1->vm_maxsaddr;
new_map = &vm2->vm_map; /* XXX */
new_map->timestamp = 1;
@@ -2431,6 +2471,7 @@ vmspace_fork(struct vmspace *vm1)
*/
vm_map_entry_link(new_map, new_map->header.prev,
new_entry);
+ vmspace_map_entry_forked(vm1, vm2, new_entry);
/*
* Update the physical map
@@ -2452,6 +2493,7 @@ vmspace_fork(struct vmspace *vm1)
new_entry->object.vm_object = NULL;
vm_map_entry_link(new_map, new_map->header.prev,
new_entry);
+ vmspace_map_entry_forked(vm1, vm2, new_entry);
vm_map_copy_entry(old_map, new_map, old_entry,
new_entry);
break;
@@ -2459,7 +2501,6 @@ vmspace_fork(struct vmspace *vm1)
old_entry = old_entry->next;
}
- new_map->size = old_map->size;
old_map->infork = 0;
vm_map_unlock(old_map);
OpenPOWER on IntegriCloud