]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfscommon/Misc/HybridAllocator.c
xnu-2050.24.15.tar.gz
[apple/xnu.git] / bsd / hfs / hfscommon / Misc / HybridAllocator.c
1 /*
2 * Copyright (c) 2009 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 #if CONFIG_HFS_ALLOC_RBTREE
30
31 #define assert(a) { if (!(a)) { panic("File "__FILE__", line %d: assertion '%s' failed.\n", __LINE__, #a); } }
32
33 //#include <sys/systm.h>
34 #include "../../hfs_macos_defs.h"
35 #include "../headers/HybridAllocator.h"
36
37 #define bool Boolean
38
39 #define ALLOC_DEBUG 0
40
41 /*
42 * The rb_wrap macro in RedBlackTree.h automatically generates the source for a variety of functions that
43 * operate on the red-black trees. The bodies of these automatically generated functions are the corresponding
44 * macro from RedBlackTree.h. For example, the extent_tree_length_new() function invokes the rb_new() macro.
45 * We re-define actual wrapper functions around them so that we can re-name them and adjust the functions
46 * that are available to the allocator in VolumeAllocation.c.
47 *
48 * Here are the functions that get automatically generated:
49 * Offset-Tree Functions:
50 *
51 * initialize the tree
52 * static void extent_tree_offset_new(extent_tree_offset_t * tree)
53 *
54 * Get the first node in the tree. If it is empty, return NULL
55 * static extent_node_t* extent_tree_offset_first (extent_tree_offset_t * tree)
56 *
57 * Get the last node in the tree. If it is empty, return NULL
58 * static extent_node_t* extent_tree_offset_last (extent_tree_offset_t * tree)
59 *
60 * From a given extent_node_t, grab the next one. If no next exists, return NULL
61 * static extent_node_t* extent_tree_offset_next (extent_tree_offset_t * tree, extent_node_t * node)
62 *
63 * From a given extent_node_t, grab the previous. If no prev exists, return NULL
64 * static extent_node_t* extent_tree_offset_prev(extent_tree_offset_t * tree, extent_node_t * node)
65 *
66 * Find a extent_node_t with the specified key (search by offset). If it does not exist, return NULL
67 * static extent_node_t* extent_tree_offset_search(extent_tree_offset_t * tree, extent_node_t * key)
68 *
69 * Find an extent node_t withthe specified key (offset). If it does not exist,
70 * either grab the next node, if possible, or return NULL
71 * static extent_node_t* extent_tree_offset_nsearch(extent_tree_offset_t * tree, extent_node_t * key)
72 *
73 * Find an extent_node_t with the specified key (offset). If it does not exist,
74 * either grab the previous node, if possible, or return NULL
75 * static extent_node_t* extent_tree_offset_psearch(extent_tree_offset_t * tree, extent_node_t * key)
76 *
77 * Insert the specified node into the tree.
78 * static void extent_tree_offset_insert(extent_tree_offset_t * tree, extent_node_t * node)
79 *
80 * Remove the specified node from the tree.
81 * static void extent_tree_offset_remove(extent_tree_offset_t * tree, extent_node_t * node)
82 *
83 */
84
85
86 /* Static Functions only used in this file */
87 static int32_t
88 extent_tree_internal_alloc_space(extent_tree_offset_t *offset_tree,
89 u_int32_t size, u_int32_t offset, extent_node_t *node);
90
91 /*
92 * cmp_offset_node
93 *
94 * Compare the extents in two nodes by offset.
95 *
96 * Returns:
97 * -1 if node 1's offset < node 2's offset.
98 * 1 if node 1's offset > node 2's offset.
99 */
100
101 __private_extern__ int
102 cmp_offset_node(extent_node_t *node_1, extent_node_t *node_2) {
103 u_int32_t addr_1 = node_1->offset;
104 u_int32_t addr_2 = node_2->offset;
105
106 return ((addr_1 > addr_2) - (addr_1 < addr_2));
107 }
108
109 /*
110 * Allocate a new red-black tree node.
111 *
112 * Currently, we get memory from the M_TEMP zone.
113 * TODO: Need to get our own zone to avoid bloating the M_TEMP zone.
114 */
115 __private_extern__ extent_node_t *
116 alloc_node(u_int32_t length, u_int32_t offset) {
117 extent_node_t *node;
118 MALLOC(node, extent_node_t *, sizeof(extent_node_t), M_TEMP, M_WAITOK);
119
120 if (node) {
121 node->offset = offset;
122 node->length = length;
123 node->offset_next = NULL;
124 }
125 return node;
126 }
127
128 /*
129 * De-allocate a red-black tree node.
130 *
131 * Currently, this goes back to the M_TEMP zone.
132 * TODO: May need to adjust this if we pull memory out of our own zone.
133 */
134 __private_extern__ void
135 free_node(extent_node_t *node) {
136 FREE(node, M_TEMP);
137 }
138
139 /*
140 * rb_wrap is a macro found in the rb.h header file. It builds functions that operate on
141 * the red-black tree based upon the types specified here. This code will build red-black tree
142 * search functions that operate on extent_node_t's and use cmp_length_node to do length searches.
143 * It uses cmp_offset_node to do offset searches. Ties are broken by offset. This will generate
144 * the functions specified above.
145 */
146
147 rb_wrap(__attribute__ ((unused)) static, extent_tree_offset_, extent_tree_offset_t, extent_node_t, offset_link, cmp_offset_node)
148
149
150 /*
151 * Create a new extent tree, composed of links sorted by offset.
152 */
153 __private_extern__ void
154 extent_tree_init(extent_tree_offset_t *offset_tree)
155 {
156 extent_node_t *node = NULL;
157 extent_tree_offset_new(offset_tree);
158
159 node = extent_tree_off_first (offset_tree);
160 if (node) {
161 node->offset_next = NULL;
162 }
163 }
164
165 /*
166 * Destroy an extent tree
167 *
168 * This function finds the first node in the specified red-black tree, then
169 * uses the embedded linked list to walk through the tree in O(n) time and destroy
170 * all of its nodes.
171 */
172 __private_extern__ void
173 extent_tree_destroy(extent_tree_offset_t *off_tree) {
174 extent_node_t *node = NULL;
175 extent_node_t *next = NULL;
176
177 node = extent_tree_offset_first (off_tree);
178
179 while (node) {
180 next = node->offset_next;
181 extent_tree_offset_remove (off_tree, node);
182 free_node (node);
183 node = next;
184 }
185 }
186
187 /*
188 * Search the extent tree by offset. The "key" argument is only used to extract
189 * the offset and length information. Its link fields are not used in the underlying
190 * tree code.
191 */
192 __private_extern__ extent_node_t *
193 extent_tree_off_search(extent_tree_offset_t *tree, extent_node_t *key) {
194 return extent_tree_offset_search(tree, key);
195 }
196
197 /*
198 * Search the extent tree by offset, finding the next node in the tree
199 * if the specified one does not exist. The "key" argument is only used to extract
200 * the offset and length information. Its link fields are not used in the underlying
201 * tree code.
202 */
203 __private_extern__ extent_node_t *
204 extent_tree_off_search_next(extent_tree_offset_t *offset_tree, extent_node_t *key) {
205
206 return extent_tree_offset_nsearch (offset_tree, key);
207 }
208
209 /*
210 * Search the extent tree by offset to find a starting position. Then, do a linear search
211 * through the list of free extents to find the first free extent in the tree that has size
212 * greater than or equal to the specified size. The "key" argument is only used to extract
213 * the offset and length information. Its link fields are not used in the underlying
214 * tree code.
215 */
216 __private_extern__ extent_node_t *
217 extent_tree_off_search_nextWithSize (extent_tree_offset_t *offset_tree, extent_node_t *key) {
218
219 extent_node_t *current;
220
221 u_int32_t min_size = key->length;
222
223 current = extent_tree_offset_nsearch (offset_tree, key);
224
225 while (current) {
226 if (current->length >= min_size) {
227 return current;
228 }
229 current = current->offset_next;
230 }
231
232 /* return NULL if no free extent of suitable size could be found. */
233 return NULL;
234 }
235
236
237 /*
238 * Search the extent tree by offset, finding the previous node in the tree
239 * if the specified one does not exist. The "key" argument is only used to extract
240 * the offset and length information. Its link fields are not used in the underlying
241 * tree code.
242 */
243 __private_extern__ extent_node_t *
244 extent_tree_off_search_prev(extent_tree_offset_t *offset_tree, extent_node_t *key) {
245
246 return extent_tree_offset_psearch (offset_tree, key);
247 }
248
249
250 /*
251 * Find the first node in the extent tree, by offset. This will be the first
252 * free space region relative to the start of the disk.
253 */
254 __private_extern__ extent_node_t *
255 extent_tree_off_first (extent_tree_offset_t *offset_tree) {
256 return extent_tree_offset_first(offset_tree);
257 }
258
259 /*
260 * From a given tree node (sorted by offset), get the next node in the tree.
261 */
262 __private_extern__ extent_node_t *
263 extent_tree_off_next(extent_tree_offset_t * tree, extent_node_t *node)
264 {
265 return extent_tree_offset_next(tree, node);
266 }
267
268 /*
269 * From a given tree node (sorted by offset), get the previous node in the tree.
270 */
271 __private_extern__ extent_node_t *
272 extent_tree_off_prev(extent_tree_offset_t * tree, extent_node_t *node)
273 {
274 return extent_tree_offset_prev(tree, node);
275 }
276
277
278 /*
279 * For a node of a given offset and size, remove it from the extent tree and
280 * insert a new node that:
281 *
282 * A) increase its offset by that of the node we just removed
283 * B) decreases its size by that of the node we just removed.
284 *
285 * NOTE: Callers must ensure that the 'size' specified is less than or equal to the
286 * length of the extent represented by node. The node pointer must point to an
287 * extant node in the tree, as it will be removed from the tree.
288 */
289 static int32_t
290 extent_tree_internal_alloc_space(extent_tree_offset_t *offset_tree, u_int32_t size,
291 u_int32_t offset, extent_node_t *node)
292 {
293 if (node) {
294 extent_node_t *prev = NULL;
295 extent_node_t *next = NULL;
296
297 if( ALLOC_DEBUG ) {
298 assert ((size <= node->length));
299 assert ((offset == node->offset));
300 }
301
302 prev = extent_tree_offset_prev(offset_tree, node);
303
304 /*
305 * Note that, unless the node is exactly the size of the amount of space
306 * requested, we do not need to remove it from the offset tree, now matter
307 * how much space we remove from the node. Remember that the offset tree is
308 * sorting the extents based on their offsets, and that each node is a discrete
309 * chunk of free space.
310 *
311 * If node A has offset B, with length C, in the offset tree, by definition, there
312 * can be no other node in the extent tree within the range {B, B+C}. If there were,
313 * we'd have overlapped extents.
314 *
315 * So in the normal case, we'll just update the offset node in place with the new offset
316 * and size.
317 *
318 * Otherwise, if we have an exact match, then just remove the node altogether. Don't forget
319 * to update the next pointer for the linked list if applicable.
320 */
321 if (node->length == size) {
322 next = node->offset_next;
323 extent_tree_offset_remove(offset_tree, node);
324 free_node(node);
325 if (prev) {
326 prev->offset_next = next;
327 }
328 }
329 else {
330 node->offset = node->offset + size;
331 node->length -= size;
332 /* The next pointer does not change since we keep the node in place */
333 }
334 return 0;
335 }
336 return -1;
337 }
338
339 /*
340 * Search the extent tree for a region of free space after the specified
341 * offset and attempt to allocate it.
342 *
343 * This is expected to be used by attempts to grow a file contiguously. If we
344 * start at a file's EOF, then we can try to allocate space immediately after it
345 * if it's available. This function specifies a tail (the offset), and then passes it
346 * into extent_tree_offset_search. Note that this is not the search_prev or search_next
347 * variant, so if no node exists at the specified offset we'll fail out.
348 *
349 */
350
351 __private_extern__ int32_t
352 extent_tree_offset_alloc_space(extent_tree_offset_t *offset_tree, u_int32_t size, u_int32_t offset) {
353 extent_node_t search_sentinel = { .offset = offset };
354 extent_node_t *node = extent_tree_offset_search(offset_tree, &search_sentinel);
355 if (node && (node->length < size)) {
356 /* It's too small. Fail the allocation */
357 if ( ALLOC_DEBUG ) {
358 printf("HFS Allocator: internal_alloc_space, ptr (%p) node->length (%d), node->offset (%d), off(%d), size (%d) \n",
359 node, node->length, node->offset, offset, size);
360 }
361 return -1;
362 }
363 return extent_tree_internal_alloc_space(offset_tree, size, offset, node);
364 }
365
366
367 /*
368 * Search the extent tree for a region of free space at the specified
369 * offset and attempt to allocate it.
370 *
371 * This is a little bit more involved than the previous function. It is intended for use when
372 * we may be allocating space from the middle of an existing extent node.
373 *
374 */
375
376
377 __private_extern__ int32_t
378 extent_tree_offset_alloc_unaligned(extent_tree_offset_t *offset_tree, u_int32_t size, u_int32_t offset) {
379 extent_node_t search_sentinel = { .offset = offset };
380 extent_node_t *node= NULL;
381
382 node = extent_tree_off_search_prev(offset_tree, &search_sentinel);
383
384 if (node == NULL) {
385 return -1;
386 }
387
388 if (node && (node->length < size)) {
389 /* It's too small. Fail the allocation */
390 if ( ALLOC_DEBUG ) {
391 printf("HFS Allocator: internal_alloc_space, ptr (%p) node->length (%d), node->offset (%d), off(%d), size (%d) \n",
392 node, node->length, node->offset, offset, size);
393 }
394 return -1;
395 }
396
397 /* Now see if we need to split this node because we're not allocating from the beginning */
398 if (offset != node->offset) {
399
400 if (ALLOC_DEBUG) {
401 assert ((offset + size) <= (node->offset + node->length));
402 if (node->offset_next) {
403 assert ((offset > node->offset) && (offset < node->offset_next->offset));
404 }
405 }
406
407 u_int32_t end = node->offset + node->length;
408 node->length = offset - node->offset;
409
410 /*
411 * Do we need to create a new node? If our extent we're carving away ends earlier than
412 * the current extent's length, then yes - we do.
413 */
414 if ((offset + size) < (end)) {
415 u_int32_t newoff = offset + size;
416 u_int32_t newlen = end - newoff;
417
418 extent_node_t* newnode = alloc_node(newlen, newoff);
419 extent_tree_offset_insert(offset_tree, newnode);
420
421 extent_node_t *next = extent_tree_offset_next(offset_tree, newnode);
422 newnode->offset_next = next;
423 node->offset_next = newnode;
424 }
425
426 return 0;
427 }
428 else {
429 return extent_tree_internal_alloc_space(offset_tree, size, offset, node);
430 }
431 }
432
433
434
435 /*
436 * Mark an extent of space as being free. This means we need to insert
437 * this extent into our tree.
438 *
439 * Search the offset tree, based on the new offset that we construct by adding
440 * the length of our extent to be freed to its offset. If something exists at
441 * that offset, then we coalesce the nodes. In this case, we do not need to adjust
442 * the offset tree because our extent we wanted to add could not have been in the tree.
443 *
444 * If no node existed at the specified offset, then create a new one and insert it
445 * into the tree.
446 *
447 * Finally, search based on the node that would precede our newly created/inserted one.
448 * If possible, coalesce the previous node into our new one.
449 *
450 * We return the node which we are modifying in this function.
451 */
452
453 __private_extern__ extent_node_t *
454 extent_tree_free_space(extent_tree_offset_t *offset_tree, u_int32_t size, u_int32_t offset)
455 {
456 extent_node_t *prev = NULL;
457 extent_node_t *node = NULL;
458 extent_node_t *next = NULL;
459 extent_node_t search_sentinel = { .offset = size + offset };
460
461 node = extent_tree_offset_nsearch(offset_tree, &search_sentinel);
462 /* Insert our node into the tree, and coalesce with the next one if necessary */
463
464 if ((node) && (node->offset == search_sentinel.offset)) {
465 node->offset = offset;
466 node->length += size;
467 next = node->offset_next;
468 }
469 else {
470 node = alloc_node(size, offset);
471 assert(node);
472 extent_tree_offset_insert(offset_tree, node);
473
474 /* Find the next entry in the tree, if applicable. */
475 next = extent_tree_offset_next(offset_tree, node);
476 node->offset_next = next;
477 }
478
479 /* Coalesce with the previous if necessary */
480 prev = extent_tree_offset_prev(offset_tree, node);
481 if (prev && (prev->offset + prev->length) == offset) {
482 extent_tree_offset_remove(offset_tree, prev);
483 node->offset = prev->offset;
484 node->length += prev->length;
485 free_node(prev);
486 prev = extent_tree_offset_prev(offset_tree, node);
487 }
488
489 /* Update the next pointer for the previous entry (if necessary) */
490 if (prev) {
491 prev->offset_next = node;
492 }
493
494 return node;
495 }
496
497 /*
498 * Remove the specified node from the offset_tree. Note that the parameter node
499 * must be an extant node in the tree. This function is used by the allocator when
500 * we are resizing a volume and need to directly manipulate the contents of the red-black
501 * tree without going through the normal allocation and deallocation routines.
502 */
503 __private_extern__ void
504 extent_tree_remove_node (extent_tree_offset_t *offset_tree, extent_node_t * node) {
505
506 if (node) {
507 /* Just remove the entry from the tree */
508 extent_tree_offset_remove(offset_tree, node);
509 }
510 return;
511
512 }
513
514
515
516 #if ALLOC_DEBUG
517 /*
518 * For each node in the tree, print out its length and block offset.
519 */
520 __private_extern__ void
521 extent_tree_offset_print(extent_tree_offset_t *offset_tree)
522 {
523 extent_node_t *node = NULL;
524
525 node = extent_tree_offset_first(offset_tree);
526 while (node) {
527 printf("length: %u, offset: %u\n", node->length, node->offset);
528 node = node->offset_next;
529 }
530 }
531 #endif
532
533 #endif