| // Copyright 2018 The Fuchsia Authors |
| // |
| // Use of this source code is governed by a MIT-style |
| // license that can be found in the LICENSE file or at |
| // https://opensource.org/licenses/MIT |
| #pragma once |
| |
| #include <fbl/canary.h> |
| #include <fbl/intrusive_double_list.h> |
| #include <fbl/mutex.h> |
| |
| #include <kernel/lockdep.h> |
| #include <vm/pmm.h> |
| |
| #include "pmm_arena.h" |
| |
| #define PMM_ENABLE_FREE_FILL 0 |
| #define PMM_FREE_FILL_BYTE 0x42 |
| |
| // per numa node collection of pmm arenas and worker threads |
| class PmmNode { |
| public: |
| PmmNode(); |
| ~PmmNode(); |
| |
| DISALLOW_COPY_ASSIGN_AND_MOVE(PmmNode); |
| |
| paddr_t PageToPaddr(const vm_page_t* page) TA_NO_THREAD_SAFETY_ANALYSIS; |
| vm_page_t* PaddrToPage(paddr_t addr) TA_NO_THREAD_SAFETY_ANALYSIS; |
| |
| // main allocator routines |
| zx_status_t AllocPage(uint alloc_flags, vm_page_t** page, paddr_t* pa); |
| zx_status_t AllocPages(size_t count, uint alloc_flags, list_node* list); |
| zx_status_t AllocRange(paddr_t address, size_t count, list_node* list); |
| zx_status_t AllocContiguous(size_t count, uint alloc_flags, uint8_t alignment_log2, paddr_t* pa, list_node* list); |
| void FreePage(vm_page* page); |
| void FreeList(list_node* list); |
| |
| uint64_t CountFreePages() const; |
| uint64_t CountTotalBytes() const; |
| |
| // printf free and overall state of the internal arenas |
| // NOTE: both functions skip mutexes and can be called inside timer or crash context |
| // though the data they return may be questionable |
| void DumpFree() const TA_NO_THREAD_SAFETY_ANALYSIS; |
| void Dump(bool is_panic) const TA_NO_THREAD_SAFETY_ANALYSIS; |
| |
| #if PMM_ENABLE_FREE_FILL |
| void EnforceFill() TA_NO_THREAD_SAFETY_ANALYSIS; |
| #endif |
| |
| zx_status_t AddArena(const pmm_arena_info_t* info); |
| |
| // add new pages to the free queue. used when boostrapping a PmmArena |
| void AddFreePages(list_node* list); |
| |
| private: |
| void FreePageHelperLocked(vm_page* page) TA_REQ(lock_); |
| void FreeListLocked(list_node* list) TA_REQ(lock_); |
| |
| fbl::Canary<fbl::magic("PNOD")> canary_; |
| |
| mutable DECLARE_MUTEX(PmmNode) lock_; |
| |
| uint64_t arena_cumulative_size_ TA_GUARDED(lock_) = 0; |
| uint64_t free_count_ TA_GUARDED(lock_) = 0; |
| |
| fbl::DoublyLinkedList<PmmArena*> arena_list_ TA_GUARDED(lock_); |
| |
| // page queues |
| list_node free_list_ TA_GUARDED(lock_) = LIST_INITIAL_VALUE(free_list_); |
| list_node inactive_list_ TA_GUARDED(lock_) = LIST_INITIAL_VALUE(inactive_list_); |
| list_node active_list_ TA_GUARDED(lock_) = LIST_INITIAL_VALUE(active_list_); |
| list_node modified_list_ TA_GUARDED(lock_) = LIST_INITIAL_VALUE(modified_list_); |
| list_node wired_list_ TA_GUARDED(lock_) = LIST_INITIAL_VALUE(wired_list_); |
| |
| #if PMM_ENABLE_FREE_FILL |
| void FreeFill(vm_page_t* page); |
| void CheckFreeFill(vm_page_t* page); |
| |
| bool enforce_fill_ = false; |
| #endif |
| }; |
| |
| // We don't need to hold the arena lock while executing this, since it is |
| // only accesses values that are set once during system initialization. |
| inline vm_page_t* PmmNode::PaddrToPage(paddr_t addr) TA_NO_THREAD_SAFETY_ANALYSIS { |
| for (auto& a : arena_list_) { |
| if (a.address_in_arena(addr)) { |
| size_t index = (addr - a.base()) / PAGE_SIZE; |
| return a.get_page(index); |
| } |
| } |
| return nullptr; |
| } |