diff options
author | Konstantin Belousov <kib@FreeBSD.org> | 2009-02-08 20:39:17 +0000 |
---|---|---|
committer | Konstantin Belousov <kib@FreeBSD.org> | 2009-02-08 20:39:17 +0000 |
commit | 897d81a0202f3b068edfd20736cb55137bc88a12 (patch) | |
tree | 090d271c805930a672a18b2da7d0cd0774d02586 /sys/vm | |
parent | e53fa61bf2819349d66b210c5dd217379aea2a20 (diff) |
Do not call vm_object_deallocate() from vm_map_delete(), because we
hold the map lock there, and might need the vnode lock for OBJT_VNODE
objects. Postpone object deallocation until caller of vm_map_delete()
drops the map lock. Link the map entries to be freed into the freelist,
that is released by the new helper function vm_map_entry_free_freelist().
Reviewed by: tegge, alc
Tested by: pho
Notes
Notes:
svn path=/head/; revision=188334
Diffstat (limited to 'sys/vm')
-rw-r--r-- | sys/vm/vm_kern.c | 12 | ||||
-rw-r--r-- | sys/vm/vm_map.c | 40 | ||||
-rw-r--r-- | sys/vm/vm_map.h | 4 | ||||
-rw-r--r-- | sys/vm/vm_mmap.c | 5 | ||||
-rw-r--r-- | sys/vm/vm_unix.c | 5 |
5 files changed, 52 insertions, 14 deletions
diff --git a/sys/vm/vm_kern.c b/sys/vm/vm_kern.c index 9a1ac634b7cf..b43805885a50 100644 --- a/sys/vm/vm_kern.c +++ b/sys/vm/vm_kern.c @@ -271,7 +271,7 @@ kmem_malloc(map, size, flags) int flags; { vm_offset_t offset, i; - vm_map_entry_t entry; + vm_map_entry_t entry, freelist; vm_offset_t addr; vm_page_t m; int pflags; @@ -355,8 +355,10 @@ retry: vm_page_unlock_queues(); } VM_OBJECT_UNLOCK(kmem_object); - vm_map_delete(map, addr, addr + size); + freelist = NULL; + vm_map_delete(map, addr, addr + size, &freelist); vm_map_unlock(map); + vm_map_entry_free_freelist(map, freelist); return (0); } if (flags & M_ZERO && (m->flags & PG_ZERO) == 0) @@ -455,14 +457,18 @@ kmem_free_wakeup(map, addr, size) vm_offset_t addr; vm_size_t size; { + vm_map_entry_t freelist; + freelist = NULL; vm_map_lock(map); - (void) vm_map_delete(map, trunc_page(addr), round_page(addr + size)); + (void) vm_map_delete(map, trunc_page(addr), round_page(addr + size), + &freelist); if (map->needs_wakeup) { map->needs_wakeup = FALSE; vm_map_wakeup(map); } vm_map_unlock(map); + vm_map_entry_free_freelist(map, freelist); } /* diff --git a/sys/vm/vm_map.c b/sys/vm/vm_map.c index 281f4366accb..43dad2bcf595 100644 --- a/sys/vm/vm_map.c +++ b/sys/vm/vm_map.c @@ -1261,16 +1261,19 @@ vm_map_fixed(vm_map_t map, vm_object_t object, vm_ooffset_t offset, vm_offset_t start, vm_size_t length, vm_prot_t prot, vm_prot_t max, int cow) { + vm_map_entry_t freelist; vm_offset_t end; int result; - vm_map_lock(map); end = start + length; + freelist = NULL; + vm_map_lock(map); VM_MAP_RANGE_CHECK(map, start, end); - (void) vm_map_delete(map, start, end); + (void) vm_map_delete(map, start, end, &freelist); result = vm_map_insert(map, object, offset, start, end, prot, max, cow); vm_map_unlock(map); + vm_map_entry_free_freelist(map, freelist); return (result); } @@ -2392,6 +2395,23 @@ vm_map_entry_unwire(vm_map_t map, vm_map_entry_t entry) entry->wired_count = 0; } +void +vm_map_entry_free_freelist(vm_map_t map, vm_map_entry_t freelist) +{ + vm_map_entry_t e; + vm_object_t object; + + while (freelist != NULL) { + e = freelist; + freelist = freelist->next; + if ((e->eflags & MAP_ENTRY_IS_SUB_MAP) == 0) { + object = e->object.vm_object; + vm_object_deallocate(object); + } + vm_map_entry_dispose(map, e); + } +} + /* * vm_map_entry_delete: [ internal use only ] * @@ -2424,10 +2444,8 @@ vm_map_entry_delete(vm_map_t map, vm_map_entry_t entry) object->size = offidxstart; } VM_OBJECT_UNLOCK(object); - vm_object_deallocate(object); - } - - vm_map_entry_dispose(map, entry); + } else + entry->object.vm_object = NULL; } /* @@ -2437,7 +2455,8 @@ vm_map_entry_delete(vm_map_t map, vm_map_entry_t entry) * map. */ int -vm_map_delete(vm_map_t map, vm_offset_t start, vm_offset_t end) +vm_map_delete(vm_map_t map, vm_offset_t start, vm_offset_t end, + vm_map_entry_t *freelist) { vm_map_entry_t entry; vm_map_entry_t first_entry; @@ -2514,6 +2533,8 @@ vm_map_delete(vm_map_t map, vm_offset_t start, vm_offset_t end) * modify bits will be set in the wrong object!) */ vm_map_entry_delete(map, entry); + entry->next = *freelist; + *freelist = entry; entry = next; } return (KERN_SUCCESS); @@ -2528,12 +2549,15 @@ vm_map_delete(vm_map_t map, vm_offset_t start, vm_offset_t end) int vm_map_remove(vm_map_t map, vm_offset_t start, vm_offset_t end) { + vm_map_entry_t freelist; int result; + freelist = NULL; vm_map_lock(map); VM_MAP_RANGE_CHECK(map, start, end); - result = vm_map_delete(map, start, end); + result = vm_map_delete(map, start, end, &freelist); vm_map_unlock(map); + vm_map_entry_free_freelist(map, freelist); return (result); } diff --git a/sys/vm/vm_map.h b/sys/vm/vm_map.h index 0c3ddc881cdb..82de3adf2948 100644 --- a/sys/vm/vm_map.h +++ b/sys/vm/vm_map.h @@ -157,6 +157,8 @@ vm_map_entry_system_wired_count(vm_map_entry_t entry) { return (entry->wired_count - vm_map_entry_user_wired_count(entry)); } + +void vm_map_entry_free_freelist(vm_map_t map, vm_map_entry_t freelist); #endif /* _KERNEL */ /* @@ -336,7 +338,7 @@ long vmspace_wired_count(struct vmspace *vmspace); #ifdef _KERNEL boolean_t vm_map_check_protection (vm_map_t, vm_offset_t, vm_offset_t, vm_prot_t); vm_map_t vm_map_create(pmap_t, vm_offset_t, vm_offset_t); -int vm_map_delete (vm_map_t, vm_offset_t, vm_offset_t); +int vm_map_delete(vm_map_t, vm_offset_t, vm_offset_t, vm_map_entry_t *); int vm_map_find(vm_map_t, vm_object_t, vm_ooffset_t, vm_offset_t *, vm_size_t, int, vm_prot_t, vm_prot_t, int); int vm_map_fixed(vm_map_t, vm_object_t, vm_ooffset_t, vm_offset_t, vm_size_t, diff --git a/sys/vm/vm_mmap.c b/sys/vm/vm_mmap.c index 2d668c42b1c4..4ee9dfc57926 100644 --- a/sys/vm/vm_mmap.c +++ b/sys/vm/vm_mmap.c @@ -552,6 +552,7 @@ munmap(td, uap) vm_offset_t addr; vm_size_t size, pageoff; vm_map_t map; + vm_map_entry_t freelist; addr = (vm_offset_t) uap->addr; size = uap->len; @@ -571,6 +572,7 @@ munmap(td, uap) map = &td->td_proc->p_vmspace->vm_map; if (addr < vm_map_min(map) || addr + size > vm_map_max(map)) return (EINVAL); + freelist = NULL; vm_map_lock(map); #ifdef HWPMC_HOOKS /* @@ -593,8 +595,9 @@ munmap(td, uap) } #endif /* returns nothing but KERN_SUCCESS anyway */ - vm_map_delete(map, addr, addr + size); + vm_map_delete(map, addr, addr + size, &freelist); vm_map_unlock(map); + vm_map_entry_free_freelist(map, freelist); return (0); } diff --git a/sys/vm/vm_unix.c b/sys/vm/vm_unix.c index cd98be975e34..80921023450f 100644 --- a/sys/vm/vm_unix.c +++ b/sys/vm/vm_unix.c @@ -72,6 +72,7 @@ obreak(td, uap) struct obreak_args *uap; { struct vmspace *vm = td->td_proc->p_vmspace; + vm_map_entry_t freelist; vm_offset_t new, old, base; rlim_t datalim, vmemlim; int rv; @@ -85,6 +86,7 @@ obreak(td, uap) do_map_wirefuture = FALSE; new = round_page((vm_offset_t)uap->nsize); + freelist = NULL; vm_map_lock(&vm->vm_map); base = round_page((vm_offset_t) vm->vm_daddr); @@ -138,7 +140,7 @@ obreak(td, uap) do_map_wirefuture = TRUE; } } else if (new < old) { - rv = vm_map_delete(&vm->vm_map, new, old); + rv = vm_map_delete(&vm->vm_map, new, old, &freelist); if (rv != KERN_SUCCESS) { error = ENOMEM; goto done; @@ -147,6 +149,7 @@ obreak(td, uap) } done: vm_map_unlock(&vm->vm_map); + vm_map_entry_free_freelist(&vm->vm_map, freelist); if (do_map_wirefuture) (void) vm_map_wire(&vm->vm_map, old, new, |