diff options
author | Artur Grabowski <art@cvs.openbsd.org> | 2001-05-10 07:59:07 +0000 |
---|---|---|
committer | Artur Grabowski <art@cvs.openbsd.org> | 2001-05-10 07:59:07 +0000 |
commit | 4fc0744584b136b73eee0c8778f998813a5bab13 (patch) | |
tree | 1b4e1a8d86cb187f0ab77fc76e0c80df80f95b43 | |
parent | 39c048cf21de365deb06f965b0af2dcc52f41fd4 (diff) |
Some locking protocol fixes and better enforcement of wiring limits.
From NetBSD.
-rw-r--r-- | sys/arch/hp300/dev/grf.c | 5 | ||||
-rw-r--r-- | sys/arch/i386/i386/gdt.c | 8 | ||||
-rw-r--r-- | sys/arch/mac68k/dev/grf.c | 5 | ||||
-rw-r--r-- | sys/uvm/uvm_extern.h | 10 | ||||
-rw-r--r-- | sys/uvm/uvm_map.c | 60 | ||||
-rw-r--r-- | sys/uvm/uvm_map_i.h | 5 | ||||
-rw-r--r-- | sys/uvm/uvm_mmap.c | 107 | ||||
-rw-r--r-- | sys/vm/vm_map.h | 165 |
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(®ion, 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 */ /* |