PhysicalZone.h 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. /*
  2. * Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #pragma once
  7. #include <AK/Bitmap.h>
  8. #include <AK/IntrusiveList.h>
  9. namespace Kernel::Memory {
  10. // A PhysicalZone is an allocator that manages a sub-area of a PhysicalRegion.
  11. // Its total size is always a power of two.
  12. // You allocate chunks at a time. One chunk is PAGE_SIZE/2, and the minimum allocation size is 2 chunks.
  13. // The allocator uses a buddy block scheme internally.
  14. class PhysicalZone {
  15. AK_MAKE_NONCOPYABLE(PhysicalZone);
  16. AK_MAKE_NONMOVABLE(PhysicalZone);
  17. public:
  18. static constexpr size_t ZONE_CHUNK_SIZE = PAGE_SIZE / 2;
  19. using ChunkIndex = i16;
  20. PhysicalZone(PhysicalAddress base, size_t page_count);
  21. Optional<PhysicalAddress> allocate_block(size_t order);
  22. void deallocate_block(PhysicalAddress, size_t order);
  23. void dump() const;
  24. size_t available() const { return m_page_count - (m_used_chunks / 2); }
  25. bool is_empty() const { return available() == 0; }
  26. PhysicalAddress base() const { return m_base_address; }
  27. bool contains(PhysicalAddress paddr) const
  28. {
  29. return paddr >= m_base_address && paddr < m_base_address.offset(m_page_count * PAGE_SIZE);
  30. }
  31. private:
  32. Optional<ChunkIndex> allocate_block_impl(size_t order);
  33. void deallocate_block_impl(ChunkIndex, size_t order);
  34. struct BuddyBucket {
  35. bool get_buddy_bit(ChunkIndex index) const
  36. {
  37. return bitmap.get(buddy_bit_index(index));
  38. }
  39. void set_buddy_bit(ChunkIndex index, bool value)
  40. {
  41. bitmap.set(buddy_bit_index(index), value);
  42. }
  43. size_t buddy_bit_index(ChunkIndex index) const
  44. {
  45. // NOTE: We cut the index in half since one chunk is half a page.
  46. return (index >> 1) >> (1 + order);
  47. }
  48. // This bucket's index in the m_buckets array. (Redundant data kept here for convenience.)
  49. size_t order { 0 };
  50. // This is the start of the freelist for this buddy size.
  51. // It's an index into the global PhysicalPageEntry array (offset by this PhysicalRegion's base.)
  52. // A value of -1 indicates an empty freelist.
  53. ChunkIndex freelist { -1 };
  54. // Bitmap with 1 bit per buddy pair.
  55. // 0 == Both blocks either free or used.
  56. // 1 == One block free, one block used.
  57. Bitmap bitmap;
  58. };
  59. static constexpr size_t max_order = 12;
  60. BuddyBucket m_buckets[max_order + 1];
  61. PhysicalPageEntry& get_freelist_entry(ChunkIndex) const;
  62. void remove_from_freelist(BuddyBucket&, ChunkIndex);
  63. PhysicalAddress m_base_address { 0 };
  64. size_t m_page_count { 0 };
  65. size_t m_used_chunks { 0 };
  66. IntrusiveListNode<PhysicalZone> m_list_node;
  67. public:
  68. using List = IntrusiveList<&PhysicalZone::m_list_node>;
  69. };
  70. }