1 // BlockAllocatorArea.cpp
3 #include "BlockAllocatorArea.h"
7 BlockAllocator::Area::Area(area_id id
, size_t size
)
19 size_t headerSize
= block_align_ceil(sizeof(Area
));
20 fFirstFree
= (TFreeBlock
*)((char*)this + headerSize
);
21 fFirstFree
->SetTo(NULL
, block_align_floor(fSize
- headerSize
), false, NULL
,
23 fFirstBlock
= fLastBlock
= fLastFree
= fFirstFree
;
24 fFreeBytes
= fFirstFree
->GetUsableSize();
28 BlockAllocator::Area
*
29 BlockAllocator::Area::Create(size_t size
)
34 area_id id
= create_area("block alloc", &base
, B_ANY_ADDRESS
,
35 size
, B_NO_LOCK
, B_READ_AREA
| B_WRITE_AREA
);
37 area_id id
= create_area("block alloc", &base
, B_ANY_KERNEL_ADDRESS
,
38 size
, B_FULL_LOCK
, B_READ_AREA
| B_WRITE_AREA
);
41 area
= new(base
) Area(id
, size
);
43 ERROR(("BlockAllocator::Area::Create(%lu): Failed to create area: %s\n",
51 BlockAllocator::Area::Delete()
58 BlockAllocator::Area::AllocateBlock(size_t usableSize
, bool dontDefragment
)
60 if (kMinBlockSize
!= block_align_ceil(sizeof(TFreeBlock
))) {
61 FATAL(("kMinBlockSize is not correctly initialized! Is %lu, but should be: "
62 "%lu\n", kMinBlockSize
, block_align_ceil(sizeof(TFreeBlock
))));
63 BA_PANIC("kMinBlockSize not correctly initialized.");
68 Block
*newBlock
= NULL
;
69 size_t size
= max(usableSize
+ sizeof(BlockHeader
), kMinBlockSize
);
70 size
= block_align_ceil(size
);
71 if (size
<= _GetBlockFreeBytes()) {
73 TFreeBlock
*block
= _FindFreeBlock(size
);
74 if (!block
&& !dontDefragment
) {
75 // defragmenting is necessary
77 block
= _FindFreeBlock(size
);
80 // Our data structures seem to be corrupted, since
81 // _GetBlockFreeBytes() promised that we would have enough
83 FATAL(("Couldn't find free block of min size %lu after "
84 "defragmenting, although we should have %lu usable free "
85 "bytes!\n", size
, _GetBlockFreeBytes()));
86 BA_PANIC("Bad area free bytes.");
91 size_t remainder
= block
->GetSize() - size
;
92 if (remainder
>= kMinBlockSize
) {
93 // enough space left for a free block
94 Block
*freePrev
= block
->GetPreviousBlock();
95 // TFreeBlock *prevFree = block->GetPreviousFreeBlock();
96 // TFreeBlock *nextFree = block->GetNextFreeBlock();
98 _MoveResizeFreeBlock(block
, size
, remainder
);
99 // setup the new block
100 // newBlock->SetSize(size, true);
101 // newBlock->SetFree(false);
102 newBlock
= _MakeUsedBlock(block
, 0, freePrev
, size
, true);
104 // not enough space left: take the free block over completely
105 // remove the block from the free list
106 _RemoveFreeBlock(block
);
108 newBlock
->SetFree(false);
111 fFreeBytes
-= newBlock
->GetSize();
123 BlockAllocator::Area::FreeBlock(Block
*block
, bool dontDefragment
)
126 // mark the block free and insert it into the free list
127 block
->SetFree(true);
128 TFreeBlock
*freeBlock
= (TFreeBlock
*)block
;
129 _InsertFreeBlock(freeBlock
);
131 if (fFreeBlockCount
== 1)
132 fFreeBytes
+= freeBlock
->GetUsableSize();
134 fFreeBytes
+= freeBlock
->GetSize();
135 // try coalescing with the next and the previous free block
137 _CoalesceWithNext(freeBlock
);
139 _CoalesceWithNext(freeBlock
->GetPreviousFreeBlock());
140 // defragment, if sensible
141 if (!dontDefragment
&& _DefragmentingRecommended())
149 BlockAllocator::Area::ResizeBlock(Block
*block
, size_t newUsableSize
,
152 //PRINT(("Area::ResizeBlock(%p, %lu)\n", block, newUsableSize));
153 // newUsableSize must be >0 !
154 if (newUsableSize
== 0)
156 Block
*resultBlock
= NULL
;
158 size_t size
= block
->GetSize();
159 size_t newSize
= max(newUsableSize
+ sizeof(BlockHeader
),
161 newSize
= block_align_ceil(newSize
);
162 if (newSize
== size
) {
163 // size doesn't change: nothing to do
165 } else if (newSize
< size
) {
167 size_t sizeDiff
= size
- newSize
;
168 Block
*nextBlock
= block
->GetNextBlock();
169 if (nextBlock
&& nextBlock
->IsFree()) {
170 // join the space with the adjoining free block
171 TFreeBlock
*freeBlock
= nextBlock
->ToFreeBlock();
172 _MoveResizeFreeBlock(freeBlock
, -sizeDiff
,
173 freeBlock
->GetSize() + sizeDiff
);
174 // resize the block and we're done
175 block
->SetSize(newSize
, true);
176 fFreeBytes
+= sizeDiff
;
177 } else if (sizeDiff
>= sizeof(TFreeBlock
)) {
178 // the freed space is large enough for a free block
179 TFreeBlock
*newFree
= _MakeFreeBlock(block
, newSize
, block
,
180 sizeDiff
, nextBlock
, NULL
, NULL
);
181 _InsertFreeBlock(newFree
);
182 block
->SetSize(newSize
, true);
183 if (fFreeBlockCount
== 1)
184 fFreeBytes
+= newFree
->GetUsableSize();
186 fFreeBytes
+= newFree
->GetSize();
187 if (!dontDefragment
&& _DefragmentingRecommended())
189 } // else: insufficient space for a free block: no changes
192 //PRINT((" grow...\n"));
194 size_t sizeDiff
= newSize
- size
;
195 Block
*nextBlock
= block
->GetNextBlock();
196 if (nextBlock
&& nextBlock
->IsFree()
197 && nextBlock
->GetSize() >= sizeDiff
) {
198 //PRINT((" adjoining free block\n"));
199 // there is a adjoining free block and it is large enough
200 TFreeBlock
*freeBlock
= nextBlock
->ToFreeBlock();
201 size_t freeSize
= freeBlock
->GetSize();
202 if (freeSize
- sizeDiff
>= sizeof(TFreeBlock
)) {
203 // the remaining space is still large enough for a free
205 _MoveResizeFreeBlock(freeBlock
, sizeDiff
,
206 freeSize
- sizeDiff
);
207 block
->SetSize(newSize
, true);
208 fFreeBytes
-= sizeDiff
;
210 // the remaining free space wouldn't be large enough for
211 // a free block: consume the free block completely
212 Block
*freeNext
= freeBlock
->GetNextBlock();
213 _RemoveFreeBlock(freeBlock
);
214 block
->SetSize(size
+ freeSize
, freeNext
);
215 _FixBlockList(block
, block
->GetPreviousBlock(), freeNext
);
216 if (fFreeBlockCount
== 0)
219 fFreeBytes
-= freeSize
;
223 //PRINT((" no adjoining free block\n"));
224 // no (large enough) adjoining free block: allocate
225 // a new block and copy the data to it
226 BlockReference
*reference
= block
->GetReference();
227 resultBlock
= AllocateBlock(newUsableSize
, dontDefragment
);
228 block
= reference
->GetBlock();
230 resultBlock
->SetReference(reference
);
231 memcpy(resultBlock
->GetData(), block
->GetData(),
232 block
->GetUsableSize());
233 FreeBlock(block
, dontDefragment
);
234 resultBlock
= reference
->GetBlock();
240 //PRINT(("Area::ResizeBlock() done: %p\n", resultBlock));
246 BlockAllocator::Area::SanityCheck() const
250 FATAL(("Area ID < 0: %lx\n", fID
));
251 BA_PANIC("Bad area ID.");
255 size_t areaHeaderSize
= block_align_ceil(sizeof(Area
));
256 if (fSize
< areaHeaderSize
+ sizeof(TFreeBlock
)) {
257 FATAL(("Area too small to contain area header and at least one free "
258 "block: %lu bytes\n", fSize
));
259 BA_PANIC("Bad area size.");
263 if (fFreeBytes
> fSize
) {
264 FATAL(("Free size greater than area size: %lu vs %lu\n", fFreeBytes
,
266 BA_PANIC("Bad area free bytes.");
270 if (fFreeBlockCount
+ fUsedBlockCount
== 0) {
271 FATAL(("Area contains no blocks at all.\n"));
272 BA_PANIC("Bad area block count.");
276 uint32 usedBlockCount
= 0;
277 uint32 freeBlockCount
= 0;
278 size_t freeBytes
= 0;
279 if (!fFirstBlock
|| !fLastBlock
) {
280 FATAL(("Invalid block list: first or last block NULL: first: %p, "
281 "last: %p\n", fFirstBlock
, fLastBlock
));
282 BA_PANIC("Bad area block list.");
285 // iterate through block list and also check free list
286 int32 blockCount
= fFreeBlockCount
+ fUsedBlockCount
;
287 Block
*block
= fFirstBlock
;
288 Block
*prevBlock
= NULL
;
289 Block
*prevFree
= NULL
;
290 Block
*nextFree
= fFirstFree
;
291 bool blockListOK
= true;
292 for (int32 i
= 0; i
< blockCount
; i
++) {
295 FATAL(("Encountered NULL in block list at index %ld, although "
296 "list should have %ld blocks\n", i
, blockCount
));
297 BA_PANIC("Bad area block list.");
300 uint64 address
= (uint32
)block
;
301 // block within area?
302 if (address
< (uint32
)this + areaHeaderSize
303 || address
+ sizeof(TFreeBlock
) > (uint32
)this + fSize
) {
304 FATAL(("Utterly mislocated block: %p, area: %p, "
305 "size: %lu\n", block
, this, fSize
));
306 BA_PANIC("Bad area block.");
309 // block too large for area?
310 size_t blockSize
= block
->GetSize();
311 if (blockSize
< sizeof(TFreeBlock
)
312 || address
+ blockSize
> (uint32
)this + fSize
) {
313 FATAL(("Mislocated block: %p, size: %lu, area: %p, "
314 "size: %lu\n", block
, blockSize
, this, fSize
));
315 BA_PANIC("Bad area block.");
319 if (block_align_floor(address
) != address
320 || block_align_floor(blockSize
) != blockSize
) {
321 FATAL(("Block %ld not properly aligned: %p, size: %lu\n",
322 i
, block
, blockSize
));
323 BA_PANIC("Bad area block.");
327 if (block
->GetPreviousBlock() != prevBlock
) {
328 FATAL(("Previous block of block %ld was not the previous "
329 "block in list: %p vs %p\n", i
,
330 block
->GetPreviousBlock(), prevBlock
));
331 BA_PANIC("Bad area block list.");
334 // additional checks for free block list
335 if (block
->IsFree()) {
337 TFreeBlock
*freeBlock
= block
->ToFreeBlock();
339 freeBytes
+= freeBlock
->GetSize();
341 freeBytes
+= freeBlock
->GetUsableSize();
342 // block == next free block of previous free block
343 if (freeBlock
!= nextFree
) {
344 FATAL(("Free block %ld is not the next block in free "
345 "list: %p vs %p\n", i
, freeBlock
, nextFree
));
346 BA_PANIC("Bad area free list.");
349 // previous free block
350 if (freeBlock
->GetPreviousFreeBlock() != prevFree
) {
351 FATAL(("Previous free block of block %ld was not the "
352 " previous block in free list: %p vs %p\n", i
,
353 freeBlock
->GetPreviousFreeBlock(), prevFree
));
354 BA_PANIC("Bad area free list.");
357 prevFree
= freeBlock
;
358 nextFree
= freeBlock
->GetNextFreeBlock();
362 block
= block
->GetNextBlock();
365 // final checks on block list
368 FATAL(("More blocks in block list than expected\n"));
369 BA_PANIC("Bad area block count.");
371 } else if (fLastBlock
!= prevBlock
) {
372 FATAL(("last block in block list was %p, but should be "
373 "%p\n", fLastBlock
, prevBlock
));
374 BA_PANIC("Bad area last block.");
376 } else if (prevFree
!= fLastFree
) {
377 FATAL(("last block in free list was %p, but should be %p\n",
378 fLastFree
, prevFree
));
379 BA_PANIC("Bad area last free block.");
382 // block counts (a bit reduntant)
383 if (freeBlockCount
!= fFreeBlockCount
) {
384 FATAL(("Free block count is %ld, but should be %ld\n",
385 fFreeBlockCount
, freeBlockCount
));
386 BA_PANIC("Bad area free block count.");
389 if (usedBlockCount
!= fUsedBlockCount
) {
390 FATAL(("Used block count is %ld, but should be %ld\n",
391 fUsedBlockCount
, usedBlockCount
));
392 BA_PANIC("Bad area used block count.");
396 if (fFreeBytes
!= freeBytes
) {
397 FATAL(("Free bytes is %lu, but should be %lu\n",
398 fFreeBytes
, freeBytes
));
399 BA_PANIC("Bad area free bytes.");
409 BlockAllocator::Area::CheckBlock(Block
*checkBlock
, size_t minSize
)
411 for (Block
*block
= fFirstBlock
; block
; block
= block
->GetNextBlock()) {
412 if (block
== checkBlock
)
413 return (block
->GetUsableSize() >= minSize
);
415 FATAL(("Block %p is not in area %p!\n", checkBlock
, this));
416 BA_PANIC("Invalid Block.");
422 BlockAllocator::Area::_FindFreeBlock(size_t minSize
)
425 for (TFreeBlock
*block
= GetFirstFreeBlock();
427 block
= block
->GetNextFreeBlock()) {
428 if (block
->GetSize() >= minSize
)
436 BlockAllocator::Area::_InsertFreeBlock(TFreeBlock
*block
)
439 // find the free block before which this one has to be inserted
440 TFreeBlock
*nextFree
= NULL
;
441 for (nextFree
= GetFirstFreeBlock();
443 nextFree
= nextFree
->GetNextFreeBlock()) {
444 if ((uint32
)nextFree
> (uint32
)block
)
447 // get the previous block and insert the block between the two
449 = (nextFree
? nextFree
->GetPreviousFreeBlock() : fLastFree
);
450 _FixFreeList(block
, prevFree
, nextFree
);
457 BlockAllocator::Area::_RemoveFreeBlock(TFreeBlock
*block
)
460 TFreeBlock
*prevFree
= block
->GetPreviousFreeBlock();
461 TFreeBlock
*nextFree
= block
->GetNextFreeBlock();
463 prevFree
->SetNextFreeBlock(nextFree
);
465 fFirstFree
= nextFree
;
467 nextFree
->SetPreviousFreeBlock(prevFree
);
469 fLastFree
= prevFree
;
474 // _MoveResizeFreeBlock
476 BlockAllocator::Area::_MoveResizeFreeBlock(TFreeBlock
*freeBlock
,
477 ssize_t offset
, size_t newSize
)
479 TFreeBlock
*movedFree
= NULL
;
480 if (freeBlock
&& offset
) {
481 // move the header of the free block
482 Block
*freePrev
= freeBlock
->GetPreviousBlock();
483 TFreeBlock
*prevFree
= freeBlock
->GetPreviousFreeBlock();
484 TFreeBlock
*nextFree
= freeBlock
->GetNextFreeBlock();
485 movedFree
= _MakeFreeBlock(freeBlock
, offset
, freePrev
, newSize
,
486 freeBlock
->HasNextBlock(), prevFree
, nextFree
);
487 // update the free list
488 _FixFreeList(movedFree
, prevFree
, nextFree
);
496 BlockAllocator::Area::_MakeFreeBlock(void *address
, ssize_t offset
,
497 Block
*previous
, size_t size
,
498 bool hasNext
, TFreeBlock
*previousFree
,
499 TFreeBlock
*nextFree
)
501 TFreeBlock
*block
= (TFreeBlock
*)((char*)address
+ offset
);
502 block
->SetTo(previous
, size
, hasNext
, previousFree
, nextFree
);
504 block
->GetNextBlock()->SetPreviousBlock(block
);
512 BlockAllocator::Area::_CoalesceWithNext(TFreeBlock
*block
)
515 TFreeBlock
*nextFree
= NULL
;
516 if (block
&& (nextFree
= block
->GetNextFreeBlock()) != NULL
517 && block
->GetNextBlock() == nextFree
) {
518 _RemoveFreeBlock(nextFree
);
519 Block
*nextBlock
= nextFree
->GetNextBlock();
520 block
->SetSize(block
->GetSize() + nextFree
->GetSize(), nextBlock
);
522 nextBlock
->SetPreviousBlock(block
);
533 BlockAllocator::Area::_MakeUsedBlock(void *address
, ssize_t offset
,
534 Block
*previous
, size_t size
,
537 Block
*block
= (Block
*)((char*)address
+ offset
);
538 block
->SetTo(previous
, size
, false, hasNext
, NULL
);
540 block
->GetNextBlock()->SetPreviousBlock(block
);
548 BlockAllocator::Area::_Defragment()
551 //PRINT(("BlockAllocator::Area::_Defragment()\n"));
552 // A trivial strategy for now: Keep the last free block and move the
553 // others so that they can be joined with it. This is done iteratively
554 // by moving the first free block to adjoin to the second one and
555 // coalescing them. A free block is moved by moving the data blocks in
557 TFreeBlock
*nextFree
= NULL
;
558 while (fFirstFree
&& (nextFree
= fFirstFree
->GetNextFreeBlock()) != NULL
) {
559 Block
*prevBlock
= fFirstFree
->GetPreviousBlock();
560 Block
*nextBlock
= fFirstFree
->GetNextBlock();
561 size_t size
= fFirstFree
->GetSize();
562 // Used blocks are relatively position independed. We can move them
563 // en bloc and only need to adjust the previous pointer of the first
565 if (!nextBlock
->IsFree()) {
566 // move the used blocks
567 size_t chunkSize
= (char*)nextFree
- (char*)nextBlock
;
568 Block
*nextFreePrev
= nextFree
->GetPreviousBlock();
569 Block
*movedBlock
= fFirstFree
;
570 memmove(movedBlock
, nextBlock
, chunkSize
);
571 movedBlock
->SetPreviousBlock(prevBlock
);
572 // init the first free block
573 Block
*movedNextFreePrev
= (Block
*)((char*)nextFreePrev
- size
);
574 fFirstFree
= _MakeFreeBlock(movedBlock
, chunkSize
,
575 movedNextFreePrev
, size
, true, NULL
, nextFree
);
576 nextFree
->SetPreviousFreeBlock(fFirstFree
);
577 // fix the references of the moved blocks
578 for (Block
*block
= movedBlock
;
580 block
= block
->GetNextBlock()) {
581 block
->FixReference();
584 // uncoalesced adjoining free block: That should never happen,
585 // since we always coalesce as early as possible.
586 INFORM(("Warning: Found uncoalesced adjoining free blocks!\n"));
588 // coalesce the first two blocks
590 _CoalesceWithNext(fFirstFree
);
594 //PRINT(("BlockAllocator::Area::_Defragment() done\n"));