PhysicalZone.h 2.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  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_ETERNAL;
  16. AK_MAKE_NONCOPYABLE(PhysicalZone);
  17. AK_MAKE_NONMOVABLE(PhysicalZone);
  18. public:
  19. static constexpr size_t ZONE_CHUNK_SIZE = PAGE_SIZE / 2;
  20. using ChunkIndex = i16;
  21. PhysicalZone(PhysicalAddress base, size_t page_count);
  22. Optional<PhysicalAddress> allocate_block(size_t order);
  23. void deallocate_block(PhysicalAddress, size_t order);
  24. void dump() const;
  25. size_t available() const { return m_page_count - (m_used_chunks / 2); }
  26. bool is_empty() const { return !available(); }
  27. PhysicalAddress base() const { return m_base_address; }
  28. bool contains(PhysicalAddress paddr) const
  29. {
  30. return paddr >= m_base_address && paddr < m_base_address.offset(m_page_count * PAGE_SIZE);
  31. }
  32. private:
  33. Optional<ChunkIndex> allocate_block_impl(size_t order);
  34. void deallocate_block_impl(ChunkIndex, size_t order);
  35. struct BuddyBucket {
  36. bool get_buddy_bit(ChunkIndex index) const
  37. {
  38. return bitmap.get(buddy_bit_index(index));
  39. }
  40. void set_buddy_bit(ChunkIndex index, bool value)
  41. {
  42. bitmap.set(buddy_bit_index(index), value);
  43. }
  44. size_t buddy_bit_index(ChunkIndex index) const
  45. {
  46. // NOTE: We cut the index in half since one chunk is half a page.
  47. return (index >> 1) >> (1 + order);
  48. }
  49. // This bucket's index in the m_buckets array. (Redundant data kept here for convenience.)
  50. size_t order { 0 };
  51. // This is the start of the freelist for this buddy size.
  52. // It's an index into the global PhysicalPageEntry array (offset by this PhysicalRegion's base.)
  53. // A value of -1 indicates an empty freelist.
  54. ChunkIndex freelist { -1 };
  55. // Bitmap with 1 bit per buddy pair.
  56. // 0 == Both blocks either free or used.
  57. // 1 == One block free, one block used.
  58. Bitmap bitmap;
  59. };
  60. static constexpr size_t max_order = 12;
  61. BuddyBucket m_buckets[max_order + 1];
  62. PhysicalPageEntry& get_freelist_entry(ChunkIndex) const;
  63. void remove_from_freelist(BuddyBucket&, ChunkIndex);
  64. PhysicalAddress m_base_address { 0 };
  65. size_t m_page_count { 0 };
  66. size_t m_used_chunks { 0 };
  67. IntrusiveListNode<PhysicalZone> m_list_node;
  68. public:
  69. using List = IntrusiveList<PhysicalZone, RawPtr<PhysicalZone>, &PhysicalZone::m_list_node>;
  70. };
  71. }