Serenity Operating System
at master 199 lines 6.7 kB view raw
1/* 2 * Copyright (c) 2021, Andreas Kling <kling@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <AK/BuiltinWrappers.h> 8#include <AK/Format.h> 9#include <Kernel/Memory/MemoryManager.h> 10#include <Kernel/Memory/PhysicalPage.h> 11#include <Kernel/Memory/PhysicalZone.h> 12 13namespace Kernel::Memory { 14 15PhysicalPageEntry& PhysicalZone::get_freelist_entry(ChunkIndex index) const 16{ 17 return MM.get_physical_page_entry(m_base_address.offset(index * ZONE_CHUNK_SIZE)); 18} 19 20PhysicalZone::PhysicalZone(PhysicalAddress base_address, size_t page_count) 21 : m_base_address(base_address) 22 , m_page_count(page_count) 23 , m_used_chunks(0) 24{ 25 size_t const chunk_count = page_count * 2; 26 for (int order = max_order; order >= 0; --order) { 27 auto& bucket = m_buckets[order]; 28 size_t block_size = 2u << order; 29 size_t bitmap_size_for_order = ceil_div((size_t)(chunk_count / block_size), (size_t)2); 30 bucket.order = order; 31 if (bitmap_size_for_order) 32 bucket.bitmap.grow(bitmap_size_for_order, false); 33 } 34 35 auto first_order = count_trailing_zeroes(page_count); 36 size_t block_size = 2u << first_order; 37 auto& bucket = m_buckets[first_order]; 38 size_t remaining_chunk_count = chunk_count; 39 size_t initial_bundle_count = remaining_chunk_count / block_size; 40 41 size_t offset = 0; 42 for (size_t i = 0; i < initial_bundle_count; ++i) { 43 ChunkIndex index = offset + i; 44 bucket.set_buddy_bit(index, true); 45 46 auto& freelist_entry = get_freelist_entry(index).freelist; 47 freelist_entry.next_index = bucket.freelist; 48 freelist_entry.prev_index = -1; 49 bucket.freelist = index; 50 51 remaining_chunk_count -= block_size; 52 offset += block_size; 53 } 54} 55 56Optional<PhysicalAddress> PhysicalZone::allocate_block(size_t order) 57{ 58 size_t block_size = 2u << order; 59 auto result = allocate_block_impl(order); 60 if (!result.has_value()) 61 return {}; 62 m_used_chunks += block_size; 63 VERIFY(!(result.value() & 1)); 64 return m_base_address.offset(result.value() * ZONE_CHUNK_SIZE); 65} 66 67Optional<PhysicalZone::ChunkIndex> PhysicalZone::allocate_block_impl(size_t order) 68{ 69 if (order > max_order) 70 return {}; 71 size_t block_size = 2u << order; 72 auto& bucket = m_buckets[order]; 73 if (bucket.freelist == -1) { 74 // The freelist for this order is empty, try to allocate a block from one order higher, and split it. 75 auto buddies = allocate_block_impl(order + 1); 76 77 if (!buddies.has_value()) { 78 // Looks like we're unable to satisfy this allocation request. 79 return {}; 80 } 81 82 // Split the block from order+1 into two parts. 83 // We keep one (in the freelist for this order) and return the other. 84 85 ChunkIndex index = buddies.value(); 86 87 // First half goes in the freelist 88 auto& freelist_entry = get_freelist_entry(index).freelist; 89 freelist_entry.next_index = -1; 90 freelist_entry.prev_index = -1; 91 bucket.freelist = index; 92 93 VERIFY(bucket.get_buddy_bit(index) == false); 94 95 // Set buddy bit to 1 (one used, one unused). 96 bucket.set_buddy_bit(index, true); 97 98 // Second half is returned. 99 return index + block_size; 100 } 101 102 // Freelist has at least one entry, return that. 103 ChunkIndex index = bucket.freelist; 104 105 bucket.freelist = get_freelist_entry(bucket.freelist).freelist.next_index; 106 if (bucket.freelist != -1) { 107 get_freelist_entry(bucket.freelist).freelist.prev_index = -1; 108 } 109 110 VERIFY(bucket.get_buddy_bit(index) == true); 111 bucket.set_buddy_bit(index, false); 112 113 return index; 114} 115 116void PhysicalZone::deallocate_block(PhysicalAddress address, size_t order) 117{ 118 size_t block_size = 2u << order; 119 ChunkIndex index = (address.get() - m_base_address.get()) / ZONE_CHUNK_SIZE; 120 deallocate_block_impl(index, order); 121 m_used_chunks -= block_size; 122} 123 124void PhysicalZone::deallocate_block_impl(ChunkIndex index, size_t order) 125{ 126 size_t block_size = 2u << order; 127 128 // Basic algorithm: 129 // If the buddy block is free (buddy bit is 1 -- because this block was the only used one): 130 // Then, 131 // 1. Merge with buddy. 132 // 2. Return the merged block to order+1. 133 // Else (buddy bit is 0 -- because both blocks are used) 134 // 1. Add the block to the freelist. 135 // 2. Set buddy bit to 1. 136 auto& bucket = m_buckets[order]; 137 138 if (bucket.get_buddy_bit(index)) { 139 // Buddy is free! Merge with buddy and coalesce upwards to the next order. 140 auto buddy_bit_index = bucket.buddy_bit_index(index); 141 ChunkIndex buddy_base_index = (buddy_bit_index << 1) << (1 + order); 142 143 if (index == buddy_base_index) 144 remove_from_freelist(bucket, buddy_base_index + block_size); 145 else 146 remove_from_freelist(bucket, buddy_base_index); 147 148 bucket.set_buddy_bit(index, false); 149 deallocate_block_impl(buddy_base_index, order + 1); 150 } else { 151 // Buddy is in use. Add freed block to freelist and set buddy bit to 1. 152 153 if (bucket.freelist != -1) { 154 get_freelist_entry(bucket.freelist).freelist.prev_index = index; 155 } 156 157 auto& freelist_entry = get_freelist_entry(index).freelist; 158 freelist_entry.next_index = bucket.freelist; 159 freelist_entry.prev_index = -1; 160 bucket.freelist = index; 161 162 bucket.set_buddy_bit(index, true); 163 } 164} 165 166void PhysicalZone::remove_from_freelist(BuddyBucket& bucket, ChunkIndex index) 167{ 168 auto& freelist_entry = get_freelist_entry(index).freelist; 169 VERIFY(freelist_entry.prev_index >= -1); 170 VERIFY(freelist_entry.next_index >= -1); 171 if (freelist_entry.prev_index != -1) { 172 auto& prev_entry = get_freelist_entry(freelist_entry.prev_index).freelist; 173 prev_entry.next_index = freelist_entry.next_index; 174 } 175 if (freelist_entry.next_index != -1) { 176 auto& next_entry = get_freelist_entry(freelist_entry.next_index).freelist; 177 next_entry.prev_index = freelist_entry.prev_index; 178 } 179 if (bucket.freelist == index) 180 bucket.freelist = freelist_entry.next_index; 181 freelist_entry.next_index = -1; 182 freelist_entry.prev_index = -1; 183} 184 185void PhysicalZone::dump() const 186{ 187 dbgln("(( {} used, {} available, page_count: {} ))", m_used_chunks, available(), m_page_count); 188 for (size_t i = 0; i <= max_order; ++i) { 189 auto const& bucket = m_buckets[i]; 190 dbgln("[{:2} / {:4}] ", i, (size_t)(2u << i)); 191 auto entry = bucket.freelist; 192 while (entry != -1) { 193 dbgln(" {}", entry); 194 entry = get_freelist_entry(entry).freelist.next_index; 195 } 196 } 197} 198 199}