diff --git a/headers/private/kernel/vm_page.h b/headers/private/kernel/vm_page.h index bedd21c650..7b4a16019b 100644 --- a/headers/private/kernel/vm_page.h +++ b/headers/private/kernel/vm_page.h @@ -14,6 +14,7 @@ struct kernel_args; +extern int32 gMappedPagesCount; #ifdef __cplusplus extern "C" { @@ -34,6 +35,7 @@ void vm_page_requeue(struct vm_page *page, bool tail); size_t vm_page_num_pages(void); size_t vm_page_num_free_pages(void); size_t vm_page_num_available_pages(void); +void vm_page_get_stats(system_info *info); status_t vm_page_write_modified_page_range(struct VMCache *cache, uint32 firstPage, uint32 endPage); diff --git a/src/system/kernel/system_info.cpp b/src/system/kernel/system_info.cpp index b5d566b499..2470bc82f2 100644 --- a/src/system/kernel/system_info.cpp +++ b/src/system/kernel/system_info.cpp @@ -77,11 +77,8 @@ _get_system_info(system_info *info, size_t size) for (int32 i = 0; i < info->cpu_count; i++) info->cpu_infos[i].active_time = cpu_get_active_time(i); + vm_page_get_stats(info); // TODO: Add page_faults - info->max_pages = vm_page_num_pages(); - info->used_pages = info->max_pages - vm_page_num_available_pages(); - info->cached_pages = info->max_pages - vm_page_num_free_pages() - - info->used_pages; info->used_threads = thread_used_threads(); info->max_threads = thread_max_threads(); diff --git a/src/system/kernel/vm/vm.cpp b/src/system/kernel/vm/vm.cpp index d71fdf8fd8..5a5db48c1a 100644 --- a/src/system/kernel/vm/vm.cpp +++ b/src/system/kernel/vm/vm.cpp @@ -1329,6 +1329,32 @@ cut_area(vm_address_space* addressSpace, vm_area* area, addr_t address, } +static inline void +increment_page_wired_count(vm_page* page) +{ + // TODO: needs to be atomic on all platforms! + // ... but at least the check isn't. Consequently we should hold + // sMappingLock, which would allows us to even avoid atomic_add() on + // gMappedPagesCount. + if (page->wired_count++ == 0) { + if (page->mappings.IsEmpty()) + atomic_add(&gMappedPagesCount, 1); + } +} + + +static inline void +decrement_page_wired_count(vm_page* page) +{ + if (--page->wired_count == 0) { + // TODO: needs to be atomic on all platforms! + // See above! + if (page->mappings.IsEmpty()) + atomic_add(&gMappedPagesCount, -1); + } +} + + /*! Deletes all areas in the given address range. The address space must be write-locked. */ @@ -1802,8 +1828,7 @@ vm_create_anonymous_area(team_id team, const char *name, void **address, physicalAddress); } - page->wired_count++; - // TODO: needs to be atomic on all platforms! + increment_page_wired_count(page); vm_page_set_state(page, PAGE_STATE_WIRED); cache->InsertPage(page, offset); } @@ -1835,8 +1860,7 @@ vm_create_anonymous_area(team_id team, const char *name, void **address, if (status < B_OK) panic("couldn't map physical page in page run\n"); - page->wired_count++; - // TODO: needs to be atomic on all platforms! + increment_page_wired_count(page); vm_page_set_state(page, PAGE_STATE_WIRED); cache->InsertPage(page, offset); } @@ -2740,6 +2764,9 @@ vm_remove_all_page_mappings(vm_page *page, uint32 *_flags) accumulatedFlags |= flags; } + if (page->wired_count == 0) + atomic_add(&gMappedPagesCount, -1); + locker.Unlock(); // free now unused mappings @@ -2778,8 +2805,7 @@ vm_unmap_pages(vm_area *area, addr_t base, size_t size, bool preserveModified) physicalAddress); } - page->wired_count--; - // TODO: needs to be atomic on all platforms! + decrement_page_wired_count(page); } } @@ -2828,9 +2854,12 @@ vm_unmap_pages(vm_area *area, addr_t base, size_t size, bool preserveModified) || page->cache_offset >= endOffset) continue; - mapping->page->mappings.Remove(mapping); + page->mappings.Remove(mapping); iterator.Remove(); + if (page->mappings.IsEmpty() && page->wired_count == 0) + atomic_add(&gMappedPagesCount, -1); + queue.Add(mapping); } @@ -2868,12 +2897,14 @@ vm_map_page(vm_area *area, vm_page *page, addr_t address, uint32 protection) map->ops->unlock(map); if (area->wiring != B_NO_LOCK) { - page->wired_count++; - // TODO: needs to be atomic on all platforms! + increment_page_wired_count(page); } else { // insert mapping into lists MutexLocker locker(sMappingLock); + if (page->mappings.IsEmpty() && page->wired_count == 0) + atomic_add(&gMappedPagesCount, 1); + page->mappings.Add(mapping); area->mappings.Add(mapping); } @@ -5063,8 +5094,7 @@ lock_memory_etc(team_id team, void *address, size_t numBytes, uint32 flags) if (page == NULL) panic("couldn't lookup physical page just allocated\n"); - page->wired_count++; - // TODO: needs to be atomic on all platforms! + increment_page_wired_count(page); continue; } } @@ -5098,7 +5128,7 @@ lock_memory_etc(team_id team, void *address, size_t numBytes, uint32 flags) if (page == NULL) panic("couldn't lookup physical page"); - page->wired_count++; + increment_page_wired_count(page); // TODO: needs to be atomic on all platforms! } @@ -5163,8 +5193,7 @@ unlock_memory_etc(team_id team, void *address, size_t numBytes, uint32 flags) if (page == NULL) panic("couldn't lookup physical page"); - page->wired_count--; - // TODO: needs to be atomic on all platforms! + decrement_page_wired_count(page); } out: diff --git a/src/system/kernel/vm/vm_page.cpp b/src/system/kernel/vm/vm_page.cpp index b456003327..06a320533a 100644 --- a/src/system/kernel/vm/vm_page.cpp +++ b/src/system/kernel/vm/vm_page.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -47,6 +48,8 @@ typedef struct page_queue { uint32 count; } page_queue; +int32 gMappedPagesCount; + static page_queue sFreePageQueue; static page_queue sClearPageQueue; static page_queue sModifiedPageQueue; @@ -1916,3 +1919,28 @@ vm_page_num_free_pages(void) return count - reservedPages; } + +void +vm_page_get_stats(system_info *info) +{ + // Get free pages count -- not really exact, since we don't know how many + // of the reserved pages have already been allocated, but good citizens + // unreserve chunk-wise as they are allocating the pages, if they have + // reserved a larger quantity. + page_num_t reserved = sReservedPages; + page_num_t free = free_page_queue_count(); + free = free > reserved ? free - reserved : 0; + + // The pages used for the block cache buffers. Those should not be counted + // as used but as cached pages. + // TODO: We should subtract the blocks that are in use ATM, since those + // can't really be freed in a low memory situation. + page_num_t blockCachePages = block_cache_used_memory() / B_PAGE_SIZE; + + info->max_pages = sNumPages; + info->used_pages = gMappedPagesCount - blockCachePages; + info->cached_pages = sNumPages >= free + info->used_pages + ? sNumPages - free - info->used_pages : 0; + + // TODO: We don't consider pages used for page directories/tables yet. +}