Quake II RTX doxygen  1.0 dev
buddy_allocator.c File Reference
#include "shared/shared.h"
#include "buddy_allocator.h"
#include <assert.h>

Go to the source code of this file.

Classes

struct  AllocatorFreeListItem
 
struct  BuddyAllocator
 

Typedefs

typedef enum AllocatorBlockState AllocatorBlockState
 
typedef struct AllocatorFreeListItem AllocatorFreeListItem
 
typedef struct BuddyAllocator BuddyAllocator
 

Enumerations

enum  AllocatorBlockState { BLOCK_NONE = 0, BLOCK_FREE, BLOCK_SPLIT, BLOCK_ALLOCATED }
 

Functions

static size_t _align (size_t value, size_t alignment)
 
static int32_t uint_log2 (uint64_t x)
 
static AllocatorFreeListItemallocate_list_item (BuddyAllocator *allocator)
 
static void free_list_item (BuddyAllocator *allocator, AllocatorFreeListItem *item)
 
static void write_free_block_to_list (BuddyAllocator *allocator, uint32_t level, uint32_t block_index)
 
static uint32_t get_level_offset (BuddyAllocator *allocator, uint32_t level)
 
static uint32_t get_next_level_offset (BuddyAllocator *allocator, uint32_t level, uint32_t offset)
 
void subdivide_block (BuddyAllocator *allocator, uint32_t src_level, uint32_t dst_level)
 
qboolean merge_blocks (BuddyAllocator *allocator, uint32_t level, uint32_t block_index)
 
void remove_block_from_free_list (BuddyAllocator *allocator, uint32_t level, uint32_t block_index)
 
BuddyAllocatorcreate_buddy_allocator (uint64_t capacity, uint64_t block_size)
 
BAResult buddy_allocator_allocate (BuddyAllocator *allocator, uint64_t size, uint64_t alignment, uint64_t *offset)
 
void buddy_allocator_free (BuddyAllocator *allocator, uint64_t offset, uint64_t size)
 
void destroy_buddy_allocator (BuddyAllocator *allocator)
 

Typedef Documentation

◆ AllocatorBlockState

◆ AllocatorFreeListItem

◆ BuddyAllocator

Enumeration Type Documentation

◆ AllocatorBlockState

Enumerator
BLOCK_NONE 
BLOCK_FREE 
BLOCK_SPLIT 
BLOCK_ALLOCATED 

Definition at line 23 of file buddy_allocator.c.

