kernel/vm: Use more than one object_cache for the page mappings.
Every time a page is mapped into an area on fault, we have to allocate a mapping object for it. While the object_cache does have per-CPU depots, these depots only store a limited number of items, and once they run out the object_cache's lock must be acquired. So, to reduce lock contention on SMP systems, create a number of object caches corresponding to the nearest power of 2 that is equal or smaller than the count of CPUs. (We already allocate dozens of object caches for the block allocator no matter how many CPUs there are, so a few more depending on CPU count shouldn't impact memory use too much. Besides, the object_caches are wired into the low_resource system.) This significantly reduces lock contention on SMP systems. Same benchmark setup as yesterday (compile mime_db and relink HaikuDepot, VMware, -j4), before: real 0m16.981s user 0m14.357s sys 0m6.060s after: real 0m14.522s user 0m14.194s sys 0m4.337s And the page_mappings object_cache locks went from having 200,000+ waits and ~14 seconds waiting time (across all threads) down to ~900 (yes, that's not a typo) and ~0.05s wait time (though these numbers were captured in conjunction with the following commit.)
This commit is contained in:
@@ -17,7 +17,6 @@
|
||||
|
||||
struct generic_io_vec;
|
||||
struct kernel_args;
|
||||
struct ObjectCache;
|
||||
struct system_memory_info;
|
||||
struct VMAddressSpace;
|
||||
struct VMArea;
|
||||
@@ -48,9 +47,6 @@ struct VMPageWiringInfo;
|
||||
#define VM_MEMORY_RESERVE_SYSTEM (VM_PAGE_RESERVE_SYSTEM * B_PAGE_SIZE)
|
||||
|
||||
|
||||
extern struct ObjectCache* gPageMappingsObjectCache;
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@@ -86,6 +82,8 @@ area_id transfer_area(area_id id, void** _address, uint32 addressSpec,
|
||||
|
||||
const char* vm_cache_type_to_string(int32 type);
|
||||
|
||||
void vm_free_page_mapping(page_num_t page, vm_page_mapping* mapping, uint32 flags);
|
||||
|
||||
status_t vm_prepare_kernel_area_debug_protection(area_id id, void** cookie);
|
||||
status_t vm_set_kernel_area_debug_protection(void* cookie, void* _address,
|
||||
size_t size, uint32 protection);
|
||||
|
||||
@@ -517,7 +517,7 @@ ARMVMTranslationMap32Bit::UnmapPages(VMArea* area, addr_t base, size_t size,
|
||||
uint32 freeFlags = CACHE_DONT_WAIT_FOR_MEMORY
|
||||
| (isKernelSpace ? CACHE_DONT_LOCK_KERNEL_SPACE : 0);
|
||||
while (vm_page_mapping* mapping = queue.RemoveHead())
|
||||
object_cache_free(gPageMappingsObjectCache, mapping, freeFlags);
|
||||
vm_free_page_mapping(mapping->page->physical_page_number, mapping, freeFlags);
|
||||
}
|
||||
|
||||
|
||||
@@ -619,7 +619,7 @@ ARMVMTranslationMap32Bit::UnmapArea(VMArea* area, bool deletingAddressSpace,
|
||||
uint32 freeFlags = CACHE_DONT_WAIT_FOR_MEMORY
|
||||
| (isKernelSpace ? CACHE_DONT_LOCK_KERNEL_SPACE : 0);
|
||||
while (vm_page_mapping* mapping = mappings.RemoveHead())
|
||||
object_cache_free(gPageMappingsObjectCache, mapping, freeFlags);
|
||||
vm_free_page_mapping(mapping->page->physical_page_number, mapping, freeFlags);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -638,7 +638,7 @@ M68KVMTranslationMap040::UnmapPages(VMArea* area, addr_t base, size_t size,
|
||||
uint32 freeFlags = CACHE_DONT_WAIT_FOR_MEMORY
|
||||
| (isKernelSpace ? CACHE_DONT_LOCK_KERNEL_SPACE : 0);
|
||||
while (vm_page_mapping* mapping = queue.RemoveHead())
|
||||
object_cache_free(gPageMappingsObjectCache, mapping, freeFlags);
|
||||
vm_free_page_mapping(mapping->page->physical_page_number, mapping, freeFlags);
|
||||
}
|
||||
|
||||
|
||||
@@ -755,7 +755,7 @@ M68KVMTranslationMap040::UnmapArea(VMArea* area, bool deletingAddressSpace,
|
||||
uint32 freeFlags = CACHE_DONT_WAIT_FOR_MEMORY
|
||||
| (isKernelSpace ? CACHE_DONT_LOCK_KERNEL_SPACE : 0);
|
||||
while (vm_page_mapping* mapping = mappings.RemoveHead())
|
||||
object_cache_free(gPageMappingsObjectCache, mapping, freeFlags);
|
||||
vm_free_page_mapping(mapping->page->physical_page_number, mapping, freeFlags);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -863,7 +863,7 @@ PPCVMTranslationMap460::UnmapPages(VMArea* area, addr_t base, size_t size,
|
||||
uint32 freeFlags = CACHE_DONT_WAIT_FOR_MEMORY
|
||||
| (isKernelSpace ? CACHE_DONT_LOCK_KERNEL_SPACE : 0);
|
||||
while (vm_page_mapping* mapping = queue.RemoveHead())
|
||||
object_cache_free(gPageMappingsObjectCache, mapping, freeFlags);
|
||||
vm_free_page_mapping(mapping->page->physical_page_number, mapping, freeFlags);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -968,7 +968,7 @@ PPCVMTranslationMap460::UnmapArea(VMArea* area, bool deletingAddressSpace,
|
||||
uint32 freeFlags = CACHE_DONT_WAIT_FOR_MEMORY
|
||||
| (isKernelSpace ? CACHE_DONT_LOCK_KERNEL_SPACE : 0);
|
||||
while (vm_page_mapping* mapping = mappings.RemoveHead())
|
||||
object_cache_free(gPageMappingsObjectCache, mapping, freeFlags);
|
||||
vm_free_page_mapping(mapping->page->physical_page_number, mapping, freeFlags);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -863,7 +863,7 @@ PPCVMTranslationMapClassic::UnmapPages(VMArea* area, addr_t base, size_t size,
|
||||
uint32 freeFlags = CACHE_DONT_WAIT_FOR_MEMORY
|
||||
| (isKernelSpace ? CACHE_DONT_LOCK_KERNEL_SPACE : 0);
|
||||
while (vm_page_mapping* mapping = queue.RemoveHead())
|
||||
object_cache_free(gPageMappingsObjectCache, mapping, freeFlags);
|
||||
vm_free_page_mapping(mapping->page->physical_page_number, mapping, freeFlags);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -968,7 +968,7 @@ PPCVMTranslationMapClassic::UnmapArea(VMArea* area, bool deletingAddressSpace,
|
||||
uint32 freeFlags = CACHE_DONT_WAIT_FOR_MEMORY
|
||||
| (isKernelSpace ? CACHE_DONT_LOCK_KERNEL_SPACE : 0);
|
||||
while (vm_page_mapping* mapping = mappings.RemoveHead())
|
||||
object_cache_free(gPageMappingsObjectCache, mapping, freeFlags);
|
||||
vm_free_page_mapping(mapping->page->physical_page_number, mapping, freeFlags);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -455,7 +455,7 @@ RISCV64VMTranslationMap::UnmapPages(VMArea* area, addr_t base, size_t size,
|
||||
| (isKernelSpace ? CACHE_DONT_LOCK_KERNEL_SPACE : 0);
|
||||
|
||||
while (vm_page_mapping* mapping = queue.RemoveHead())
|
||||
object_cache_free(gPageMappingsObjectCache, mapping, freeFlags);
|
||||
vm_free_page_mapping(mapping->page->physical_page_number, mapping, freeFlags);
|
||||
}
|
||||
|
||||
|
||||
@@ -553,7 +553,7 @@ RISCV64VMTranslationMap::UnmapArea(VMArea* area, bool deletingAddressSpace,
|
||||
| (isKernelSpace ? CACHE_DONT_LOCK_KERNEL_SPACE : 0);
|
||||
|
||||
while (vm_page_mapping* mapping = mappings.RemoveHead())
|
||||
object_cache_free(gPageMappingsObjectCache, mapping, freeFlags);
|
||||
vm_free_page_mapping(mapping->page->physical_page_number, mapping, freeFlags);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -500,7 +500,7 @@ X86VMTranslationMap32Bit::UnmapPages(VMArea* area, addr_t base, size_t size,
|
||||
uint32 freeFlags = CACHE_DONT_WAIT_FOR_MEMORY
|
||||
| (isKernelSpace ? CACHE_DONT_LOCK_KERNEL_SPACE : 0);
|
||||
while (vm_page_mapping* mapping = queue.RemoveHead())
|
||||
object_cache_free(gPageMappingsObjectCache, mapping, freeFlags);
|
||||
vm_free_page_mapping(mapping->page->physical_page_number, mapping, freeFlags);
|
||||
}
|
||||
|
||||
|
||||
@@ -602,7 +602,7 @@ X86VMTranslationMap32Bit::UnmapArea(VMArea* area, bool deletingAddressSpace,
|
||||
uint32 freeFlags = CACHE_DONT_WAIT_FOR_MEMORY
|
||||
| (isKernelSpace ? CACHE_DONT_LOCK_KERNEL_SPACE : 0);
|
||||
while (vm_page_mapping* mapping = mappings.RemoveHead())
|
||||
object_cache_free(gPageMappingsObjectCache, mapping, freeFlags);
|
||||
vm_free_page_mapping(mapping->page->physical_page_number, mapping, freeFlags);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -502,7 +502,7 @@ X86VMTranslationMap64Bit::UnmapPages(VMArea* area, addr_t base, size_t size,
|
||||
uint32 freeFlags = CACHE_DONT_WAIT_FOR_MEMORY
|
||||
| (isKernelSpace ? CACHE_DONT_LOCK_KERNEL_SPACE : 0);
|
||||
while (vm_page_mapping* mapping = queue.RemoveHead())
|
||||
object_cache_free(gPageMappingsObjectCache, mapping, freeFlags);
|
||||
vm_free_page_mapping(mapping->page->physical_page_number, mapping, freeFlags);
|
||||
}
|
||||
|
||||
|
||||
@@ -598,7 +598,7 @@ X86VMTranslationMap64Bit::UnmapArea(VMArea* area, bool deletingAddressSpace,
|
||||
uint32 freeFlags = CACHE_DONT_WAIT_FOR_MEMORY
|
||||
| (isKernelSpace ? CACHE_DONT_LOCK_KERNEL_SPACE : 0);
|
||||
while (vm_page_mapping* mapping = mappings.RemoveHead())
|
||||
object_cache_free(gPageMappingsObjectCache, mapping, freeFlags);
|
||||
vm_free_page_mapping(mapping->page->physical_page_number, mapping, freeFlags);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -747,7 +747,7 @@ X86VMTranslationMapPAE::UnmapPages(VMArea* area, addr_t base, size_t size,
|
||||
uint32 freeFlags = CACHE_DONT_WAIT_FOR_MEMORY
|
||||
| (isKernelSpace ? CACHE_DONT_LOCK_KERNEL_SPACE : 0);
|
||||
while (vm_page_mapping* mapping = queue.RemoveHead())
|
||||
object_cache_free(gPageMappingsObjectCache, mapping, freeFlags);
|
||||
vm_free_page_mapping(mapping->page->physical_page_number, mapping, freeFlags);
|
||||
}
|
||||
|
||||
|
||||
@@ -875,7 +875,7 @@ X86VMTranslationMapPAE::UnmapArea(VMArea* area, bool deletingAddressSpace,
|
||||
uint32 freeFlags = CACHE_DONT_WAIT_FOR_MEMORY
|
||||
| (isKernelSpace ? CACHE_DONT_LOCK_KERNEL_SPACE : 0);
|
||||
while (vm_page_mapping* mapping = mappings.RemoveHead())
|
||||
object_cache_free(gPageMappingsObjectCache, mapping, freeFlags);
|
||||
vm_free_page_mapping(mapping->page->physical_page_number, mapping, freeFlags);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -206,7 +206,7 @@ VMTranslationMap::PageUnmapped(VMArea* area, page_num_t pageNumber,
|
||||
|
||||
if (mapping != NULL) {
|
||||
bool isKernelSpace = area->address_space == VMAddressSpace::Kernel();
|
||||
object_cache_free(gPageMappingsObjectCache, mapping,
|
||||
vm_free_page_mapping(pageNumber, mapping,
|
||||
CACHE_DONT_WAIT_FOR_MEMORY
|
||||
| (isKernelSpace ? CACHE_DONT_LOCK_KERNEL_SPACE : 0));
|
||||
}
|
||||
@@ -252,7 +252,7 @@ VMTranslationMap::UnaccessedPageUnmapped(VMArea* area, page_num_t pageNumber)
|
||||
atomic_add(&gMappedPagesCount, -1);
|
||||
|
||||
if (mapping != NULL) {
|
||||
object_cache_free(gPageMappingsObjectCache, mapping,
|
||||
vm_free_page_mapping(pageNumber, mapping,
|
||||
CACHE_DONT_WAIT_FOR_MEMORY | CACHE_DONT_LOCK_KERNEL_SPACE);
|
||||
// Since this is called by the page daemon, we never want to lock
|
||||
// the kernel address space.
|
||||
|
||||
+61
-12
@@ -244,7 +244,8 @@ static const size_t kMemoryReserveForPriority[] = {
|
||||
};
|
||||
|
||||
|
||||
ObjectCache* gPageMappingsObjectCache;
|
||||
static ObjectCache** sPageMappingsObjectCaches;
|
||||
static uint32 sPageMappingsMask;
|
||||
|
||||
static rw_lock sAreaCacheLock = RW_LOCK_INITIALIZER("area->cache");
|
||||
|
||||
@@ -410,6 +411,61 @@ private:
|
||||
#endif // VM_PAGE_FAULT_TRACING
|
||||
|
||||
|
||||
// #pragma mark - page mappings allocation
|
||||
|
||||
|
||||
static void
|
||||
create_page_mappings_object_caches()
|
||||
{
|
||||
// We want an even power of 2 smaller than the number of CPUs.
|
||||
const int32 numCPUs = smp_get_num_cpus();
|
||||
int32 count = next_power_of_2(numCPUs);
|
||||
if (count > numCPUs)
|
||||
count >>= 1;
|
||||
sPageMappingsMask = count - 1;
|
||||
|
||||
sPageMappingsObjectCaches = new object_cache*[count];
|
||||
if (sPageMappingsObjectCaches == NULL)
|
||||
panic("failed to allocate page mappings object_cache array");
|
||||
|
||||
for (int32 i = 0; i < count; i++) {
|
||||
char name[32];
|
||||
snprintf(name, sizeof(name), "page mappings %" B_PRId32, i);
|
||||
|
||||
object_cache* cache = create_object_cache_etc(name,
|
||||
sizeof(vm_page_mapping), 0, 0, 64, 128, CACHE_LARGE_SLAB, NULL, NULL,
|
||||
NULL, NULL);
|
||||
if (cache == NULL)
|
||||
panic("failed to create page mappings object_cache");
|
||||
|
||||
object_cache_set_minimum_reserve(cache, 1024);
|
||||
sPageMappingsObjectCaches[i] = cache;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static object_cache*
|
||||
page_mapping_object_cache_for(page_num_t page)
|
||||
{
|
||||
return sPageMappingsObjectCaches[page & sPageMappingsMask];
|
||||
}
|
||||
|
||||
|
||||
static vm_page_mapping*
|
||||
allocate_page_mapping(page_num_t page, uint32 flags = 0)
|
||||
{
|
||||
return (vm_page_mapping*)object_cache_alloc(page_mapping_object_cache_for(page),
|
||||
flags);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
vm_free_page_mapping(page_num_t page, vm_page_mapping* mapping, uint32 flags)
|
||||
{
|
||||
object_cache_free(page_mapping_object_cache_for(page), mapping, flags);
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
@@ -561,8 +617,7 @@ map_page(VMArea* area, vm_page* page, addr_t address, uint32 protection,
|
||||
DEBUG_PAGE_ACCESS_CHECK(page);
|
||||
|
||||
bool isKernelSpace = area->address_space == VMAddressSpace::Kernel();
|
||||
vm_page_mapping* mapping = (vm_page_mapping*)object_cache_alloc(
|
||||
gPageMappingsObjectCache,
|
||||
vm_page_mapping* mapping = allocate_page_mapping(page->physical_page_number,
|
||||
CACHE_DONT_WAIT_FOR_MEMORY
|
||||
| (isKernelSpace ? CACHE_DONT_LOCK_KERNEL_SPACE : 0));
|
||||
if (mapping == NULL)
|
||||
@@ -4429,14 +4484,7 @@ vm_init(kernel_args* args)
|
||||
(void *)ROUNDDOWN(0xdeadbeef, B_PAGE_SIZE), B_PAGE_SIZE * 64);
|
||||
#endif
|
||||
|
||||
// create the object cache for the page mappings
|
||||
gPageMappingsObjectCache = create_object_cache_etc("page mappings",
|
||||
sizeof(vm_page_mapping), 0, 0, 64, 128, CACHE_LARGE_SLAB, NULL, NULL,
|
||||
NULL, NULL);
|
||||
if (gPageMappingsObjectCache == NULL)
|
||||
panic("failed to create page mappings object cache");
|
||||
|
||||
object_cache_set_minimum_reserve(gPageMappingsObjectCache, 1024);
|
||||
create_page_mappings_object_caches();
|
||||
|
||||
#if DEBUG_CACHE_LIST
|
||||
if (vm_page_num_free_pages() >= 200 * 1024 * 1024 / B_PAGE_SIZE) {
|
||||
@@ -5073,7 +5121,8 @@ vm_soft_fault(VMAddressSpace* addressSpace, addr_t originalAddress,
|
||||
|
||||
context.UnlockAll();
|
||||
|
||||
if (object_cache_reserve(gPageMappingsObjectCache, 1, 0)
|
||||
if (object_cache_reserve(page_mapping_object_cache_for(
|
||||
context.page->physical_page_number), 1, 0)
|
||||
!= B_OK) {
|
||||
// Apparently the situation is serious. Let's get ourselves
|
||||
// killed.
|
||||
|
||||
Reference in New Issue
Block a user