PhysicalZone.cpp 6.6 KB

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