summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArtur Grabowski <art@cvs.openbsd.org>2001-05-10 07:59:07 +0000
committerArtur Grabowski <art@cvs.openbsd.org>2001-05-10 07:59:07 +0000
commit4fc0744584b136b73eee0c8778f998813a5bab13 (patch)
tree1b4e1a8d86cb187f0ab77fc76e0c80df80f95b43
parent39c048cf21de365deb06f965b0af2dcc52f41fd4 (diff)
Some locking protocol fixes and better enforcement of wiring limits.
From NetBSD.
-rw-r--r--sys/arch/hp300/dev/grf.c5
-rw-r--r--sys/arch/i386/i386/gdt.c8
-rw-r--r--sys/arch/mac68k/dev/grf.c5
-rw-r--r--sys/uvm/uvm_extern.h10
-rw-r--r--sys/uvm/uvm_map.c60
-rw-r--r--sys/uvm/uvm_map_i.h5
-rw-r--r--sys/uvm/uvm_mmap.c107
-rw-r--r--sys/vm/vm_map.h165
8 files changed, 283 insertions, 82 deletions
diff --git a/sys/arch/hp300/dev/grf.c b/sys/arch/hp300/dev/grf.c
index 1d0445c18c7..e79d3edd46a 100644
--- a/sys/arch/hp300/dev/grf.c
+++ b/sys/arch/hp300/dev/grf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: grf.c,v 1.9 2001/05/04 22:48:58 aaron Exp $ */
+/* $OpenBSD: grf.c,v 1.10 2001/05/10 07:59:05 art Exp $ */
/* $NetBSD: grf.c,v 1.30 1998/08/20 08:33:41 kleink Exp $ */
/*
@@ -644,7 +644,8 @@ grfmap(dev, addrp, p)
#if defined(UVM)
error = uvm_mmap(&p->p_vmspace->vm_map, (vaddr_t *)addrp,
(vsize_t)len, VM_PROT_ALL, VM_PROT_ALL,
- flags, (caddr_t)&vn, 0);
+ flags, (caddr_t)&vn, 0,
+ p->p_rlimit[RLIMIT_MEMLOCK].rlim_cur);
#else
error = vm_mmap(&p->p_vmspace->vm_map, (vaddr_t *)addrp,
(vsize_t)len, VM_PROT_ALL, VM_PROT_ALL,
diff --git a/sys/arch/i386/i386/gdt.c b/sys/arch/i386/i386/gdt.c
index b5458b4e757..26b5b12cc2f 100644
--- a/sys/arch/i386/i386/gdt.c
+++ b/sys/arch/i386/i386/gdt.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: gdt.c,v 1.14 2001/05/05 23:25:35 art Exp $ */
+/* $OpenBSD: gdt.c,v 1.15 2001/05/10 07:59:05 art Exp $ */
/* $NetBSD: gdt.c,v 1.8 1996/05/03 19:42:06 christos Exp $ */
/*-
@@ -168,7 +168,7 @@ gdt_init()
dynamic_gdt = (union descriptor *)uvm_km_valloc(kernel_map, max_len);
uvm_map_pageable(kernel_map, (vaddr_t)dynamic_gdt,
- (vaddr_t)dynamic_gdt + min_len, FALSE);
+ (vaddr_t)dynamic_gdt + min_len, FALSE, FALSE);
bcopy(gdt, dynamic_gdt, NGDT * sizeof(union descriptor));
setregion(&region, dynamic_gdt, max_len - 1);
@@ -185,7 +185,7 @@ gdt_grow()
new_len = old_len << 1;
uvm_map_pageable(kernel_map, (vaddr_t)dynamic_gdt + old_len,
- (vaddr_t)dynamic_gdt + new_len, FALSE);
+ (vaddr_t)dynamic_gdt + new_len, FALSE, FALSE);
}
void
@@ -197,7 +197,7 @@ gdt_shrink()
gdt_size >>= 1;
new_len = old_len >> 1;
uvm_map_pageable(kernel_map, (vaddr_t)dynamic_gdt + new_len,
- (vaddr_t)dynamic_gdt + old_len, TRUE);
+ (vaddr_t)dynamic_gdt + old_len, TRUE, FALSE);
}
/*
diff --git a/sys/arch/mac68k/dev/grf.c b/sys/arch/mac68k/dev/grf.c
index cfc63247957..71e990f23a0 100644
--- a/sys/arch/mac68k/dev/grf.c
+++ b/sys/arch/mac68k/dev/grf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: grf.c,v 1.10 2001/05/08 17:30:39 aaron Exp $ */
+/* $OpenBSD: grf.c,v 1.11 2001/05/10 07:59:05 art Exp $ */
/* $NetBSD: grf.c,v 1.41 1997/02/24 06:20:04 scottr Exp $ */
/*
@@ -376,7 +376,8 @@ grfmap(dev, addrp, p)
#if defined(UVM)
error = uvm_mmap(&p->p_vmspace->vm_map, (vm_offset_t *)addrp,
- (vm_size_t)len, VM_PROT_ALL, VM_PROT_ALL, flags, (caddr_t)&vn, 0);
+ (vm_size_t)len, VM_PROT_ALL, VM_PROT_ALL, flags, (caddr_t)&vn, 0
+ p->p_rlimit[RLIMIT_MEMLOCK].rlim_cur);
#else
error = vm_mmap(&p->p_vmspace->vm_map, (vm_offset_t *) addrp,
(vm_size_t) len, VM_PROT_ALL, VM_PROT_ALL, flags, (caddr_t) &vn, 0);
diff --git a/sys/uvm/uvm_extern.h b/sys/uvm/uvm_extern.h
index 95b93c48569..bcfd4060cd3 100644
--- a/sys/uvm/uvm_extern.h
+++ b/sys/uvm/uvm_extern.h
@@ -1,5 +1,5 @@
-/* $OpenBSD: uvm_extern.h,v 1.11 2001/05/07 16:08:40 art Exp $ */
-/* $NetBSD: uvm_extern.h,v 1.29 1999/06/17 15:47:22 thorpej Exp $ */
+/* $OpenBSD: uvm_extern.h,v 1.12 2001/05/10 07:59:06 art Exp $ */
+/* $NetBSD: uvm_extern.h,v 1.32 1999/07/02 23:20:58 thorpej Exp $ */
/*
*
@@ -181,10 +181,12 @@ struct uvmexp {
/* swap */
int nswapdev; /* number of configured swap devices in system */
int swpages; /* number of PAGE_SIZE'ed swap pages */
+ int swpguniq; /* number of swap pages in use, not also in RAM */
int swpginuse; /* number of swap pages in use */
int swpgonly; /* number of swap pages in use, not also in RAM */
int nswget; /* number of times fault calls uvm_swap_get() */
int nanon; /* number total of anon's in system */
+ int nanonneeded;/* number of anons currently needed */
int nfreeanon; /* number of free anon's */
/* stat counters */
@@ -324,7 +326,7 @@ void uvm_km_free_poolpage1 __P((vm_map_t, vaddr_t));
int uvm_map __P((vm_map_t, vaddr_t *, vsize_t,
struct uvm_object *, vaddr_t, uvm_flag_t));
int uvm_map_pageable __P((vm_map_t, vaddr_t,
- vaddr_t, boolean_t));
+ vaddr_t, boolean_t, boolean_t));
int uvm_map_pageable_all __P((vm_map_t, int, vsize_t));
boolean_t uvm_map_checkprot __P((vm_map_t, vaddr_t,
vaddr_t, vm_prot_t));
@@ -350,7 +352,7 @@ void uvm_total __P((struct vmtotal *));
/* uvm_mmap.c */
int uvm_mmap __P((vm_map_t, vaddr_t *, vsize_t,
vm_prot_t, vm_prot_t, int,
- caddr_t, vaddr_t));
+ caddr_t, vaddr_t, vsize_t));
/* uvm_page.c */
struct vm_page *uvm_pagealloc_strat __P((struct uvm_object *,
diff --git a/sys/uvm/uvm_map.c b/sys/uvm/uvm_map.c
index 266e8462def..4d98199d524 100644
--- a/sys/uvm/uvm_map.c
+++ b/sys/uvm/uvm_map.c
@@ -1,5 +1,5 @@
-/* $OpenBSD: uvm_map.c,v 1.13 2001/05/07 16:08:40 art Exp $ */
-/* $NetBSD: uvm_map.c,v 1.58 1999/06/17 00:24:10 thorpej Exp $ */
+/* $OpenBSD: uvm_map.c,v 1.14 2001/05/10 07:59:06 art Exp $ */
+/* $NetBSD: uvm_map.c,v 1.60 1999/07/01 20:07:05 thorpej Exp $ */
/*
* Copyright (c) 1997 Charles D. Cranor and Washington University.
@@ -187,22 +187,6 @@ static void uvm_map_entry_unwire __P((vm_map_t, vm_map_entry_t));
* local inlines
*/
-/* XXX Should not exist! */
-#define vm_map_downgrade(map) \
- (void) lockmgr(&(map)->lock, LK_DOWNGRADE, NULL, curproc)
-
-/* XXX Should not exist! */
-#ifdef DIAGNOSTIC
-#define vm_map_upgrade(map) \
-do { \
- if (lockmgr(&(map)->lock, LK_UPGRADE, NULL, curproc) != 0) \
- panic("vm_map_upgrade: failed to upgrade lock"); \
-} while (0)
-#else
-#define vm_map_upgrade(map) \
- (void) lockmgr(&(map)->lock, LK_UPGRADE, NULL, curproc)
-#endif /* DIAGNOSTIC */
-
/*
* uvm_mapent_alloc: allocate a map entry
*
@@ -1976,18 +1960,24 @@ uvm_map_advice(map, start, end, new_advice)
* for that, use uvm_fault_wire()/uvm_fault_unwire() (see uvm_vslock()).
* => regions sepcified as not pageable require lock-down (wired) memory
* and page tables.
- * => map must not be locked.
+ * => map must never be read-locked
+ * => if islocked is TRUE, map is already write-locked
+ * => we always unlock the map, since we must downgrade to a read-lock
+ * to call uvm_fault_wire()
* => XXXCDC: check this and try and clean it up.
*/
int
-uvm_map_pageable(map, start, end, new_pageable)
+uvm_map_pageable(map, start, end, new_pageable, islocked)
vm_map_t map;
vaddr_t start, end;
- boolean_t new_pageable;
+ boolean_t new_pageable, islocked;
{
vm_map_entry_t entry, start_entry, failed_entry;
int rv;
+#ifdef DIAGNOSTIC
+ u_int timestamp_save;
+#endif
UVMHIST_FUNC("uvm_map_pageable"); UVMHIST_CALLED(maphist);
UVMHIST_LOG(maphist,"(map=0x%x,start=0x%x,end=0x%x,new_pageable=0x%x)",
map, start, end, new_pageable);
@@ -1997,7 +1987,8 @@ uvm_map_pageable(map, start, end, new_pageable)
panic("uvm_map_pageable: map %p not pageable", map);
#endif
- vm_map_lock(map);
+ if (islocked == FALSE)
+ vm_map_lock(map);
VM_MAP_RANGE_CHECK(map, start, end);
/*
@@ -2130,6 +2121,10 @@ uvm_map_pageable(map, start, end, new_pageable)
* Pass 2.
*/
+#ifdef DIAGNOSTIC
+ timestamp_save = map->timestamp;
+#endif
+ vm_map_busy(map);
vm_map_downgrade(map);
rv = 0;
@@ -2155,6 +2150,12 @@ uvm_map_pageable(map, start, end, new_pageable)
* Get back to an exclusive (write) lock.
*/
vm_map_upgrade(map);
+ vm_map_unbusy(map);
+
+#ifdef DIAGNOSTIC
+ if (timestamp_save != map->timestamp)
+ panic("uvm_map_pageable: stale map");
+#endif
/*
* first drop the wiring count on all the entries
@@ -2183,6 +2184,7 @@ uvm_map_pageable(map, start, end, new_pageable)
}
/* We are holding a read lock here. */
+ vm_map_unbusy(map);
vm_map_unlock_read(map);
UVMHIST_LOG(maphist,"<- done (OK WIRE)",0,0,0,0);
@@ -2207,6 +2209,9 @@ uvm_map_pageable_all(map, flags, limit)
vm_map_entry_t entry, failed_entry;
vsize_t size;
int rv;
+#ifdef DIAGNOSTIC
+ u_int timestamp_save;
+#endif
UVMHIST_FUNC("uvm_map_pageable_all"); UVMHIST_CALLED(maphist);
UVMHIST_LOG(maphist,"(map=0x%x,flags=0x%x)", map, flags, 0, 0);
@@ -2335,6 +2340,10 @@ uvm_map_pageable_all(map, flags, limit)
* Pass 3.
*/
+#ifdef DIAGNOSTIC
+ timestamp_save = map->timestamp;
+#endif
+ vm_map_busy(map);
vm_map_downgrade(map);
rv = KERN_SUCCESS;
@@ -2359,6 +2368,12 @@ uvm_map_pageable_all(map, flags, limit)
* Get back an exclusive (write) lock.
*/
vm_map_upgrade(map);
+ vm_map_unbusy(map);
+
+#ifdef DIAGNOSTIC
+ if (timestamp_save != map->timestamp)
+ panic("uvm_map_pageable_all: stale map");
+#endif
/*
* first drop the wiring count on all the entries
@@ -2385,6 +2400,7 @@ uvm_map_pageable_all(map, flags, limit)
}
/* We are holding a read lock here. */
+ vm_map_unbusy(map);
vm_map_unlock_read(map);
UVMHIST_LOG(maphist,"<- done (OK WIRE)",0,0,0,0);
diff --git a/sys/uvm/uvm_map_i.h b/sys/uvm/uvm_map_i.h
index f2849603770..39262affb39 100644
--- a/sys/uvm/uvm_map_i.h
+++ b/sys/uvm/uvm_map_i.h
@@ -1,5 +1,5 @@
-/* $OpenBSD: uvm_map_i.h,v 1.6 2001/03/09 14:20:51 art Exp $ */
-/* $NetBSD: uvm_map_i.h,v 1.15 1999/06/14 22:05:23 thorpej Exp $ */
+/* $OpenBSD: uvm_map_i.h,v 1.7 2001/05/10 07:59:06 art Exp $ */
+/* $NetBSD: uvm_map_i.h,v 1.16 1999/07/01 20:07:05 thorpej Exp $ */
/*
* Copyright (c) 1997 Charles D. Cranor and Washington University.
@@ -127,6 +127,7 @@ uvm_map_setup(map, min, max, flags)
lockinit(&map->lock, PVM, "vmmaplk", 0, 0);
simple_lock_init(&map->ref_lock);
simple_lock_init(&map->hint_lock);
+ simple_lock_init(&map->flags_lock);
/*
* If the map is interrupt safe, place it on the list
diff --git a/sys/uvm/uvm_mmap.c b/sys/uvm/uvm_mmap.c
index 3aa87738299..56a4cad7503 100644
--- a/sys/uvm/uvm_mmap.c
+++ b/sys/uvm/uvm_mmap.c
@@ -1,5 +1,5 @@
-/* $OpenBSD: uvm_mmap.c,v 1.11 2001/05/05 21:26:46 art Exp $ */
-/* $NetBSD: uvm_mmap.c,v 1.23 1999/06/16 17:25:39 minoura Exp $ */
+/* $OpenBSD: uvm_mmap.c,v 1.12 2001/05/10 07:59:06 art Exp $ */
+/* $NetBSD: uvm_mmap.c,v 1.28 1999/07/06 02:31:05 cgd Exp $ */
/*
* Copyright (c) 1997 Charles D. Cranor and Washington University.
@@ -336,6 +336,15 @@ sys_mmap(p, v, retval)
pos = SCARG(uap, pos);
/*
+ * Fixup the old deprecated MAP_COPY into MAP_PRIVATE, and
+ * validate the flags.
+ */
+ if (flags & MAP_COPY)
+ flags = (flags & ~MAP_COPY) | MAP_PRIVATE;
+ if ((flags & (MAP_SHARED|MAP_PRIVATE)) == (MAP_SHARED|MAP_PRIVATE))
+ return (EINVAL);
+
+ /*
* make sure that the newsize fits within a vaddr_t
* XXX: need to revise addressing data types
*/
@@ -419,7 +428,7 @@ sys_mmap(p, v, retval)
*
* XXX: how does MAP_ANON fit in the picture?
*/
- if ((flags & (MAP_SHARED|MAP_PRIVATE|MAP_COPY)) == 0) {
+ if ((flags & (MAP_SHARED|MAP_PRIVATE)) == 0) {
#if defined(DEBUG)
printf("WARNING: defaulted mmap() share type to "
"%s (pid %d comm %s)\n", vp->v_type == VCHR ?
@@ -489,22 +498,39 @@ sys_mmap(p, v, retval)
handle = (caddr_t)vp;
} else { /* MAP_ANON case */
-
+ /*
+ * XXX What do we do about (MAP_SHARED|MAP_PRIVATE) == 0?
+ */
if (fd != -1)
return (EINVAL);
-is_anon: /* label for SunOS style /dev/zero */
+ is_anon: /* label for SunOS style /dev/zero */
handle = NULL;
maxprot = VM_PROT_ALL;
pos = 0;
}
/*
+ * XXX (in)sanity check. We don't do proper datasize checking
+ * XXX for anonymous (or private writable) mmap(). However,
+ * XXX know that if we're trying to allocate more than the amount
+ * XXX remaining under our current data size limit, _that_ should
+ * XXX be disallowed.
+ */
+ if ((flags & MAP_ANON) != 0 ||
+ ((flags & MAP_PRIVATE) != 0 && (prot & PROT_WRITE) != 0)) {
+ if (size >
+ (p->p_rlimit[RLIMIT_DATA].rlim_cur - ctob(p->p_vmspace->vm_dsize))) {
+ return (ENOMEM);
+ }
+ }
+
+ /*
* now let kernel internal function uvm_mmap do the work.
*/
error = uvm_mmap(&p->p_vmspace->vm_map, &addr, size, prot, maxprot,
- flags, handle, pos);
+ flags, handle, pos, p->p_rlimit[RLIMIT_MEMLOCK].rlim_cur);
if (error == 0)
/* remember to add offset */
@@ -893,7 +919,8 @@ sys_mlock(p, v, retval)
return (error);
#endif
- error = uvm_map_pageable(&p->p_vmspace->vm_map, addr, addr+size, FALSE);
+ error = uvm_map_pageable(&p->p_vmspace->vm_map, addr, addr+size, FALSE,
+ FALSE);
return (error == KERN_SUCCESS ? 0 : ENOMEM);
}
@@ -939,7 +966,8 @@ sys_munlock(p, v, retval)
return (error);
#endif
- error = uvm_map_pageable(&p->p_vmspace->vm_map, addr, addr+size, TRUE);
+ error = uvm_map_pageable(&p->p_vmspace->vm_map, addr, addr+size, TRUE,
+ FALSE);
return (error == KERN_SUCCESS ? 0 : ENOMEM);
}
@@ -956,7 +984,6 @@ sys_mlockall(p, v, retval)
struct sys_mlockall_args /* {
syscallarg(int) flags;
} */ *uap = v;
- vsize_t limit;
int error, flags;
flags = SCARG(uap, flags);
@@ -965,16 +992,13 @@ sys_mlockall(p, v, retval)
(flags & ~(MCL_CURRENT|MCL_FUTURE)) != 0)
return (EINVAL);
-#ifdef pmap_wired_count
- /* Actually checked in uvm_map_pageable_all() */
- limit = p->p_rlimit[RLIMIT_MEMLOCK].rlim_cur;
-#else
- limit = 0;
+#ifndef pmap_wired_count
if ((error = suser(p->p_ucred, &p->p_acflag)) != 0)
return (error);
#endif
- error = uvm_map_pageable_all(&p->p_vmspace->vm_map, flags, limit);
+ error = uvm_map_pageable_all(&p->p_vmspace->vm_map, flags,
+ p->p_rlimit[RLIMIT_MEMLOCK].rlim_cur);
switch (error) {
case KERN_SUCCESS:
error = 0;
@@ -1020,7 +1044,7 @@ sys_munlockall(p, v, retval)
*/
int
-uvm_mmap(map, addr, size, prot, maxprot, flags, handle, foff)
+uvm_mmap(map, addr, size, prot, maxprot, flags, handle, foff, locklimit)
vm_map_t map;
vaddr_t *addr;
vsize_t size;
@@ -1028,6 +1052,7 @@ uvm_mmap(map, addr, size, prot, maxprot, flags, handle, foff)
int flags;
caddr_t handle; /* XXX: VNODE? */
vaddr_t foff;
+ vsize_t locklimit;
{
struct uvm_object *uobj;
struct vnode *vp;
@@ -1141,8 +1166,51 @@ uvm_mmap(map, addr, size, prot, maxprot, flags, handle, foff)
retval = uvm_map(map, addr, size, uobj, foff, uvmflag);
- if (retval == KERN_SUCCESS)
- return(0);
+ if (retval == KERN_SUCCESS) {
+ /*
+ * POSIX 1003.1b -- if our address space was configured
+ * to lock all future mappings, wire the one we just made.
+ */
+ if (prot == VM_PROT_NONE) {
+ /*
+ * No more work to do in this case.
+ */
+ return (0);
+ }
+
+ vm_map_lock(map);
+
+ if (map->flags & VM_MAP_WIREFUTURE) {
+ /*
+ * uvm_map_pageable() always returns the map
+ * unlocked.
+ */
+ if ((atop(size) + uvmexp.wired) > uvmexp.wiredmax
+#ifdef pmap_wired_count
+ || (locklimit != 0 && (size +
+ ptoa(pmap_wired_count(vm_map_pmap(map)))) >
+ locklimit)
+#endif
+ ) {
+ retval = KERN_RESOURCE_SHORTAGE;
+ /* unmap the region! */
+ (void) uvm_unmap(map, *addr, *addr + size);
+ goto bad;
+ }
+ retval = uvm_map_pageable(map, *addr, *addr + size,
+ FALSE, TRUE);
+ if (retval != KERN_SUCCESS) {
+ /* unmap the region! */
+ (void) uvm_unmap(map, *addr, *addr + size);
+ goto bad;
+ }
+ return (0);
+ }
+
+ vm_map_unlock(map);
+
+ return (0);
+ }
/*
* errors: first detach from the uobj, if any.
@@ -1151,10 +1219,13 @@ uvm_mmap(map, addr, size, prot, maxprot, flags, handle, foff)
if (uobj)
uobj->pgops->pgo_detach(uobj);
+ bad:
switch (retval) {
case KERN_INVALID_ADDRESS:
case KERN_NO_SPACE:
return(ENOMEM);
+ case KERN_RESOURCE_SHORTAGE:
+ return (EAGAIN);
case KERN_PROTECTION_FAILURE:
return(EACCES);
}
diff --git a/sys/vm/vm_map.h b/sys/vm/vm_map.h
index 9878db88895..47107fec0e7 100644
--- a/sys/vm/vm_map.h
+++ b/sys/vm/vm_map.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: vm_map.h,v 1.12 2001/03/09 14:20:52 art Exp $ */
+/* $OpenBSD: vm_map.h,v 1.13 2001/05/10 07:59:06 art Exp $ */
/* $NetBSD: vm_map.h,v 1.11 1995/03/26 20:39:10 jtc Exp $ */
/*
@@ -149,6 +149,54 @@ struct vm_map_entry {
* by address. A single hint is provided to start
* searches again from the last successful search,
* insertion, or removal.
+#if defined(UVM)
+ *
+ * LOCKING PROTOCOL NOTES:
+ * -----------------------
+ *
+ * VM map locking is a little complicated. There are both shared
+ * and exclusive locks on maps. However, it is sometimes required
+ * to downgrade an exclusive lock to a shared lock, and upgrade to
+ * an exclusive lock again (to perform error recovery). However,
+ * another thread *must not* queue itself to receive an exclusive
+ * lock while before we upgrade back to exclusive, otherwise the
+ * error recovery becomes extremely difficult, if not impossible.
+ *
+ * In order to prevent this scenario, we introduce the notion of
+ * a `busy' map. A `busy' map is read-locked, but other threads
+ * attempting to write-lock wait for this flag to clear before
+ * entering the lock manager. A map may only be marked busy
+ * when the map is write-locked (and then the map must be downgraded
+ * to read-locked), and may only be marked unbusy by the thread
+ * which marked it busy (holding *either* a read-lock or a
+ * write-lock, the latter being gained by an upgrade).
+ *
+ * Access to the map `flags' member is controlled by the `flags_lock'
+ * simple lock. Note that some flags are static (set once at map
+ * creation time, and never changed), and thus require no locking
+ * to check those flags. All flags which are r/w must be set or
+ * cleared while the `flags_lock' is asserted. Additional locking
+ * requirements are:
+ *
+ * VM_MAP_PAGEABLE r/o static flag; no locking required
+ *
+ * VM_MAP_INTRSAFE r/o static flag; no locking required
+ *
+ * VM_MAP_WIREFUTURE r/w; may only be set or cleared when
+ * map is write-locked. may be tested
+ * without asserting `flags_lock'.
+ *
+ * VM_MAP_BUSY r/w; may only be set when map is
+ * write-locked, may only be cleared by
+ * thread which set it, map read-locked
+ * or write-locked. must be tested
+ * while `flags_lock' is asserted.
+ *
+ * VM_MAP_WANTLOCK r/w; may only be set when the map
+ * is busy, and thread is attempting
+ * to write-lock. must be tested
+ * while `flags_lock' is asserted.
+#endif
*/
struct vm_map {
struct pmap * pmap; /* Physical map */
@@ -165,14 +213,8 @@ struct vm_map {
simple_lock_data_t hint_lock; /* lock for hint storage */
vm_map_entry_t first_free; /* First free space hint */
#ifdef UVM
- /*
- * Locking note: read-only flags need not be locked to read
- * them; they are set once at map creation time, and never
- * changed again. Only read-write flags require that the
- * appropriate map lock be acquired before reading or writing
- * the flag.
- */
int flags; /* flags */
+ simple_lock_data_t flags_lock; /* Lock for flags field */
#else
boolean_t entries_pageable; /* map entries pageable?? */
#endif
@@ -186,6 +228,9 @@ struct vm_map {
#define VM_MAP_PAGEABLE 0x01 /* ro: entries are pageable*/
#define VM_MAP_INTRSAFE 0x02 /* ro: interrupt safe map */
#define VM_MAP_WIREFUTURE 0x04 /* rw: wire future mappings */
+#define VM_MAP_BUSY 0x08 /* rw: map is busy */
+#define VM_MAP_WANTLOCK 0x10 /* rw: want to write-lock */
+
/*
* Interrupt-safe maps must also be kept on a special list,
* to assist uvm_fault() in avoiding locking problems.
@@ -259,6 +304,14 @@ typedef struct {
*
* vm_map_unlock_read: release a shared lock on a map.
*
+ * vm_map_downgrade: downgrade an exclusive lock to a shared lock.
+ *
+ * vm_map_upgrade: upgrade a shared lock to an exclusive lock.
+ *
+ * vm_map_busy: mark a map as busy.
+ *
+ * vm_map_unbusy: clear busy status on a map.
+ *
* Note that "intrsafe" maps use only exclusive, spin locks. We simply
* use the sleep lock's interlock for this.
*/
@@ -266,9 +319,11 @@ typedef struct {
#ifdef _KERNEL
/* XXX: clean up later */
#include <sys/time.h>
-#include <sys/proc.h> /* XXX for curproc and p_pid */
+#include <sys/proc.h> /* for tsleep(), wakeup() */
+#include <sys/systm.h> /* for panic() */
static __inline boolean_t vm_map_lock_try __P((vm_map_t));
+static __inline void vm_map_lock __P((vm_map_t));
static __inline boolean_t
vm_map_lock_try(map)
@@ -278,8 +333,15 @@ vm_map_lock_try(map)
if (map->flags & VM_MAP_INTRSAFE)
rv = simple_lock_try(&map->lock.lk_interlock);
- else
- rv = (lockmgr(&map->lock, LK_EXCLUSIVE|LK_NOWAIT, NULL, curproc) == 0);
+ else {
+ simple_lock(&map->flags_lock);
+ if (map->flags & VM_MAP_BUSY) {
+ simple_unlock(&map->flags_lock);
+ return (FALSE);
+ }
+ rv = (lockmgr(&map->lock, LK_EXCLUSIVE|LK_NOWAIT|LK_INTERLOCK,
+ &map->flags_lock, curproc) == 0);
+ }
if (rv)
map->timestamp++;
@@ -287,25 +349,39 @@ vm_map_lock_try(map)
return (rv);
}
+static __inline void
+vm_map_lock(map)
+ vm_map_t map;
+{
+ int error;
+
+ if (map->flags & VM_MAP_INTRSAFE) {
+ simple_lock(&map->lock.lk_interlock);
+ return;
+ }
+
+ try_again:
+ simple_lock(&map->flags_lock);
+ if (map->flags & VM_MAP_BUSY) {
+ map->flags |= VM_MAP_WANTLOCK;
+ simple_unlock(&map->flags_lock);
+ (void) tsleep(&map->flags, PVM, "vmmapbsy", 0);
+ goto try_again;
+ }
+
+ error = lockmgr(&map->lock, LK_EXCLUSIVE|LK_SLEEPFAIL|LK_INTERLOCK,
+ &map->flags_lock, curproc);
+
+ if (error) {
#ifdef DIAGNOSTIC
-#define _vm_map_lock(map) \
-do { \
- if (lockmgr(&(map)->lock, LK_EXCLUSIVE, NULL, curproc) != 0) \
- panic("vm_map_lock: failed to get lock"); \
-} while (0)
-#else
-#define _vm_map_lock(map) \
- (void) lockmgr(&(map)->lock, LK_EXCLUSIVE, NULL, curproc)
+ if (error != ENOLCK)
+ panic("vm_map_lock: failed to get lock");
#endif
-
-#define vm_map_lock(map) \
-do { \
- if ((map)->flags & VM_MAP_INTRSAFE) \
- simple_lock(&(map)->lock.lk_interlock); \
- else \
- _vm_map_lock((map)); \
- (map)->timestamp++; \
-} while (0)
+ goto try_again;
+ }
+
+ (map)->timestamp++;
+}
#ifdef DIAGNOSTIC
#define vm_map_lock_read(map) \
@@ -329,6 +405,39 @@ do { \
#define vm_map_unlock_read(map) \
(void) lockmgr(&(map)->lock, LK_RELEASE, NULL, curproc)
+
+#define vm_map_downgrade(map) \
+ (void) lockmgr(&(map)->lock, LK_DOWNGRADE, NULL, curproc)
+
+#ifdef DIAGNOSTIC
+#define vm_map_upgrade(map) \
+do { \
+ if (lockmgr(&(map)->lock, LK_UPGRADE, NULL, curproc) != 0) \
+ panic("vm_map_upgrade: failed to upgrade lock"); \
+} while (0)
+#else
+#define vm_map_upgrade(map) \
+ (void) lockmgr(&(map)->lock, LK_UPGRADE, NULL, curproc)
+#endif
+
+#define vm_map_busy(map) \
+do { \
+ simple_lock(&(map)->flags_lock); \
+ (map)->flags |= VM_MAP_BUSY; \
+ simple_unlock(&(map)->flags_lock); \
+} while (0)
+
+#define vm_map_unbusy(map) \
+do { \
+ int oflags; \
+ \
+ simple_lock(&(map)->flags_lock); \
+ oflags = (map)->flags; \
+ (map)->flags &= ~(VM_MAP_BUSY|VM_MAP_WANTLOCK); \
+ simple_unlock(&(map)->flags_lock); \
+ if (oflags & VM_MAP_WANTLOCK) \
+ wakeup(&(map)->flags); \
+} while (0)
#endif /* _KERNEL */
#else /* UVM */
/*