1 // Copyright 2018 The Fuchsia Authors
2 //
3 // Use of this source code is governed by a MIT-style
4 // license that can be found in the LICENSE file or at
5 // https://opensource.org/licenses/MIT
6 #pragma once
7
8 #include <fbl/canary.h>
9 #include <fbl/intrusive_double_list.h>
10 #include <fbl/mutex.h>
11
12 #include <kernel/lockdep.h>
13 #include <vm/pmm.h>
14
15 #include "pmm_arena.h"
16
17 #define PMM_ENABLE_FREE_FILL 0
18 #define PMM_FREE_FILL_BYTE 0x42
19
20 // per numa node collection of pmm arenas and worker threads
21 class PmmNode {
22 public:
23 PmmNode();
24 ~PmmNode();
25
26 DISALLOW_COPY_ASSIGN_AND_MOVE(PmmNode);
27
28 paddr_t PageToPaddr(const vm_page_t* page) TA_NO_THREAD_SAFETY_ANALYSIS;
29 vm_page_t* PaddrToPage(paddr_t addr) TA_NO_THREAD_SAFETY_ANALYSIS;
30
31 // main allocator routines
32 zx_status_t AllocPage(uint alloc_flags, vm_page_t** page, paddr_t* pa);
33 zx_status_t AllocPages(size_t count, uint alloc_flags, list_node* list);
34 zx_status_t AllocRange(paddr_t address, size_t count, list_node* list);
35 zx_status_t AllocContiguous(size_t count, uint alloc_flags, uint8_t alignment_log2, paddr_t* pa, list_node* list);
36 void FreePage(vm_page* page);
37 void FreeList(list_node* list);
38
39 uint64_t CountFreePages() const;
40 uint64_t CountTotalBytes() const;
41 void CountTotalStates(uint64_t state_count[VM_PAGE_STATE_COUNT_]) const;
42
43 // printf free and overall state of the internal arenas
44 // NOTE: both functions skip mutexes and can be called inside timer or crash context
45 // though the data they return may be questionable
46 void DumpFree() const TA_NO_THREAD_SAFETY_ANALYSIS;
47 void Dump(bool is_panic) const TA_NO_THREAD_SAFETY_ANALYSIS;
48
49 #if PMM_ENABLE_FREE_FILL
50 void EnforceFill() TA_NO_THREAD_SAFETY_ANALYSIS;
51 #endif
52
53 zx_status_t AddArena(const pmm_arena_info_t* info);
54
55 // add new pages to the free queue. used when boostrapping a PmmArena
56 void AddFreePages(list_node* list);
57
58 private:
59 void FreePageLocked(vm_page* page) TA_REQ(lock_);
60 void FreeListLocked(list_node* list) TA_REQ(lock_);
61
62 fbl::Canary<fbl::magic("PNOD")> canary_;
63
64 mutable DECLARE_MUTEX(PmmNode) lock_;
65
66 uint64_t arena_cumulative_size_ TA_GUARDED(lock_) = 0;
67 uint64_t free_count_ TA_GUARDED(lock_) = 0;
68
69 fbl::DoublyLinkedList<PmmArena*> arena_list_ TA_GUARDED(lock_);
70
71 // page queues
72 list_node free_list_ TA_GUARDED(lock_) = LIST_INITIAL_VALUE(free_list_);
73 list_node inactive_list_ TA_GUARDED(lock_) = LIST_INITIAL_VALUE(inactive_list_);
74 list_node active_list_ TA_GUARDED(lock_) = LIST_INITIAL_VALUE(active_list_);
75 list_node modified_list_ TA_GUARDED(lock_) = LIST_INITIAL_VALUE(modified_list_);
76 list_node wired_list_ TA_GUARDED(lock_) = LIST_INITIAL_VALUE(wired_list_);
77
78 #if PMM_ENABLE_FREE_FILL
79 void FreeFill(vm_page_t* page);
80 void CheckFreeFill(vm_page_t* page);
81
82 bool enforce_fill_ = false;
83 #endif
84 };
85
86 // We don't need to hold the arena lock while executing this, since it is
87 // only accesses values that are set once during system initialization.
PaddrToPage(paddr_t addr)88 inline vm_page_t* PmmNode::PaddrToPage(paddr_t addr) TA_NO_THREAD_SAFETY_ANALYSIS {
89 for (auto& a : arena_list_) {
90 if (a.address_in_arena(addr)) {
91 size_t index = (addr - a.base()) / PAGE_SIZE;
92 return a.get_page(index);
93 }
94 }
95 return nullptr;
96 }
97