From 95ace7f3743010630ea969e29b8249eec91d9705 Mon Sep 17 00:00:00 2001 From: Augustin Cavalier Date: Sat, 21 Mar 2026 13:48:13 -0400 Subject: [PATCH] kernel/vm: Introduce methods to change the cache of all an area's clones. This will be useful in the case of cloned device caches based around physical memory, e.g. for framebuffers. Change-Id: If2b864208e86f436e91379b73c4b866b0bd5e1d7 Reviewed-on: https://review.haiku-os.org/c/haiku/+/10558 Reviewed-by: waddlesplash Tested-by: Commit checker robot --- headers/private/kernel/vm/vm.h | 1 + src/system/kernel/vm/vm.cpp | 90 ++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) diff --git a/headers/private/kernel/vm/vm.h b/headers/private/kernel/vm/vm.h index 9da5f4296d..c54751edd5 100644 --- a/headers/private/kernel/vm/vm.h +++ b/headers/private/kernel/vm/vm.h @@ -111,6 +111,7 @@ area_id vm_copy_area(team_id team, const char *name, void **_address, area_id vm_clone_area(team_id team, const char *name, void **address, uint32 addressSpec, uint32 protection, uint32 mapping, area_id sourceArea, bool kernel); +status_t vm_change_clones_to_null_areas(area_id area); status_t vm_delete_area(team_id teamID, area_id areaID, bool kernel); status_t vm_create_vnode_cache(struct vnode *vnode, struct VMCache **_cache); status_t vm_set_area_memory_type(area_id id, phys_addr_t physicalBase, diff --git a/src/system/kernel/vm/vm.cpp b/src/system/kernel/vm/vm.cpp index 17fdc1dcfb..e3cc6f59fb 100644 --- a/src/system/kernel/vm/vm.cpp +++ b/src/system/kernel/vm/vm.cpp @@ -2612,6 +2612,96 @@ vm_clone_area(team_id team, const char* name, void** address, } +/*! Changes all clones of an area (but not the area itself) to use a different cache + (and thus no longer be clones of this area.) + + The target cache must be locked. +*/ +status_t +vm_change_cache_of_clones(area_id areaId, struct VMCache* toCache) +{ + AddressSpaceReadLocker addressSpaceLocker; + VMArea* fromArea; + addressSpaceLocker.SetFromArea(areaId, fromArea); + + VMCache* cache = vm_area_get_locked_cache(fromArea); + addressSpaceLocker.Unlock(); + + VMCacheChainLocker cacheChainLocker(cache); + cacheChainLocker.LockAllSourceCaches(); + + VMArea* area = NULL; + int32 releaseStoreRefs = 0; + while (true) { + if (area == NULL) + area = cache->areas.First(); + else + area = cache->areas.GetNext(area); + + if (area == NULL) + break; + if (area == fromArea) + continue; + ASSERT(!area->IsWired()); + + unmap_pages(area, area->Base(), area->Size()); + + rw_lock_write_lock(&sAreaCacheLock); + + // We have to do this manually, because RemoveArea() expects + // to be called unlocked, so it can call ReleaseStoreRef(). + cache->areas.Remove(area); + + area->cache = toCache; + area->cache_type = cache->type; + toCache->InsertAreaLocked(area); + toCache->AcquireRefLocked(); + + rw_lock_write_unlock(&sAreaCacheLock); + + cache->ReleaseRefLocked(); + releaseStoreRefs++; + + // We removed this area, so restart iteration. + area = NULL; + } + + // Release the store references after dropping the lock. + cacheChainLocker.Unlock(cache); + cache->Unlock(); + while (releaseStoreRefs > 0) { + cache->ReleaseStoreRef(); + releaseStoreRefs--; + } + + // Release the reference kept when unlocking the chain. + cache->ReleaseRef(); + + return B_OK; +} + + +/*! Changes all clones of an area (but not the area itself) to be null areas. +*/ +status_t +vm_change_clones_to_null_areas(area_id areaId) +{ + VMCache* cache; + status_t status = VMCacheFactory::CreateNullCache(VM_PRIORITY_SYSTEM, cache); + if (status != B_OK) + return status; + + cache->Lock(); + cache->temporary = 1; + cache->virtual_end = SSIZE_MAX; + + status = vm_change_cache_of_clones(areaId, cache); + + cache->ReleaseRefAndUnlock(); + return status; +} + + /*! Deletes the specified area of the given address space. The address space must be write-locked.