24 {
25  BLOCK_NONE = 0,
26  BLOCK_FREE,

Function Documentation

◆ _align()

static size_t _align ( size_t  value,
size_t  alignment 
)
inlinestatic

Definition at line 234 of file buddy_allocator.c.

235 {
236  return (value + alignment - 1) / alignment * alignment;
237 }

Referenced by create_buddy_allocator().

◆ allocate_list_item()

static AllocatorFreeListItem * allocate_list_item ( BuddyAllocator allocator)
inlinestatic

Definition at line 247 of file buddy_allocator.c.

248 {
249  AllocatorFreeListItem* item = allocator->free_items;
250  allocator->free_items = item->next;
251  return item;
252 }

Referenced by subdivide_block(), and write_free_block_to_list().

◆ buddy_allocator_allocate()

BAResult buddy_allocator_allocate ( BuddyAllocator allocator,
uint64_t  size,
uint64_t  alignment,
uint64_t *  offset 
)

Definition at line 97 of file buddy_allocator.c.

98 {
99  uint32_t level = max((uint32_t)ceil(log2(size)), allocator->base_level) - allocator->base_level;
100 
101  // The requested size exceeds the allocator capacity
102  if (level >= allocator->level_num)
103  return BA_NOT_ENOUGH_MEMORY;
104 
105  // Every block is aligned to its size
106  const uint64_t block_size = (uint64_t)(1 << (level + allocator->base_level));
107  const uint64_t alignment_size = block_size % alignment;
108 
109  assert(alignment_size == 0);
110  if (alignment_size != 0)
111  return BA_INVALID_ALIGNMENT;
112 
113  uint32_t i = level;
114  for (; i < allocator->level_num && allocator->free_block_lists[i] == NULL; i++)
115  {
116  }
117 
118  if (i == allocator->level_num)
119  return BA_NOT_ENOUGH_MEMORY;
120 
121  if (i > level)
122  subdivide_block(allocator, i, level);
123 
124  AllocatorFreeListItem* item = allocator->free_block_lists[level];
125  allocator->free_block_lists[level] = item->next;
126 
127  const uint32_t level_block_offset = get_level_offset(allocator, level);
128  allocator->block_states[level_block_offset + item->block_index] = BLOCK_ALLOCATED;
129 
130  *offset = item->block_index * block_size;
131  free_list_item(allocator, item);
132 
133  return BA_SUCCESS;
134 }

Referenced by allocate_device_memory().

◆ buddy_allocator_free()

void buddy_allocator_free ( BuddyAllocator allocator,
uint64_t  offset,
uint64_t  size 
)

Definition at line 136 of file buddy_allocator.c.

137 {
138  const uint32_t level = max((uint32_t)ceil(log2(size)), allocator->base_level) - allocator->base_level;
139  const uint64_t block_size = (uint64_t)(1 << (level + allocator->base_level));
140  const uint32_t block_index = offset / block_size;
141 
142  const uint32_t level_block_offset = get_level_offset(allocator, level);
143  assert(allocator->block_states[level_block_offset + block_index] == BLOCK_ALLOCATED);
144  allocator->block_states[level_block_offset + block_index] = BLOCK_FREE;
145 
146  if (!merge_blocks(allocator, level, block_index))
147  write_free_block_to_list(allocator, level, block_index);
148 }

Referenced by free_device_memory().

◆ create_buddy_allocator()

BuddyAllocator* create_buddy_allocator ( uint64_t  capacity,
uint64_t  block_size 
)

Definition at line 59 of file buddy_allocator.c.

60 {
61  const uint32_t base_level = uint_log2(block_size);
62  const uint32_t level_num = uint_log2(capacity) - base_level + 1;
63 
64  uint32_t block_num = 0;
65  for (uint32_t i = 0; i < level_num; i++)
66  block_num += 1 << ((level_num - 1) - i);
67 
68  const size_t alignment = 16;
69  const size_t allocator_size = _align(sizeof(BuddyAllocator), alignment);
70  const size_t free_list_array_size = _align(level_num * sizeof(AllocatorFreeListItem*), alignment);
71  const size_t free_item_buffer_size = _align(block_num * sizeof(AllocatorFreeListItem), alignment);
72  const size_t block_state_size = _align(block_num * sizeof(uint8_t), alignment);
73  char* memory = malloc(allocator_size + free_list_array_size + free_item_buffer_size + block_state_size);
74 
75  BuddyAllocator* allocator = (BuddyAllocator*)memory;
76  allocator->base_level = base_level;
77  allocator->level_num = level_num;
78  allocator->memory = memory;
79  allocator->free_block_lists = (AllocatorFreeListItem**)(memory + allocator_size);
80  allocator->free_items = (AllocatorFreeListItem*)(memory + allocator_size + free_list_array_size);
81  allocator->block_states = (uint8_t*)(memory + allocator_size + free_list_array_size + free_item_buffer_size);
82 
83  memset(allocator->free_block_lists, 0, level_num * sizeof(AllocatorFreeListItem*));
84  memset(allocator->free_items, 0, block_num * sizeof(AllocatorFreeListItem));
85  memset(allocator->block_states, 0, block_num * sizeof(uint8_t));
86 
87  for (uint32_t i = 0; i < block_num; i++)
88  allocator->free_items[i].next = allocator->free_items + i + 1;
89  allocator->free_items[block_num - 1].next = NULL;
90 
91  allocator->block_states[block_num - 1] = BLOCK_FREE;
92  write_free_block_to_list(allocator, level_num - 1, 0);
93 
94  return allocator;
95 }

Referenced by create_sub_allocator().

◆ destroy_buddy_allocator()

void destroy_buddy_allocator ( BuddyAllocator allocator)

Definition at line 150 of file buddy_allocator.c.

151 {
152  free(allocator->memory);
153 }

◆ free_list_item()

static void free_list_item ( BuddyAllocator allocator,
AllocatorFreeListItem item 
)
inlinestatic

Definition at line 254 of file buddy_allocator.c.

255 {
256  item->next = allocator->free_items;
257  allocator->free_items = item;
258 }

Referenced by buddy_allocator_allocate(), and remove_block_from_free_list().

◆ get_level_offset()

static uint32_t get_level_offset ( BuddyAllocator allocator,
uint32_t  level 
)
inlinestatic

Definition at line 268 of file buddy_allocator.c.

269 {
270  uint32_t offset = 0;
271  for (int32_t i = 0; i < level; i++)
272  offset += 1 << ((allocator->level_num - 1) - i);
273 
274  return offset;
275 }

Referenced by buddy_allocator_allocate(), buddy_allocator_free(), merge_blocks(), and subdivide_block().

◆ get_next_level_offset()

static uint32_t get_next_level_offset ( BuddyAllocator allocator,
uint32_t  level,
uint32_t  offset 
)
inlinestatic

Definition at line 277 of file buddy_allocator.c.

278 {
279  return offset + (1 << ((allocator->level_num - 1) - prev_level));
280 }

Referenced by subdivide_block().

◆ merge_blocks()

qboolean merge_blocks ( BuddyAllocator allocator,
uint32_t  level,
uint32_t  block_index 
)

Definition at line 190 of file buddy_allocator.c.

191 {
192  if (level == allocator->level_num - 1)
193  {
194  write_free_block_to_list(allocator, level, block_index);
195  return qtrue;
196  }
197 
198  const uint32_t level_block_offset = get_level_offset(allocator, level);
199  const uint32_t buddy_block_index = (block_index % 2) == 0 ? block_index + 1 : block_index - 1;
200 
201  if (allocator->block_states[level_block_offset + buddy_block_index] != BLOCK_FREE)
202  return qfalse;
203 
204  remove_block_from_free_list(allocator, level, buddy_block_index);
205 
206  const uint32_t next_level_block_offset = get_level_offset(allocator, level + 1);
207  const uint32_t next_level_block_index = block_index / 2;
208  assert(allocator->block_states[next_level_block_offset + next_level_block_index] == BLOCK_SPLIT);
209  allocator->block_states[next_level_block_offset + next_level_block_index] = BLOCK_FREE;
210 
211  if (!merge_blocks(allocator, level + 1, next_level_block_index))
212  write_free_block_to_list(allocator, level + 1, next_level_block_index);
213 
214  return qtrue;
215 }

Referenced by buddy_allocator_free().

◆ remove_block_from_free_list()

void remove_block_from_free_list ( BuddyAllocator allocator,
uint32_t  level,
uint32_t  block_index 
)

Definition at line 217 of file buddy_allocator.c.

218 {
219  AllocatorFreeListItem** prev_next = &allocator->free_block_lists[level];
220  AllocatorFreeListItem* item = allocator->free_block_lists[level];
221 
222  assert(item != NULL);
223  while (item->block_index != block_index)
224  {
225  prev_next = &item->next;
226  item = item->next;
227  assert(item != NULL);
228  }
229 
230  *prev_next = item->next;
231  free_list_item(allocator, item);
232 }

Referenced by merge_blocks().

◆ subdivide_block()

void subdivide_block ( BuddyAllocator allocator,
uint32_t  src_level,
uint32_t  dst_level 
)

Definition at line 155 of file buddy_allocator.c.

156 {
157  assert(dst_level < src_level);
158 
159  {
160  // Task free block from the list
161  AllocatorFreeListItem* item = allocator->free_block_lists[src_level];
162  allocator->free_block_lists[src_level] = item->next;
163 
164  // Find offsets for states
165  const uint32_t previous_level_block_offset = get_level_offset(allocator, src_level - 1);
166  const uint32_t current_level_block_offset = get_next_level_offset(allocator, src_level - 1, previous_level_block_offset);
167 
168  const uint32_t block_index = item->block_index;
169 
170  // Change state of the blocks
171  assert(allocator->block_states[current_level_block_offset + block_index] == BLOCK_FREE);
172  allocator->block_states[current_level_block_offset + block_index] = BLOCK_SPLIT;
173  allocator->block_states[previous_level_block_offset + block_index * 2] = BLOCK_FREE;
174  allocator->block_states[previous_level_block_offset + block_index * 2 + 1] = BLOCK_FREE;
175 
176  // Add blocks to free list
177  AllocatorFreeListItem* item0 = allocate_list_item(allocator);
178  AllocatorFreeListItem* item1 = allocate_list_item(allocator);
179  item0->block_index = block_index * 2;
180  item0->next = item1;
181  item1->block_index = block_index * 2 + 1;
182  item1->next = allocator->free_block_lists[src_level - 1];
183  allocator->free_block_lists[src_level - 1] = item0;
184  }
185 
186  if (src_level - 1 != dst_level)
187  subdivide_block(allocator, src_level - 1, dst_level);
188 }

Referenced by buddy_allocator_allocate().

◆ uint_log2()

static int32_t uint_log2 ( uint64_t  x)
inlinestatic

Definition at line 239 of file buddy_allocator.c.

240 {
241  uint32_t result = 0;
242  while (x >>= 1)
243  result++;
244  return result;
245 }

Referenced by create_buddy_allocator().

◆ write_free_block_to_list()

static void write_free_block_to_list ( BuddyAllocator allocator,
uint32_t  level,
uint32_t  block_index 
)
inlinestatic

Definition at line 260 of file buddy_allocator.c.

261 {
262  AllocatorFreeListItem* item = allocate_list_item(allocator);
263  item->block_index = block_index;
264  item->next = allocator->free_block_lists[level];
265  allocator->free_block_lists[level] = item;
266 }

Referenced by buddy_allocator_free(), create_buddy_allocator(), and merge_blocks().

free_list_item
static void free_list_item(BuddyAllocator *allocator, AllocatorFreeListItem *item)
Definition: buddy_allocator.c:254
subdivide_block
void subdivide_block(BuddyAllocator *allocator, uint32_t src_level, uint32_t dst_level)
Definition: buddy_allocator.c:155
BLOCK_SPLIT
@ BLOCK_SPLIT
Definition: buddy_allocator.c:27
uint_log2
static int32_t uint_log2(uint64_t x)
Definition: buddy_allocator.c:239
BuddyAllocator::block_states
uint8_t * block_states
Definition: buddy_allocator.c:42
BA_SUCCESS
@ BA_SUCCESS
Definition: buddy_allocator.h:24
BuddyAllocator::free_block_lists
struct AllocatorFreeListItem ** free_block_lists
Definition: buddy_allocator.c:41
BuddyAllocator::base_level
uint32_t base_level
Definition: buddy_allocator.c:39
BuddyAllocator::free_items
struct AllocatorFreeListItem * free_items
Definition: buddy_allocator.c:43
BLOCK_NONE
@ BLOCK_NONE
Definition: buddy_allocator.c:25
remove_block_from_free_list
void remove_block_from_free_list(BuddyAllocator *allocator, uint32_t level, uint32_t block_index)
Definition: buddy_allocator.c:217
_align
static size_t _align(size_t value, size_t alignment)
Definition: buddy_allocator.c:234
BLOCK_FREE
@ BLOCK_FREE
Definition: buddy_allocator.c:26
BuddyAllocator
Definition: buddy_allocator.c:37
get_level_offset
static uint32_t get_level_offset(BuddyAllocator *allocator, uint32_t level)
Definition: buddy_allocator.c:268
write_free_block_to_list
static void write_free_block_to_list(BuddyAllocator *allocator, uint32_t level, uint32_t block_index)
Definition: buddy_allocator.c:260
AllocatorFreeListItem::block_index
uint32_t block_index
Definition: buddy_allocator.c:33
BA_INVALID_ALIGNMENT
@ BA_INVALID_ALIGNMENT
Definition: buddy_allocator.h:26
get_next_level_offset
static uint32_t get_next_level_offset(BuddyAllocator *allocator, uint32_t level, uint32_t offset)
Definition: buddy_allocator.c:277
AllocatorBlockState
AllocatorBlockState
Definition: buddy_allocator.c:23
BuddyAllocator::memory
void * memory
Definition: buddy_allocator.c:44
AllocatorFreeListItem::next
struct AllocatorFreeListItem * next
Definition: buddy_allocator.c:34
level
level_locals_t level
Definition: g_main.c:22
BuddyAllocator::level_num
uint32_t level_num
Definition: buddy_allocator.c:40
BLOCK_ALLOCATED
@ BLOCK_ALLOCATED
Definition: buddy_allocator.c:28
BA_NOT_ENOUGH_MEMORY
@ BA_NOT_ENOUGH_MEMORY
Definition: buddy_allocator.h:25
merge_blocks
qboolean merge_blocks(BuddyAllocator *allocator, uint32_t level, uint32_t block_index)
Definition: buddy_allocator.c:190
AllocatorFreeListItem
Definition: buddy_allocator.c:31
allocate_list_item
static AllocatorFreeListItem * allocate_list_item(BuddyAllocator *allocator)
Definition: buddy_allocator.c:247