PhysicalZone.cpp 6.5 KB

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