summaryrefslogtreecommitdiffstats
path: root/sys/vm
diff options
context:
space:
mode:
authoralfred <alfred@FreeBSD.org>2002-02-05 21:23:05 +0000
committeralfred <alfred@FreeBSD.org>2002-02-05 21:23:05 +0000
commitc6a128a4b907ab2e60ff122305aee878371b5e92 (patch)
tree26c78c7d9d9ce9844263c5da070f87c4f6627da9 /sys/vm
parent31898ce15f5d4ffc3a4deae6238e274e853a26c8 (diff)
downloadFreeBSD-src-c6a128a4b907ab2e60ff122305aee878371b5e92.zip
FreeBSD-src-c6a128a4b907ab2e60ff122305aee878371b5e92.tar.gz
Fix a race with free'ing vmspaces at process exit when vmspaces are
shared. Also introduce vm_endcopy instead of using pointer tricks when initializing new vmspaces. The race occured because of how the reference was utilized: test vmspace reference, possibly block, decrement reference When sharing a vmspace between multiple processes it was possible for two processes exiting at the same time to test the reference count, possibly block and neither one free because they wouldn't see the other's update. Submitted by: green
Diffstat (limited to 'sys/vm')
-rw-r--r--sys/vm/vm_extern.h1
-rw-r--r--sys/vm/vm_glue.c2
-rw-r--r--sys/vm/vm_map.c45
-rw-r--r--sys/vm/vm_map.h2
4 files changed, 33 insertions, 17 deletions
diff --git a/sys/vm/vm_extern.h b/sys/vm/vm_extern.h
index 7f24e11..728b5b6 100644
--- a/sys/vm/vm_extern.h
+++ b/sys/vm/vm_extern.h
@@ -90,6 +90,7 @@ struct vmspace *vmspace_fork __P((struct vmspace *));
void vmspace_exec __P((struct proc *));
void vmspace_unshare __P((struct proc *));
void vmspace_free __P((struct vmspace *));
+void vmspace_exitfree __P((struct proc *));
void vnode_pager_setsize __P((struct vnode *, vm_ooffset_t));
void vslock __P((caddr_t, u_int));
void vsunlock __P((caddr_t, u_int));
diff --git a/sys/vm/vm_glue.c b/sys/vm/vm_glue.c
index f9fb68c..d2ad1e2 100644
--- a/sys/vm/vm_glue.c
+++ b/sys/vm/vm_glue.c
@@ -305,7 +305,7 @@ vm_waitproc(p)
pmap_dispose_proc(p); /* drop per-process resources */
FOREACH_THREAD_IN_PROC(p, td)
pmap_dispose_thread(td);
- vmspace_free(p->p_vmspace); /* and clean-out the vmspace */
+ vmspace_exitfree(p); /* and clean-out the vmspace */
}
/*
diff --git a/sys/vm/vm_map.c b/sys/vm/vm_map.c
index 8dc3550..8b7a230 100644
--- a/sys/vm/vm_map.c
+++ b/sys/vm/vm_map.c
@@ -172,6 +172,7 @@ vmspace_alloc(min, max)
vm->vm_map.pmap = vmspace_pmap(vm); /* XXX */
vm->vm_refcnt = 1;
vm->vm_shm = NULL;
+ vm->vm_freer = NULL;
return (vm);
}
@@ -189,6 +190,24 @@ vm_init2(void)
vm_object_init2();
}
+static __inline void
+vmspace_dofree( struct vmspace *vm)
+{
+ CTR1(KTR_VM, "vmspace_free: %p", vm);
+ /*
+ * Lock the map, to wait out all other references to it.
+ * Delete all of the mappings and pages they hold, then call
+ * the pmap module to reclaim anything left.
+ */
+ vm_map_lock(&vm->vm_map);
+ (void) vm_map_delete(&vm->vm_map, vm->vm_map.min_offset,
+ vm->vm_map.max_offset);
+ vm_map_unlock(&vm->vm_map);
+ pmap_release(vmspace_pmap(vm));
+ vm_map_destroy(&vm->vm_map);
+ zfree(vmspace_zone, vm);
+}
+
void
vmspace_free(struct vmspace *vm)
{
@@ -197,23 +216,17 @@ vmspace_free(struct vmspace *vm)
if (vm->vm_refcnt == 0)
panic("vmspace_free: attempt to free already freed vmspace");
- if (--vm->vm_refcnt == 0) {
+ if (--vm->vm_refcnt == 0)
+ vmspace_dofree(vm);
+}
- CTR1(KTR_VM, "vmspace_free: %p", vm);
- /*
- * Lock the map, to wait out all other references to it.
- * Delete all of the mappings and pages they hold, then call
- * the pmap module to reclaim anything left.
- */
- vm_map_lock(&vm->vm_map);
- (void) vm_map_delete(&vm->vm_map, vm->vm_map.min_offset,
- vm->vm_map.max_offset);
- vm_map_unlock(&vm->vm_map);
+void
+vmspace_exitfree(struct proc *p)
+{
+ GIANT_REQUIRED;
- pmap_release(vmspace_pmap(vm));
- vm_map_destroy(&vm->vm_map);
- zfree(vmspace_zone, vm);
- }
+ if (p == p->p_vmspace->vm_freer)
+ vmspace_dofree(p->p_vmspace);
}
/*
@@ -2317,7 +2330,7 @@ vmspace_fork(struct vmspace *vm1)
vm2 = vmspace_alloc(old_map->min_offset, old_map->max_offset);
bcopy(&vm1->vm_startcopy, &vm2->vm_startcopy,
- (caddr_t) (vm1 + 1) - (caddr_t) &vm1->vm_startcopy);
+ (caddr_t) &vm1->vm_endcopy - (caddr_t) &vm1->vm_startcopy);
new_map = &vm2->vm_map; /* XXX */
new_map->timestamp = 1;
diff --git a/sys/vm/vm_map.h b/sys/vm/vm_map.h
index c9831d5..b376cc1 100644
--- a/sys/vm/vm_map.h
+++ b/sys/vm/vm_map.h
@@ -188,6 +188,8 @@ struct vmspace {
caddr_t vm_daddr; /* user virtual address of data XXX */
caddr_t vm_maxsaddr; /* user VA at max stack growth */
caddr_t vm_minsaddr; /* user VA at max stack growth */
+#define vm_endcopy vm_freer
+ struct proc *vm_freer; /* vm freed on whose behalf */
};
#ifdef _KERNEL
OpenPOWER on IntegriCloud