* Fixed the strange vm_cache_ref reference count mechanism: now, a fresh

vm_cache_ref starts with a reference count of 1. When acquiring a vm_cache,
  you no longer need to worry if that should go through the vm_store, or not;
  as it now always does.
* map_backing_store() no longer needs to play with the vm_cache_ref
  references.
* that simplified some code.
* vfs_get_vnode_cache() now grabs a reference to the cache, if successful.
* better balanced vnode ownership on vnode_store creation (vnode_store
  released the vnode before if its creation failed).


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@15641 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler
2005-12-21 17:00:03 +00:00
parent d5abe15e84
commit ea2cd27e57
6 changed files with 77 additions and 76 deletions
+1 -1
View File
@@ -22,7 +22,7 @@ extern "C" {
status_t vm_cache_init(struct kernel_args *args); status_t vm_cache_init(struct kernel_args *args);
vm_cache *vm_cache_create(vm_store *store); vm_cache *vm_cache_create(vm_store *store);
vm_cache_ref *vm_cache_ref_create(vm_cache *cache); vm_cache_ref *vm_cache_ref_create(vm_cache *cache);
void vm_cache_acquire_ref(vm_cache_ref *cache_ref, bool acquire_store_ref); void vm_cache_acquire_ref(vm_cache_ref *cache_ref);
void vm_cache_release_ref(vm_cache_ref *cache_ref); void vm_cache_release_ref(vm_cache_ref *cache_ref);
vm_page *vm_cache_lookup_page(vm_cache_ref *cacheRef, off_t page); vm_page *vm_cache_lookup_page(vm_cache_ref *cacheRef, off_t page);
void vm_cache_insert_page(vm_cache_ref *cacheRef, vm_page *page, off_t offset); void vm_cache_insert_page(vm_cache_ref *cacheRef, vm_page *page, off_t offset);
+1
View File
@@ -947,6 +947,7 @@ cache_prefetch_vnode(void *vnode, off_t offset, size_t size)
out: out:
mutex_unlock(&cache->lock); mutex_unlock(&cache->lock);
vm_cache_release_ref(cache);
} }
+2 -4
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright 2004, Axel Dörfler, axeld@pinc-software.de. * Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
*/ */
@@ -112,10 +112,8 @@ extern "C" vm_store *
vm_create_vnode_store(void *vnode) vm_create_vnode_store(void *vnode)
{ {
vnode_store *store = (vnode_store *)malloc(sizeof(struct vnode_store)); vnode_store *store = (vnode_store *)malloc(sizeof(struct vnode_store));
if (store == NULL) { if (store == NULL)
vfs_put_vnode(vnode);
return NULL; return NULL;
}
store->vm.ops = &sStoreOps; store->vm.ops = &sStoreOps;
store->vm.cache = NULL; store->vm.cache = NULL;
+9 -1
View File
@@ -2805,12 +2805,19 @@ vfs_write_pages(void *_vnode, void *cookie, off_t pos, const iovec *vecs, size_t
} }
/** Gets the vnode's vm_cache object. If it didn't have one, it will be
* created if \a allocate is \c true.
* In case it's successful, it will also grab a reference to the cache
* it returns.
*/
extern "C" status_t extern "C" status_t
vfs_get_vnode_cache(void *_vnode, vm_cache_ref **_cache, bool allocate) vfs_get_vnode_cache(void *_vnode, vm_cache_ref **_cache, bool allocate)
{ {
struct vnode *vnode = (struct vnode *)_vnode; struct vnode *vnode = (struct vnode *)_vnode;
if (vnode->cache != NULL) { if (vnode->cache != NULL) {
vm_cache_acquire_ref(vnode->cache);
*_cache = vnode->cache; *_cache = vnode->cache;
return B_OK; return B_OK;
} }
@@ -2825,7 +2832,8 @@ vfs_get_vnode_cache(void *_vnode, vm_cache_ref **_cache, bool allocate)
status = vm_create_vnode_cache(vnode, &vnode->cache); status = vm_create_vnode_cache(vnode, &vnode->cache);
else else
status = B_BAD_VALUE; status = B_BAD_VALUE;
} } else
vm_cache_acquire_ref(vnode->cache);
if (status == B_OK) if (status == B_OK)
*_cache = vnode->cache; *_cache = vnode->cache;
+53 -64
View File
@@ -499,7 +499,6 @@ insert_area(vm_address_space *addressSpace, void **_address,
} }
// a ref to the cache holding this store must be held before entering here
static status_t static status_t
map_backing_store(vm_address_space *addressSpace, vm_store *store, void **_virtualAddress, map_backing_store(vm_address_space *addressSpace, vm_store *store, void **_virtualAddress,
off_t offset, addr_t size, uint32 addressSpec, int wiring, int protection, off_t offset, addr_t size, uint32 addressSpec, int wiring, int protection,
@@ -508,11 +507,8 @@ map_backing_store(vm_address_space *addressSpace, vm_store *store, void **_virtu
vm_cache *cache; vm_cache *cache;
vm_cache_ref *cache_ref; vm_cache_ref *cache_ref;
vm_area *area; vm_area *area;
vm_cache *nu_cache;
vm_cache_ref *nu_cache_ref = NULL;
vm_store *nu_store;
int err; status_t err;
TRACE(("map_backing_store: aspace %p, store %p, *vaddr %p, offset 0x%Lx, size %lu, addressSpec %ld, wiring %d, protection %d, _area %p, area_name '%s'\n", TRACE(("map_backing_store: aspace %p, store %p, *vaddr %p, offset 0x%Lx, size %lu, addressSpec %ld, wiring %d, protection %d, _area %p, area_name '%s'\n",
addressSpace, store, *_virtualAddress, offset, size, addressSpec, addressSpace, store, *_virtualAddress, offset, size, addressSpec,
@@ -528,6 +524,11 @@ map_backing_store(vm_address_space *addressSpace, vm_store *store, void **_virtu
// if this is a private map, we need to create a new cache & store object // if this is a private map, we need to create a new cache & store object
// pair to handle the private copies of pages as they are written to // pair to handle the private copies of pages as they are written to
if (mapping == REGION_PRIVATE_MAP) { if (mapping == REGION_PRIVATE_MAP) {
vm_cache *nu_cache;
vm_cache_ref *nu_cache_ref = NULL;
vm_store *nu_store;
// ToDo: panic???
// create an anonymous store object // create an anonymous store object
nu_store = vm_store_create_anonymous_noswap((protection & B_STACK_AREA) != 0, USER_STACK_GUARD_PAGES); nu_store = vm_store_create_anonymous_noswap((protection & B_STACK_AREA) != 0, USER_STACK_GUARD_PAGES);
if (nu_store == NULL) if (nu_store == NULL)
@@ -543,9 +544,6 @@ map_backing_store(vm_address_space *addressSpace, vm_store *store, void **_virtu
nu_cache->source = cache; nu_cache->source = cache;
// grab a ref to the cache object we're now linked to as a source
vm_cache_acquire_ref(cache_ref, true);
cache = nu_cache; cache = nu_cache;
cache_ref = cache->ref; cache_ref = cache->ref;
store = nu_store; store = nu_store;
@@ -554,9 +552,7 @@ map_backing_store(vm_address_space *addressSpace, vm_store *store, void **_virtu
err = vm_cache_set_minimal_commitment(cache_ref, offset + size); err = vm_cache_set_minimal_commitment(cache_ref, offset + size);
if (err != B_OK) if (err != B_OK)
goto err1a; goto err1;
vm_cache_acquire_ref(cache_ref, true);
acquire_sem_etc(addressSpace->sem, WRITE_COUNT, 0, 0); acquire_sem_etc(addressSpace->sem, WRITE_COUNT, 0, 0);
@@ -565,12 +561,12 @@ map_backing_store(vm_address_space *addressSpace, vm_store *store, void **_virtu
// okay, someone is trying to delete this address space now, so we can't // okay, someone is trying to delete this address space now, so we can't
// insert the area, so back out // insert the area, so back out
err = B_BAD_TEAM_ID; err = B_BAD_TEAM_ID;
goto err1b; goto err2;
} }
err = insert_area(addressSpace, _virtualAddress, addressSpec, size, area); err = insert_area(addressSpace, _virtualAddress, addressSpec, size, area);
if (err < B_OK) if (err < B_OK)
goto err1b; goto err2;
// attach the cache to the area // attach the cache to the area
area->cache_ref = cache_ref; area->cache_ref = cache_ref;
@@ -591,18 +587,14 @@ map_backing_store(vm_address_space *addressSpace, vm_store *store, void **_virtu
*_area = area; *_area = area;
return B_OK; return B_OK;
err1b: err2:
release_sem_etc(addressSpace->sem, WRITE_COUNT, 0); release_sem_etc(addressSpace->sem, WRITE_COUNT, 0);
vm_cache_release_ref(cache_ref); err1:
goto err; if (mapping == REGION_PRIVATE_MAP) {
err1a: // we created this cache, so we must delete it again
if (nu_cache_ref) {
// had never acquired it's initial ref, so acquire and then release it
// this should clean up all the objects it references
vm_cache_acquire_ref(cache_ref, true);
vm_cache_release_ref(cache_ref); vm_cache_release_ref(cache_ref);
} }
err:
free(area->name); free(area->name);
free(area); free(area);
return err; return err;
@@ -724,7 +716,7 @@ vm_create_anonymous_area(team_id aid, const char *name, void **address,
vm_page *page = NULL; vm_page *page = NULL;
bool isStack = (protection & B_STACK_AREA) != 0; bool isStack = (protection & B_STACK_AREA) != 0;
bool canOvercommit = false; bool canOvercommit = false;
status_t err; status_t status;
TRACE(("create_anonymous_area %s: size 0x%lx\n", name, size)); TRACE(("create_anonymous_area %s: size 0x%lx\n", name, size));
@@ -783,6 +775,7 @@ vm_create_anonymous_area(team_id aid, const char *name, void **address,
} }
} }
// ToDo: panic???
// create an anonymous store object // create an anonymous store object
store = vm_store_create_anonymous_noswap(canOvercommit, isStack ? store = vm_store_create_anonymous_noswap(canOvercommit, isStack ?
((protection & B_USER_PROTECTION) != 0 ? ((protection & B_USER_PROTECTION) != 0 ?
@@ -810,11 +803,10 @@ vm_create_anonymous_area(team_id aid, const char *name, void **address,
break; break;
} }
vm_cache_acquire_ref(cache_ref, true); status = map_backing_store(addressSpace, store, address, 0, size, addressSpec, wiring,
err = map_backing_store(addressSpace, store, address, 0, size, addressSpec, wiring,
protection, REGION_NO_PRIVATE_MAP, &area, name); protection, REGION_NO_PRIVATE_MAP, &area, name);
vm_cache_release_ref(cache_ref); if (status < B_OK) {
if (err < 0) { vm_cache_release_ref(cache_ref);
vm_put_address_space(addressSpace); vm_put_address_space(addressSpace);
if (wiring == B_CONTIGUOUS) { if (wiring == B_CONTIGUOUS) {
@@ -829,7 +821,7 @@ vm_create_anonymous_area(team_id aid, const char *name, void **address,
vm_page_set_state(page, PAGE_STATE_FREE); vm_page_set_state(page, PAGE_STATE_FREE);
} }
} }
return err; return status;
} }
cache_ref = store->cache->ref; cache_ref = store->cache->ref;
@@ -917,8 +909,8 @@ vm_create_anonymous_area(team_id aid, const char *name, void **address,
panic("couldn't lookup physical page just allocated\n"); panic("couldn't lookup physical page just allocated\n");
atomic_add(&page->ref_count, 1); atomic_add(&page->ref_count, 1);
err = (*map->ops->map)(map, virtualAddress, physicalAddress, protection); status = (*map->ops->map)(map, virtualAddress, physicalAddress, protection);
if (err < 0) if (status < 0)
panic("couldn't map physical page in page run\n"); panic("couldn't map physical page in page run\n");
vm_page_set_state(page, PAGE_STATE_WIRED); vm_page_set_state(page, PAGE_STATE_WIRED);
@@ -937,9 +929,6 @@ vm_create_anonymous_area(team_id aid, const char *name, void **address,
TRACE(("vm_create_anonymous_area: done\n")); TRACE(("vm_create_anonymous_area: done\n"));
if (area == NULL)
return B_NO_MEMORY;
return area->id; return area->id;
} }
@@ -991,10 +980,10 @@ vm_map_physical_memory(team_id areaID, const char *name, void **_address,
// tell the page scanner to skip over this area, it's pages are special // tell the page scanner to skip over this area, it's pages are special
cache->scan_skip = 1; cache->scan_skip = 1;
vm_cache_acquire_ref(cacheRef, true);
status = map_backing_store(addressSpace, store, _address, 0, size, status = map_backing_store(addressSpace, store, _address, 0, size,
addressSpec & ~B_MTR_MASK, 0, protection, REGION_NO_PRIVATE_MAP, &area, name); addressSpec & ~B_MTR_MASK, 0, protection, REGION_NO_PRIVATE_MAP, &area, name);
vm_cache_release_ref(cacheRef); if (status < B_OK)
vm_cache_release_ref(cacheRef);
if (status >= B_OK && (addressSpec & B_MTR_MASK) != 0) { if (status >= B_OK && (addressSpec & B_MTR_MASK) != 0) {
// set requested memory type // set requested memory type
@@ -1041,6 +1030,7 @@ vm_create_null_area(team_id aid, const char *name, void **address, uint32 addres
size = PAGE_ALIGN(size); size = PAGE_ALIGN(size);
// create an null store object // create an null store object
// TODO: panic???
store = vm_store_create_null(); store = vm_store_create_null();
if (store == NULL) if (store == NULL)
panic("vm_map_physical_memory: vm_store_create_null returned NULL"); panic("vm_map_physical_memory: vm_store_create_null returned NULL");
@@ -1053,12 +1043,13 @@ vm_create_null_area(team_id aid, const char *name, void **address, uint32 addres
// tell the page scanner to skip over this area, no pages will be mapped here // tell the page scanner to skip over this area, no pages will be mapped here
cache->scan_skip = 1; cache->scan_skip = 1;
vm_cache_acquire_ref(cache_ref, true);
err = map_backing_store(addressSpace, store, address, 0, size, addressSpec, 0, B_KERNEL_READ_AREA, REGION_NO_PRIVATE_MAP, &area, name); err = map_backing_store(addressSpace, store, address, 0, size, addressSpec, 0, B_KERNEL_READ_AREA, REGION_NO_PRIVATE_MAP, &area, name);
vm_cache_release_ref(cache_ref);
vm_put_address_space(addressSpace); vm_put_address_space(addressSpace);
if (err < 0)
if (err < B_OK) {
vm_cache_release_ref(cache_ref);
return err; return err;
}
return area->id; return area->id;
} }
@@ -1090,10 +1081,6 @@ vm_create_vnode_cache(void *vnode, struct vm_cache_ref **_cacheRef)
return B_NO_MEMORY; return B_NO_MEMORY;
} }
// acquire the cache ref once to represent the ref that the vnode will have
// this is one of the only places where we dont want to ref to ripple down to the store
vm_cache_acquire_ref(cacheRef, false);
*_cacheRef = cacheRef; *_cacheRef = cacheRef;
return B_OK; return B_OK;
} }
@@ -1124,7 +1111,8 @@ _vm_map_file(team_id aid, const char *name, void **_address, uint32 addressSpec,
if (addressSpace == NULL) if (addressSpace == NULL)
return B_BAD_TEAM_ID; return B_BAD_TEAM_ID;
TRACE(("_vm_map_file(\"%s\", offset = %Ld, size = %lu, mapping %ld)\n", path, offset, size, mapping)); TRACE(("_vm_map_file(\"%s\", offset = %Ld, size = %lu, mapping %ld)\n",
path, offset, size, mapping));
offset = ROUNDOWN(offset, B_PAGE_SIZE); offset = ROUNDOWN(offset, B_PAGE_SIZE);
size = PAGE_ALIGN(size); size = PAGE_ALIGN(size);
@@ -1134,26 +1122,26 @@ _vm_map_file(team_id aid, const char *name, void **_address, uint32 addressSpec,
if (status < B_OK) if (status < B_OK)
goto err1; goto err1;
// ToDo: this only works for file systems that use the file cache
status = vfs_get_vnode_cache(vnode, &cacheRef, false); status = vfs_get_vnode_cache(vnode, &cacheRef, false);
vfs_put_vnode(vnode);
// we don't need this vnode anymore - if the above call was
// successful, the store already has a ref to it
if (status < B_OK)
goto err1;
status = map_backing_store(addressSpace, cacheRef->cache->store, _address,
offset, size, addressSpec, 0, protection, mapping, &area, name);
if (status < B_OK) if (status < B_OK)
goto err2; goto err2;
// acquire a ref to the cache before we do work on it. Dont ripple the ref acquision to the vnode
// below because we'll have to release it later anyway, since we grabbed a ref to the vnode at
// vfs_get_vnode_from_path(). This puts the ref counts in sync.
vm_cache_acquire_ref(cacheRef, false);
status = map_backing_store(addressSpace, cacheRef->cache->store, _address, offset, size,
addressSpec, 0, protection, mapping, &area, name);
vm_cache_release_ref(cacheRef);
vm_put_address_space(addressSpace); vm_put_address_space(addressSpace);
if (status < B_OK)
return status;
return area->id; return area->id;
err2: err2:
vfs_put_vnode(vnode); vm_cache_release_ref(cacheRef);
err1: err1:
vm_put_address_space(addressSpace); vm_put_address_space(addressSpace);
return status; return status;
@@ -1167,7 +1155,8 @@ vm_map_file(team_id aid, const char *name, void **address, uint32 addressSpec,
if (!arch_vm_supports_protection(protection)) if (!arch_vm_supports_protection(protection))
return B_NOT_SUPPORTED; return B_NOT_SUPPORTED;
return _vm_map_file(aid, name, address, addressSpec, size, protection, mapping, path, offset, true); return _vm_map_file(aid, name, address, addressSpec, size, protection,
mapping, path, offset, true);
} }
@@ -1234,11 +1223,9 @@ vm_clone_area(team_id team, const char *name, void **address, uint32 addressSpec
} else } else
#endif #endif
{ {
vm_cache_acquire_ref(sourceArea->cache_ref, true); status = map_backing_store(addressSpace, sourceArea->cache_ref->cache->store,
status = map_backing_store(addressSpace, sourceArea->cache_ref->cache->store, address, address, sourceArea->cache_offset, sourceArea->size, addressSpec,
sourceArea->cache_offset, sourceArea->size, addressSpec, sourceArea->wiring, sourceArea->wiring, protection, mapping, &newArea, name);
protection, mapping, &newArea, name);
vm_cache_release_ref(sourceArea->cache_ref);
} }
vm_put_area(sourceArea); vm_put_area(sourceArea);
@@ -1439,7 +1426,7 @@ vm_copy_on_write_area(vm_area *area)
upperCacheRef->ref_count = 1; upperCacheRef->ref_count = 1;
// grab a ref to the cache object we're now linked to as a source // grab a ref to the cache object we're now linked to as a source
vm_cache_acquire_ref(lowerCacheRef, true); vm_cache_acquire_ref(lowerCacheRef);
// We now need to remap all pages from the area read-only, so that // We now need to remap all pages from the area read-only, so that
// a copy will be created on next write access // a copy will be created on next write access
@@ -1507,6 +1494,8 @@ vm_copy_area(team_id addressSpaceID, const char *name, void **_address, uint32 a
if (status < B_OK) if (status < B_OK)
goto err; goto err;
vm_cache_acquire_ref(cacheRef);
// If the source area is writable, we need to move it one layer up as well // If the source area is writable, we need to move it one layer up as well
if ((source->protection & (B_KERNEL_WRITE_AREA | B_WRITE_AREA)) != 0) { if ((source->protection & (B_KERNEL_WRITE_AREA | B_WRITE_AREA)) != 0) {
@@ -1929,7 +1918,7 @@ vm_delete_areas(struct vm_address_space *addressSpace)
vm_area *area; vm_area *area;
vm_area *next, *last = NULL; vm_area *next, *last = NULL;
TRACE(("vm_delete_areas: called on aspace 0x%lx\n", addressSpace->id)); TRACE(("vm_delete_areas: called on address space 0x%lx\n", addressSpace->id));
acquire_sem_etc(addressSpace->sem, WRITE_COUNT, 0, 0); acquire_sem_etc(addressSpace->sem, WRITE_COUNT, 0, 0);
@@ -2485,7 +2474,7 @@ vm_soft_fault(addr_t originalAddress, bool isWrite, bool isUser)
top_cache_ref = area->cache_ref; top_cache_ref = area->cache_ref;
cacheOffset = address - area->base + area->cache_offset; cacheOffset = address - area->base + area->cache_offset;
vm_cache_acquire_ref(top_cache_ref, true); vm_cache_acquire_ref(top_cache_ref);
change_count = addressSpace->change_count; change_count = addressSpace->change_count;
release_sem_etc(addressSpace->sem, READ_COUNT, 0); release_sem_etc(addressSpace->sem, READ_COUNT, 0);
+11 -6
View File
@@ -122,7 +122,7 @@ vm_cache_ref_create(vm_cache *cache)
ref->cache = cache; ref->cache = cache;
mutex_init(&ref->lock, "cache_ref_mutex"); mutex_init(&ref->lock, "cache_ref_mutex");
ref->areas = NULL; ref->areas = NULL;
ref->ref_count = 0; ref->ref_count = 1;
cache->ref = ref; cache->ref = ref;
return ref; return ref;
@@ -130,14 +130,15 @@ vm_cache_ref_create(vm_cache *cache)
void void
vm_cache_acquire_ref(vm_cache_ref *cache_ref, bool acquire_store_ref) vm_cache_acquire_ref(vm_cache_ref *cache_ref)
{ {
// dprintf("vm_cache_acquire_ref: cache_ref 0x%x, ref will be %d\n", cache_ref, cache_ref->ref_count+1); TRACE(("vm_cache_acquire_ref: cache_ref %p, ref will be %ld\n",
cache_ref, cache_ref->ref_count + 1));
if (cache_ref == NULL) if (cache_ref == NULL)
panic("vm_cache_acquire_ref: passed NULL\n"); panic("vm_cache_acquire_ref: passed NULL\n");
if (acquire_store_ref && cache_ref->cache->store->ops->acquire_ref) if (cache_ref->cache->store->ops->acquire_ref != NULL)
cache_ref->cache->store->ops->acquire_ref(cache_ref->cache->store); cache_ref->cache->store->ops->acquire_ref(cache_ref->cache->store);
atomic_add(&cache_ref->ref_count, 1); atomic_add(&cache_ref->ref_count, 1);
@@ -149,12 +150,15 @@ vm_cache_release_ref(vm_cache_ref *cache_ref)
{ {
vm_page *page; vm_page *page;
TRACE(("vm_cache_release_ref: cache_ref %p, ref will be %ld\n", cache_ref, cache_ref->ref_count - 1)); TRACE(("vm_cache_release_ref: cache_ref %p, ref will be %ld\n",
cache_ref, cache_ref->ref_count - 1));
if (cache_ref == NULL) if (cache_ref == NULL)
panic("vm_cache_release_ref: passed NULL\n"); panic("vm_cache_release_ref: passed NULL\n");
if (atomic_add(&cache_ref->ref_count, -1) != 1) { if (atomic_add(&cache_ref->ref_count, -1) != 1) {
// the store ref is only released on the "working" refs, not
// on the initial one (this is vnode specific)
if (cache_ref->cache->store->ops->release_ref) if (cache_ref->cache->store->ops->release_ref)
cache_ref->cache->store->ops->release_ref(cache_ref->cache->store); cache_ref->cache->store->ops->release_ref(cache_ref->cache->store);
@@ -184,7 +188,8 @@ vm_cache_release_ref(vm_cache_ref *cache_ref)
release_spinlock(&page_cache_table_lock); release_spinlock(&page_cache_table_lock);
restore_interrupts(state); restore_interrupts(state);
TRACE(("vm_cache_release_ref: freeing page 0x%lx\n", oldPage->ppn)); TRACE(("vm_cache_release_ref: freeing page 0x%lx\n",
oldPage->physical_page_number));
vm_page_set_state(oldPage, PAGE_STATE_FREE); vm_page_set_state(oldPage, PAGE_STATE_FREE);
} }