-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbuddy.c
333 lines (287 loc) · 9.36 KB
/
buddy.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
#include "types.h"
#include "param.h"
#include "memlayout.h"
#include "spinlock.h"
#include "riscv.h"
#include "defs.h"
#include "list.h"
// Buddy allocator
static int nsizes; // the number of entries in bd_sizes array
#define LEAF_SIZE 16 // The smallest block size
#define MAXSIZE (nsizes - 1) // Largest index in bd_sizes array
#define BLK_SIZE(k) ((1L << (k)) * LEAF_SIZE) // Size of block at size k
#define HEAP_SIZE BLK_SIZE(MAXSIZE)
#define NBLK(k) (1 << (MAXSIZE - k)) // Number of block at size k
#define ROUNDUP(n, sz) \
(((((n)-1) / (sz)) + 1) * (sz)) // Round up to the next multiple of sz
typedef struct list Bd_list;
// The allocator has sz_info for each size k. Each sz_info has a free
// list, an array alloc to keep track which blocks have been
// allocated, and an split array to to keep track which blocks have
// been split. The arrays are of type char (which is 1 byte), but the
// allocator uses 1 bit per block (thus, one char records the info of
// 8 blocks).
struct sz_info {
Bd_list free;
char *alloc;
char *split;
};
typedef struct sz_info Sz_info;
static Sz_info *bd_sizes;
static void *bd_base; // start address of memory managed by the buddy allocator
static struct spinlock lock;
// Return 1 if bit at position index in array is set to 1
int bit_isset(char *array, int index) {
char b = array[index / 8];
char m = (1 << (index % 8));
return (b & m) == m;
}
// Set bit at position index in array to 1
void bit_set(char *array, int index) {
char b = array[index / 8];
char m = (1 << (index % 8));
array[index / 8] = (b | m);
}
// Clear bit at position index in array
void bit_clear(char *array, int index) {
char b = array[index / 8];
char m = (1 << (index % 8));
array[index / 8] = (b & ~m);
}
// Print a bit vector as a list of ranges of 1 bits
void bd_print_vector(char *vector, int len) {
int last, lb;
last = 1;
lb = 0;
for (int b = 0; b < len; b++) {
if (last == bit_isset(vector, b)) continue;
if (last == 1) printf(" [%d, %d)", lb, b);
lb = b;
last = bit_isset(vector, b);
}
if (lb == 0 || last == 1) {
printf(" [%d, %d)", lb, len);
}
printf("\n");
}
// Print buddy's data structures
void bd_print() {
for (int k = 0; k < nsizes; k++) {
printf("size %d (blksz %ld nblk %d): free list: ", k, BLK_SIZE(k), NBLK(k));
lst_print(&bd_sizes[k].free);
printf(" alloc:");
bd_print_vector(bd_sizes[k].alloc, NBLK(k));
if (k > 0) {
printf(" split:");
bd_print_vector(bd_sizes[k].split, NBLK(k));
}
}
}
// What is the first k such that 2^k >= n?
int firstk(uint64 n) {
int k = 0;
uint64 size = LEAF_SIZE;
while (size < n) {
k++;
size *= 2;
}
return k;
}
// Compute the block index for address p at size k
int blk_index(int k, char *p) {
int n = p - (char *)bd_base;
return n / BLK_SIZE(k);
}
// Convert a block index at size k back into an address
void *addr(int k, int bi) {
int n = bi * BLK_SIZE(k);
return (char *)bd_base + n;
}
// allocate nbytes, but malloc won't return anything smaller than LEAF_SIZE
void *bd_malloc(uint64 nbytes) {
int fk, k;
acquire(&lock);
// Find a free block >= nbytes, starting with smallest k possible
fk = firstk(nbytes);
for (k = fk; k < nsizes; k++) {
if (!lst_empty(&bd_sizes[k].free)) break;
}
if (k >= nsizes) { // No free blocks?
release(&lock);
return 0;
}
// Found a block; pop it and potentially split it.
char *p = lst_pop(&bd_sizes[k].free);
bit_set(bd_sizes[k].alloc, blk_index(k, p));
for (; k > fk; k--) {
// split a block at size k and mark one half allocated at size k-1
// and put the buddy on the free list at size k-1
char *q = p + BLK_SIZE(k - 1); // p's buddy
bit_set(bd_sizes[k].split, blk_index(k, p));
bit_set(bd_sizes[k - 1].alloc, blk_index(k - 1, p));
lst_push(&bd_sizes[k - 1].free, q);
}
release(&lock);
return p;
}
// Find the size of the block that p points to.
int size(char *p) {
for (int k = 0; k < nsizes; k++) {
if (bit_isset(bd_sizes[k + 1].split, blk_index(k + 1, p))) {
return k;
}
}
return 0;
}
// Free memory pointed to by p, which was earlier allocated using
// bd_malloc.
void bd_free(void *p) {
void *q;
int k;
acquire(&lock);
for (k = size(p); k < MAXSIZE; k++) {
int bi = blk_index(k, p);
int buddy = (bi % 2 == 0) ? bi + 1 : bi - 1;
bit_clear(bd_sizes[k].alloc, bi); // free p at size k
if (bit_isset(bd_sizes[k].alloc, buddy)) { // is buddy allocated?
break; // break out of loop
}
// budy is free; merge with buddy
q = addr(k, buddy);
lst_remove(q); // remove buddy from free list
if (buddy % 2 == 0) {
p = q;
}
// at size k+1, mark that the merged buddy pair isn't split
// anymore
bit_clear(bd_sizes[k + 1].split, blk_index(k + 1, p));
}
lst_push(&bd_sizes[k].free, p);
release(&lock);
}
// Compute the first block at size k that doesn't contain p
int blk_index_next(int k, char *p) {
int n = (p - (char *)bd_base) / BLK_SIZE(k);
if ((p - (char *)bd_base) % BLK_SIZE(k) != 0) n++;
return n;
}
int _log2(uint64 n) {
int k = 0;
while (n > 1) {
k++;
n = n >> 1;
}
return k;
}
// Mark memory from [start, stop), starting at size 0, as allocated.
void bd_mark(void *start, void *stop) {
int bi, bj;
if (((uint64)start % LEAF_SIZE != 0) || ((uint64)stop % LEAF_SIZE != 0))
panic("bd_mark");
for (int k = 0; k < nsizes; k++) {
bi = blk_index(k, start);
bj = blk_index_next(k, stop);
for (; bi < bj; bi++) {
if (k > 0) {
// if a block is allocated at size k, mark it as split too.
bit_set(bd_sizes[k].split, bi);
}
bit_set(bd_sizes[k].alloc, bi);
}
}
}
// If a block is marked as allocated and the buddy is free, put the
// buddy on the free list at size k.
int bd_initfree_pair(int k, int bi) {
int buddy = (bi % 2 == 0) ? bi + 1 : bi - 1;
int free = 0;
if (bit_isset(bd_sizes[k].alloc, bi) != bit_isset(bd_sizes[k].alloc, buddy)) {
// one of the pair is free
free = BLK_SIZE(k);
if (bit_isset(bd_sizes[k].alloc, bi))
lst_push(&bd_sizes[k].free, addr(k, buddy)); // put buddy on free list
else
lst_push(&bd_sizes[k].free, addr(k, bi)); // put bi on free list
}
return free;
}
// Initialize the free lists for each size k. For each size k, there
// are only two pairs that may have a buddy that should be on free list:
// bd_left and bd_right.
int bd_initfree(void *bd_left, void *bd_right) {
int free = 0;
for (int k = 0; k < MAXSIZE; k++) { // skip max size
int left = blk_index_next(k, bd_left);
int right = blk_index(k, bd_right);
free += bd_initfree_pair(k, left);
if (right <= left) continue;
free += bd_initfree_pair(k, right);
}
return free;
}
// Mark the range [bd_base,p) as allocated
int bd_mark_data_structures(char *p) {
int meta = p - (char *)bd_base;
printf("bd: %d meta bytes for managing %ld bytes of memory\n", meta,
BLK_SIZE(MAXSIZE));
bd_mark(bd_base, p);
return meta;
}
// Mark the range [end, HEAPSIZE) as allocated
int bd_mark_unavailable(void *end, void *left) {
int unavailable = BLK_SIZE(MAXSIZE) - (end - bd_base);
if (unavailable > 0) unavailable = ROUNDUP(unavailable, LEAF_SIZE);
printf("bd: 0x%x bytes unavailable\n", unavailable);
void *bd_end = bd_base + BLK_SIZE(MAXSIZE) - unavailable;
bd_mark(bd_end, bd_base + BLK_SIZE(MAXSIZE));
return unavailable;
}
// Initialize the buddy allocator: it manages memory from [base, end).
void bd_init(void *base, void *end) {
char *p = (char *)ROUNDUP((uint64)base, LEAF_SIZE);
int sz;
initlock(&lock, "buddy");
bd_base = (void *)p;
// compute the number of sizes we need to manage [base, end)
nsizes = _log2(((char *)end - p) / LEAF_SIZE) + 1;
if ((char *)end - p > BLK_SIZE(MAXSIZE)) {
nsizes++; // round up to the next power of 2
}
printf("bd: memory sz is %ld bytes; allocate an size array of length %d\n",
(char *)end - p, nsizes);
// allocate bd_sizes array
bd_sizes = (Sz_info *)p;
p += sizeof(Sz_info) * nsizes;
memset(bd_sizes, 0, sizeof(Sz_info) * nsizes);
// initialize free list and allocate the alloc array for each size k
for (int k = 0; k < nsizes; k++) {
lst_init(&bd_sizes[k].free);
sz = sizeof(char) * ROUNDUP(NBLK(k), 8) / 8;
bd_sizes[k].alloc = p;
memset(bd_sizes[k].alloc, 0, sz);
p += sz;
}
// allocate the split array for each size k, except for k = 0, since
// we will not split blocks of size k = 0, the smallest size.
for (int k = 1; k < nsizes; k++) {
sz = sizeof(char) * (ROUNDUP(NBLK(k), 8)) / 8;
bd_sizes[k].split = p;
memset(bd_sizes[k].split, 0, sz);
p += sz;
}
p = (char *)ROUNDUP((uint64)p, LEAF_SIZE);
// done allocating; mark the memory range [base, p) as allocated, so
// that buddy will not hand out that memory.
int meta = bd_mark_data_structures(p);
// mark the unavailable memory range [end, HEAP_SIZE) as allocated,
// so that buddy will not hand out that memory.
int unavailable = bd_mark_unavailable(end, p);
void *bd_end = bd_base + BLK_SIZE(MAXSIZE) - unavailable;
// initialize free lists for each size k
int free = bd_initfree(p, bd_end);
// check if the amount that is free is what we expect
if (free != BLK_SIZE(MAXSIZE) - meta - unavailable) {
printf("free %d %ld\n", free, BLK_SIZE(MAXSIZE) - meta - unavailable);
panic("bd_init: free mem");
}
}