summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authoralc <alc@FreeBSD.org>2003-12-06 05:45:32 +0000
committeralc <alc@FreeBSD.org>2003-12-06 05:45:32 +0000
commit3b8e185f65b43a35fd03a8c1e33517c56dafd196 (patch)
tree560c9b455e9d0e51f3d5cfe4b3698382da778053 /sys
parentb7377409b878fdfc3f6ec9283862d597e7a898a0 (diff)
downloadFreeBSD-src-3b8e185f65b43a35fd03a8c1e33517c56dafd196.zip
FreeBSD-src-3b8e185f65b43a35fd03a8c1e33517c56dafd196.tar.gz
Fix a deadlock between vm_fault() and vm_mmap(): The expected lock ordering
between vm_map and vnode locks is that vm_map locks are acquired first. In revision 1.150 mmap(2) was changed to pass a locked vnode into vm_mmap(). This creates a lock-order reversal when vm_mmap() calls one of the vm_map routines that acquires a vm_map lock. The solution implemented herein is to release the vnode lock in mmap() before calling vm_mmap() and reacquire this lock if necessary in vm_mmap(). Approved by: re (scottl) Reviewed by: jeff, kan, rwatson
Diffstat (limited to 'sys')
-rw-r--r--sys/vm/vm_mmap.c17
1 files changed, 13 insertions, 4 deletions
diff --git a/sys/vm/vm_mmap.c b/sys/vm/vm_mmap.c
index be2aad3..c210a8d 100644
--- a/sys/vm/vm_mmap.c
+++ b/sys/vm/vm_mmap.c
@@ -433,7 +433,6 @@ mmap(td, uap)
goto done;
}
- mtx_unlock(&Giant);
error = 0;
#ifdef MAC
if (handle != NULL && (flags & MAP_SHARED) != 0) {
@@ -441,6 +440,11 @@ mmap(td, uap)
(struct vnode *)handle, prot);
}
#endif
+ if (vp != NULL) {
+ vput(vp);
+ vp = NULL;
+ }
+ mtx_unlock(&Giant);
if (error == 0)
error = vm_mmap(&vms->vm_map, &addr, size, prot, maxprot,
flags, handle, pos);
@@ -1164,7 +1168,7 @@ vm_mmap(vm_map_t map, vm_offset_t *addr, vm_size_t size, vm_prot_t prot,
objtype_t type;
int rv = KERN_SUCCESS;
vm_ooffset_t objsize;
- int docow;
+ int docow, error;
struct thread *td = curthread;
if (size == 0)
@@ -1211,16 +1215,20 @@ vm_mmap(vm_map_t map, vm_offset_t *addr, vm_size_t size, vm_prot_t prot,
} else {
vp = (struct vnode *) handle;
mtx_lock(&Giant);
- ASSERT_VOP_LOCKED(vp, "vm_mmap");
+ error = vget(vp, LK_EXCLUSIVE, td);
+ if (error) {
+ mtx_unlock(&Giant);
+ return (error);
+ }
if (vp->v_type == VCHR) {
type = OBJT_DEVICE;
handle = vp->v_rdev;
} else {
struct vattr vat;
- int error;
error = VOP_GETATTR(vp, &vat, td->td_ucred, td);
if (error) {
+ vput(vp);
mtx_unlock(&Giant);
return (error);
}
@@ -1234,6 +1242,7 @@ vm_mmap(vm_map_t map, vm_offset_t *addr, vm_size_t size, vm_prot_t prot,
flags |= MAP_NOSYNC;
}
}
+ vput(vp);
mtx_unlock(&Giant);
}
OpenPOWER on IntegriCloud