xref: /haiku/src/add-ons/kernel/file_systems/bfs/BlockAllocator.cpp (revision 9a6a20d4689307142a7ed26a1437ba47e244e73f)
1 /*
2  * Copyright 2001-2020, Axel Dörfler, axeld@pinc-software.de.
3  * This file may be used under the terms of the MIT License.
4  */
5 
6 
7 //! Block bitmap handling and allocation policies
8 
9 
10 #include "BlockAllocator.h"
11 
12 #include "Debug.h"
13 #include "Inode.h"
14 #include "Volume.h"
15 
16 
17 // Things the BlockAllocator should do:
18 
19 // - find a range of blocks of a certain size nearby a specific position
20 // - allocating an unsharp range of blocks for pre-allocation
21 // - free blocks
22 // - know how to deal with each allocation, special handling for directories,
23 //   files, symlinks, etc. (type sensitive allocation policies)
24 
25 // What makes the code complicated is the fact that we are not just reading
26 // in the whole bitmap and operate on that in memory - e.g. a 13 GB partition
27 // with a block size of 2048 bytes already has a 800kB bitmap, and the size
28 // of partitions will grow even more - so that's not an option.
29 // Instead we are reading in every block when it's used - since an allocation
30 // group can span several blocks in the block bitmap, the AllocationBlock
31 // class is there to make handling those easier.
32 
33 // The current implementation is only slightly optimized and could probably
34 // be improved a lot. Furthermore, the allocation policies used here should
35 // have some real world tests.
36 
37 #if BFS_TRACING && !defined(FS_SHELL)
38 namespace BFSBlockTracing {
39 
40 
41 class Allocate : public AbstractTraceEntry {
42 public:
43 	Allocate(block_run run)
44 		:
45 		fRun(run)
46 	{
47 		Initialized();
48 	}
49 
50 	virtual void AddDump(TraceOutput& out)
51 	{
52 		out.Print("bfs:alloc %" B_PRId32 ".%" B_PRIu16 ".%" B_PRIu16,
53 			fRun.AllocationGroup(), fRun.Start(), fRun.Length());
54 	}
55 
56 	const block_run& Run() const { return fRun; }
57 
58 private:
59 			block_run			fRun;
60 };
61 
62 
63 class Free : public AbstractTraceEntry {
64 public:
65 	Free(block_run run)
66 		:
67 		fRun(run)
68 	{
69 		Initialized();
70 	}
71 
72 	virtual void AddDump(TraceOutput& out)
73 	{
74 		out.Print("bfs:free %" B_PRId32 ".%" B_PRIu16 ".%" B_PRIu16,
75 			fRun.AllocationGroup(), fRun.Start(), fRun.Length());
76 	}
77 
78 	const block_run& Run() const { return fRun; }
79 
80 private:
81 			block_run			fRun;
82 };
83 
84 
85 static uint32
86 checksum(const uint8* data, size_t size)
87 {
88 	const uint32* data4 = (const uint32*)data;
89 	uint32 sum = 0;
90 	while (size >= 4) {
91 		sum += *data4;
92 		data4++;
93 		size -= 4;
94 	}
95 	return sum;
96 }
97 
98 
99 class Block : public AbstractTraceEntry {
100 public:
101 	Block(const char* label, off_t blockNumber, const uint8* data,
102 			size_t size, uint32 start = 0, uint32 length = 0)
103 		:
104 		fBlock(blockNumber),
105 		fData(data),
106 		fStart(start),
107 		fLength(length),
108 		fLabel(label)
109 	{
110 		fSum = checksum(data, size);
111 		Initialized();
112 	}
113 
114 	virtual void AddDump(TraceOutput& out)
115 	{
116 		out.Print("bfs:%s: block %lld (%p), sum %lu, s/l %lu/%lu", fLabel,
117 			fBlock, fData, fSum, fStart, fLength);
118 	}
119 
120 private:
121 			off_t				fBlock;
122 			const uint8*		fData;
123 			uint32				fStart;
124 			uint32				fLength;
125 			uint32				fSum;
126 			const char*			fLabel;
127 };
128 
129 
130 class BlockChange : public AbstractTraceEntry {
131 public:
132 	BlockChange(const char* label, int32 block, uint32 oldData, uint32 newData)
133 		:
134 		fBlock(block),
135 		fOldData(oldData),
136 		fNewData(newData),
137 		fLabel(label)
138 	{
139 		Initialized();
140 	}
141 
142 	virtual void AddDump(TraceOutput& out)
143 	{
144 		out.Print("bfs:%s: block %ld, %08lx -> %08lx", fLabel,
145 			fBlock, fOldData, fNewData);
146 	}
147 
148 private:
149 			int32				fBlock;
150 			uint32				fOldData;
151 			uint32				fNewData;
152 			const char*			fLabel;
153 };
154 
155 
156 }	// namespace BFSBlockTracing
157 
158 #	define T(x) new(std::nothrow) BFSBlockTracing::x;
159 #else
160 #	define T(x) ;
161 #endif
162 
163 #ifdef DEBUG_ALLOCATION_GROUPS
164 #	define CHECK_ALLOCATION_GROUP(group) _CheckGroup(group)
165 #else
166 #	define CHECK_ALLOCATION_GROUP(group) ;
167 #endif
168 
169 
170 class AllocationBlock : public CachedBlock {
171 public:
172 	AllocationBlock(Volume* volume);
173 
174 	inline void Allocate(uint16 start, uint16 numBlocks);
175 	inline void Free(uint16 start, uint16 numBlocks);
176 	inline bool IsUsed(uint16 block);
177 
178 	status_t SetTo(AllocationGroup& group, uint16 block);
179 	status_t SetToWritable(Transaction& transaction, AllocationGroup& group,
180 		uint16 block);
181 
182 	uint32 NumBlockBits() const { return fNumBits; }
183 	uint32& Chunk(int32 index) { return ((uint32*)fBlock)[index]; }
184 	uint8* Block() const { return (uint8*)fBlock; }
185 
186 private:
187 	uint32	fNumBits;
188 #ifdef DEBUG
189 	bool	fWritable;
190 #endif
191 };
192 
193 
194 class AllocationGroup {
195 public:
196 	AllocationGroup();
197 
198 	void AddFreeRange(int32 start, int32 blocks);
199 	bool IsFull() const { return fFreeBits == 0; }
200 
201 	status_t Allocate(Transaction& transaction, uint16 start, int32 length);
202 	status_t Free(Transaction& transaction, uint16 start, int32 length);
203 
204 	uint32 NumBits() const { return fNumBits; }
205 	uint32 NumBitmapBlocks() const { return fNumBitmapBlocks; }
206 	int32 Start() const { return fStart; }
207 
208 private:
209 	friend class BlockAllocator;
210 
211 	uint32	fNumBits;
212 	uint32	fNumBitmapBlocks;
213 	int32	fStart;
214 	int32	fFirstFree;
215 	int32	fFreeBits;
216 
217 	int32	fLargestStart;
218 	int32	fLargestLength;
219 	bool	fLargestValid;
220 };
221 
222 
223 AllocationBlock::AllocationBlock(Volume* volume)
224 	: CachedBlock(volume)
225 {
226 }
227 
228 
229 status_t
230 AllocationBlock::SetTo(AllocationGroup& group, uint16 block)
231 {
232 	// 8 blocks per byte
233 	fNumBits = fVolume->BlockSize() << 3;
234 	// the last group may have less bits than the others
235 	if ((block + 1) * fNumBits > group.NumBits())
236 		fNumBits = group.NumBits() % fNumBits;
237 
238 #ifdef DEBUG
239 	fWritable = false;
240 #endif
241 	return CachedBlock::SetTo(group.Start() + block);
242 }
243 
244 
245 status_t
246 AllocationBlock::SetToWritable(Transaction& transaction, AllocationGroup& group,
247 	uint16 block)
248 {
249 	// 8 blocks per byte
250 	fNumBits = fVolume->BlockSize() << 3;
251 	// the last group may have less bits in the last block
252 	if ((block + 1) * fNumBits > group.NumBits())
253 		fNumBits = group.NumBits() % fNumBits;
254 
255 #ifdef DEBUG
256 	fWritable = true;
257 #endif
258 	return CachedBlock::SetToWritable(transaction, group.Start() + block);
259 }
260 
261 
262 bool
263 AllocationBlock::IsUsed(uint16 block)
264 {
265 	if (block > fNumBits)
266 		return true;
267 
268 	// the block bitmap is accessed in 32-bit chunks
269 	return Chunk(block >> 5) & HOST_ENDIAN_TO_BFS_INT32(1UL << (block % 32));
270 }
271 
272 
273 void
274 AllocationBlock::Allocate(uint16 start, uint16 numBlocks)
275 {
276 	ASSERT(start < fNumBits);
277 	ASSERT(uint32(start + numBlocks) <= fNumBits);
278 #ifdef DEBUG
279 	ASSERT(fWritable);
280 #endif
281 
282 	T(Block("b-alloc-in", fBlockNumber, fBlock, fVolume->BlockSize(),
283 		start, numBlocks));
284 
285 	int32 block = start >> 5;
286 
287 	while (numBlocks > 0) {
288 		uint32 mask = 0;
289 		for (int32 i = start % 32; i < 32 && numBlocks; i++, numBlocks--)
290 			mask |= 1UL << i;
291 
292 		T(BlockChange("b-alloc", block, Chunk(block),
293 			Block(block) | HOST_ENDIAN_TO_BFS_INT32(mask)));
294 
295 #if KDEBUG
296 		// check for already set blocks
297 		if (HOST_ENDIAN_TO_BFS_INT32(mask) & ((uint32*)fBlock)[block]) {
298 			FATAL(("AllocationBlock::Allocate(): some blocks are already "
299 				"allocated, start = %u, numBlocks = %u\n", start, numBlocks));
300 			panic("blocks already set!");
301 		}
302 #endif
303 
304 		Chunk(block++) |= HOST_ENDIAN_TO_BFS_INT32(mask);
305 		start = 0;
306 	}
307 	T(Block("b-alloc-out", fBlockNumber, fBlock, fVolume->BlockSize(),
308 		start, numBlocks));
309 }
310 
311 
312 void
313 AllocationBlock::Free(uint16 start, uint16 numBlocks)
314 {
315 	ASSERT(start < fNumBits);
316 	ASSERT(uint32(start + numBlocks) <= fNumBits);
317 #ifdef DEBUG
318 	ASSERT(fWritable);
319 #endif
320 
321 	int32 block = start >> 5;
322 
323 	while (numBlocks > 0) {
324 		uint32 mask = 0;
325 		for (int32 i = start % 32; i < 32 && numBlocks; i++, numBlocks--)
326 			mask |= 1UL << (i % 32);
327 
328 		T(BlockChange("b-free", block, Chunk(block),
329 			Block(block) & HOST_ENDIAN_TO_BFS_INT32(~mask)));
330 
331 		Chunk(block++) &= HOST_ENDIAN_TO_BFS_INT32(~mask);
332 		start = 0;
333 	}
334 }
335 
336 
337 //	#pragma mark -
338 
339 
340 /*!	The allocation groups are created and initialized in
341 	BlockAllocator::Initialize() and BlockAllocator::InitializeAndClearBitmap()
342 	respectively.
343 */
344 AllocationGroup::AllocationGroup()
345 	:
346 	fFirstFree(-1),
347 	fFreeBits(0),
348 	fLargestValid(false)
349 {
350 }
351 
352 
353 void
354 AllocationGroup::AddFreeRange(int32 start, int32 blocks)
355 {
356 	//D(if (blocks > 512)
357 	//	PRINT(("range of %ld blocks starting at %ld\n",blocks,start)));
358 
359 	if (fFirstFree == -1)
360 		fFirstFree = start;
361 
362 	if (!fLargestValid || fLargestLength < blocks) {
363 		fLargestStart = start;
364 		fLargestLength = blocks;
365 		fLargestValid = true;
366 	}
367 
368 	fFreeBits += blocks;
369 }
370 
371 
372 /*!	Allocates the specified run in the allocation group.
373 	Doesn't check if the run is valid or already allocated partially, nor
374 	does it maintain the free ranges hints or the volume's used blocks count.
375 	It only does the low-level work of allocating some bits in the block bitmap.
376 	Assumes that the block bitmap lock is hold.
377 */
378 status_t
379 AllocationGroup::Allocate(Transaction& transaction, uint16 start, int32 length)
380 {
381 	ASSERT(start + length <= (int32)fNumBits);
382 
383 	// Update the allocation group info
384 	// TODO: this info will be incorrect if something goes wrong later
385 	// Note, the fFirstFree block doesn't have to be really free
386 	if (start == fFirstFree)
387 		fFirstFree = start + length;
388 	fFreeBits -= length;
389 
390 	if (fLargestValid) {
391 		bool cut = false;
392 		if (fLargestStart == start) {
393 			// cut from start
394 			fLargestStart += length;
395 			fLargestLength -= length;
396 			cut = true;
397 		} else if (start > fLargestStart
398 			&& start < fLargestStart + fLargestLength) {
399 			// cut from end
400 			fLargestLength = start - fLargestStart;
401 			cut = true;
402 		}
403 		if (cut && (fLargestLength < fLargestStart
404 				|| fLargestLength
405 						< (int32)fNumBits - (fLargestStart + fLargestLength))) {
406 			// might not be the largest block anymore
407 			fLargestValid = false;
408 		}
409 	}
410 
411 	Volume* volume = transaction.GetVolume();
412 
413 	// calculate block in the block bitmap and position within
414 	uint32 bitsPerBlock = volume->BlockSize() << 3;
415 	uint32 block = start / bitsPerBlock;
416 	start = start % bitsPerBlock;
417 
418 	AllocationBlock cached(volume);
419 
420 	while (length > 0) {
421 		if (cached.SetToWritable(transaction, *this, block) < B_OK) {
422 			fLargestValid = false;
423 			RETURN_ERROR(B_IO_ERROR);
424 		}
425 
426 		uint32 numBlocks = length;
427 		if (start + numBlocks > cached.NumBlockBits())
428 			numBlocks = cached.NumBlockBits() - start;
429 
430 		cached.Allocate(start, numBlocks);
431 
432 		length -= numBlocks;
433 		start = 0;
434 		block++;
435 	}
436 
437 	return B_OK;
438 }
439 
440 
441 /*!	Frees the specified run in the allocation group.
442 	Doesn't check if the run is valid or was not completely allocated, nor
443 	does it maintain the free ranges hints or the volume's used blocks count.
444 	It only does the low-level work of freeing some bits in the block bitmap.
445 	Assumes that the block bitmap lock is hold.
446 */
447 status_t
448 AllocationGroup::Free(Transaction& transaction, uint16 start, int32 length)
449 {
450 	ASSERT(start + length <= (int32)fNumBits);
451 
452 	// Update the allocation group info
453 	// TODO: this info will be incorrect if something goes wrong later
454 	if (fFirstFree > start)
455 		fFirstFree = start;
456 	fFreeBits += length;
457 
458 	// The range to be freed cannot be part of the valid largest range
459 	ASSERT(!fLargestValid || start + length <= fLargestStart
460 		|| start > fLargestStart);
461 
462 	if (fLargestValid
463 		&& (start + length == fLargestStart
464 			|| fLargestStart + fLargestLength == start
465 			|| (start < fLargestStart && fLargestStart > fLargestLength)
466 			|| (start > fLargestStart
467 				&& (int32)fNumBits - (fLargestStart + fLargestLength)
468 						> fLargestLength))) {
469 		fLargestValid = false;
470 	}
471 
472 	Volume* volume = transaction.GetVolume();
473 
474 	// calculate block in the block bitmap and position within
475 	uint32 bitsPerBlock = volume->BlockSize() << 3;
476 	uint32 block = start / bitsPerBlock;
477 	start = start % bitsPerBlock;
478 
479 	AllocationBlock cached(volume);
480 
481 	while (length > 0) {
482 		if (cached.SetToWritable(transaction, *this, block) < B_OK)
483 			RETURN_ERROR(B_IO_ERROR);
484 
485 		T(Block("free-1", block, cached.Block(), volume->BlockSize()));
486 		uint16 freeLength = length;
487 		if (uint32(start + length) > cached.NumBlockBits())
488 			freeLength = cached.NumBlockBits() - start;
489 
490 		cached.Free(start, freeLength);
491 
492 		length -= freeLength;
493 		start = 0;
494 		T(Block("free-2", block, cached.Block(), volume->BlockSize()));
495 		block++;
496 	}
497 	return B_OK;
498 }
499 
500 
501 //	#pragma mark -
502 
503 
504 BlockAllocator::BlockAllocator(Volume* volume)
505 	:
506 	fVolume(volume),
507 	fGroups(NULL)
508 	//fCheckBitmap(NULL),
509 	//fCheckCookie(NULL)
510 {
511 	recursive_lock_init(&fLock, "bfs allocator");
512 }
513 
514 
515 BlockAllocator::~BlockAllocator()
516 {
517 	recursive_lock_destroy(&fLock);
518 	delete[] fGroups;
519 }
520 
521 
522 status_t
523 BlockAllocator::Initialize(bool full)
524 {
525 	fNumGroups = fVolume->AllocationGroups();
526 	fBlocksPerGroup = fVolume->SuperBlock().BlocksPerAllocationGroup();
527 	fNumBitmapBlocks = fVolume->NumBitmapBlocks();
528 
529 	fGroups = new(std::nothrow) AllocationGroup[fNumGroups];
530 	if (fGroups == NULL)
531 		return B_NO_MEMORY;
532 
533 	if (!full)
534 		return B_OK;
535 
536 	recursive_lock_lock(&fLock);
537 		// the lock will be released by the _Initialize() method
538 
539 	thread_id id = spawn_kernel_thread((thread_func)BlockAllocator::_Initialize,
540 		"bfs block allocator", B_LOW_PRIORITY, this);
541 	if (id < B_OK)
542 		return _Initialize(this);
543 
544 	recursive_lock_transfer_lock(&fLock, id);
545 
546 	return resume_thread(id);
547 }
548 
549 
550 status_t
551 BlockAllocator::InitializeAndClearBitmap(Transaction& transaction)
552 {
553 	status_t status = Initialize(false);
554 	if (status != B_OK)
555 		return status;
556 
557 	uint32 numBits = 8 * fBlocksPerGroup * fVolume->BlockSize();
558 	uint32 blockShift = fVolume->BlockShift();
559 
560 	uint32* buffer = (uint32*)malloc(numBits >> 3);
561 	if (buffer == NULL)
562 		RETURN_ERROR(B_NO_MEMORY);
563 
564 	memset(buffer, 0, numBits >> 3);
565 
566 	off_t offset = 1;
567 		// the bitmap starts directly after the superblock
568 
569 	// initialize the AllocationGroup objects and clear the on-disk bitmap
570 
571 	for (int32 i = 0; i < fNumGroups; i++) {
572 		if (write_pos(fVolume->Device(), offset << blockShift, buffer,
573 				fBlocksPerGroup << blockShift) < B_OK) {
574 			free(buffer);
575 			return B_ERROR;
576 		}
577 
578 		// the last allocation group may contain less blocks than the others
579 		if (i == fNumGroups - 1) {
580 			fGroups[i].fNumBits = fVolume->NumBlocks() - i * numBits;
581 			fGroups[i].fNumBitmapBlocks = 1 + ((fGroups[i].NumBits() - 1)
582 				>> (blockShift + 3));
583 		} else {
584 			fGroups[i].fNumBits = numBits;
585 			fGroups[i].fNumBitmapBlocks = fBlocksPerGroup;
586 		}
587 		fGroups[i].fStart = offset;
588 		fGroups[i].fFirstFree = fGroups[i].fLargestStart = 0;
589 		fGroups[i].fFreeBits = fGroups[i].fLargestLength = fGroups[i].fNumBits;
590 		fGroups[i].fLargestValid = true;
591 
592 		offset += fBlocksPerGroup;
593 	}
594 	free(buffer);
595 
596 	// reserve the boot block, the log area, and the block bitmap itself
597 	uint32 reservedBlocks = fVolume->ToBlock(fVolume->Log()) + fVolume->Log().Length();
598 	uint32 blocksToReserve = reservedBlocks;
599 	for (int32 i = 0; i < fNumGroups; i++) {
600 		int32 reservedBlocksInGroup = min_c(blocksToReserve, numBits);
601 		if (fGroups[i].Allocate(transaction, 0, reservedBlocksInGroup) < B_OK) {
602 			FATAL(("could not allocate reserved space for block bitmap/log!\n"));
603 			return B_ERROR;
604 		}
605 		blocksToReserve -= reservedBlocksInGroup;
606 		if (blocksToReserve == 0)
607 			break;
608 	}
609 	fVolume->SuperBlock().used_blocks
610 		= HOST_ENDIAN_TO_BFS_INT64(reservedBlocks);
611 
612 	return B_OK;
613 }
614 
615 
616 status_t
617 BlockAllocator::_Initialize(BlockAllocator* allocator)
618 {
619 	// The lock must already be held at this point
620 	RecursiveLocker locker(allocator->fLock, true);
621 
622 	Volume* volume = allocator->fVolume;
623 	uint32 blocks = allocator->fBlocksPerGroup;
624 	uint32 blockShift = volume->BlockShift();
625 	off_t freeBlocks = 0;
626 
627 	uint32* buffer = (uint32*)malloc(blocks << blockShift);
628 	if (buffer == NULL)
629 		RETURN_ERROR(B_NO_MEMORY);
630 
631 	AllocationGroup* groups = allocator->fGroups;
632 	off_t offset = 1;
633 	uint32 bitsPerGroup = 8 * (blocks << blockShift);
634 	int32 numGroups = allocator->fNumGroups;
635 
636 	for (int32 i = 0; i < numGroups; i++) {
637 		if (read_pos(volume->Device(), offset << blockShift, buffer,
638 				blocks << blockShift) < B_OK)
639 			break;
640 
641 		// the last allocation group may contain less blocks than the others
642 		if (i == numGroups - 1) {
643 			groups[i].fNumBits = volume->NumBlocks() - i * bitsPerGroup;
644 			groups[i].fNumBitmapBlocks = 1 + ((groups[i].NumBits() - 1)
645 				>> (blockShift + 3));
646 		} else {
647 			groups[i].fNumBits = bitsPerGroup;
648 			groups[i].fNumBitmapBlocks = blocks;
649 		}
650 		groups[i].fStart = offset;
651 
652 		// finds all free ranges in this allocation group
653 		int32 start = -1, range = 0;
654 		int32 numBits = groups[i].fNumBits, bit = 0;
655 		int32 count = (numBits + 31) / 32;
656 
657 		for (int32 k = 0; k < count; k++) {
658 			for (int32 j = 0; j < 32 && bit < numBits; j++, bit++) {
659 				if (buffer[k] & (1UL << j)) {
660 					// block is in use
661 					if (range > 0) {
662 						groups[i].AddFreeRange(start, range);
663 						range = 0;
664 					}
665 				} else if (range++ == 0) {
666 					// block is free, start new free range
667 					start = bit;
668 				}
669 			}
670 		}
671 		if (range)
672 			groups[i].AddFreeRange(start, range);
673 
674 		freeBlocks += groups[i].fFreeBits;
675 
676 		offset += blocks;
677 	}
678 	free(buffer);
679 
680 	// check if block bitmap and log area are reserved
681 	uint32 reservedBlocks = volume->ToBlock(volume->Log()) + volume->Log().Length();
682 
683 	if (allocator->CheckBlocks(0, reservedBlocks) != B_OK) {
684 		if (volume->IsReadOnly()) {
685 			FATAL(("Space for block bitmap or log area is not reserved "
686 				"(volume is mounted read-only)!\n"));
687 		} else {
688 			Transaction transaction(volume, 0);
689 			if (groups[0].Allocate(transaction, 0, reservedBlocks) != B_OK) {
690 				FATAL(("Could not allocate reserved space for block "
691 					"bitmap/log!\n"));
692 				volume->Panic();
693 			} else {
694 				transaction.Done();
695 				FATAL(("Space for block bitmap or log area was not "
696 					"reserved!\n"));
697 			}
698 		}
699 	}
700 
701 	off_t usedBlocks = volume->NumBlocks() - freeBlocks;
702 	if (volume->UsedBlocks() != usedBlocks) {
703 		// If the disk in a dirty state at mount time, it's
704 		// normal that the values don't match
705 		INFORM(("volume reports %" B_PRIdOFF " used blocks, correct is %"
706 			B_PRIdOFF "\n", volume->UsedBlocks(), usedBlocks));
707 		volume->SuperBlock().used_blocks = HOST_ENDIAN_TO_BFS_INT64(usedBlocks);
708 	}
709 
710 	return B_OK;
711 }
712 
713 
714 void
715 BlockAllocator::Uninitialize()
716 {
717 	// We only have to make sure that the initializer thread isn't running
718 	// anymore.
719 	recursive_lock_lock(&fLock);
720 }
721 
722 
723 /*!	Tries to allocate between \a minimum, and \a maximum blocks starting
724 	at group \a groupIndex with offset \a start. The resulting allocation
725 	is put into \a run.
726 
727 	The number of allocated blocks is always a multiple of \a minimum which
728 	has to be a power of two value.
729 */
730 status_t
731 BlockAllocator::AllocateBlocks(Transaction& transaction, int32 groupIndex,
732 	uint16 start, uint16 maximum, uint16 minimum, block_run& run)
733 {
734 	if (maximum == 0)
735 		return B_BAD_VALUE;
736 
737 	FUNCTION_START(("group = %" B_PRId32 ", start = %" B_PRIu16
738 		", maximum = %" B_PRIu16 ", minimum = %" B_PRIu16 "\n",
739 		groupIndex, start, maximum, minimum));
740 
741 	AllocationBlock cached(fVolume);
742 	RecursiveLocker lock(fLock);
743 
744 	uint32 bitsPerFullBlock = fVolume->BlockSize() << 3;
745 
746 	// Find the block_run that can fulfill the request best
747 	int32 bestGroup = -1;
748 	int32 bestStart = -1;
749 	int32 bestLength = -1;
750 
751 	for (int32 i = 0; i < fNumGroups + 1; i++, groupIndex++, start = 0) {
752 		groupIndex = groupIndex % fNumGroups;
753 		AllocationGroup& group = fGroups[groupIndex];
754 
755 		CHECK_ALLOCATION_GROUP(groupIndex);
756 
757 		if (start >= group.NumBits() || group.IsFull())
758 			continue;
759 
760 		// The wanted maximum is smaller than the largest free block in the
761 		// group or already smaller than the minimum
762 
763 		if (start < group.fFirstFree)
764 			start = group.fFirstFree;
765 
766 		if (group.fLargestValid) {
767 			if (group.fLargestLength < bestLength)
768 				continue;
769 
770 			if (group.fLargestStart >= start) {
771 				if (group.fLargestLength >= bestLength) {
772 					bestGroup = groupIndex;
773 					bestStart = group.fLargestStart;
774 					bestLength = group.fLargestLength;
775 
776 					if (bestLength >= maximum)
777 						break;
778 				}
779 
780 				// We know everything about this group we have to, let's skip
781 				// to the next
782 				continue;
783 			}
784 		}
785 
786 		// There may be more than one block per allocation group - and
787 		// we iterate through it to find a place for the allocation.
788 		// (one allocation can't exceed one allocation group)
789 
790 		uint32 block = start / (fVolume->BlockSize() << 3);
791 		int32 currentStart = 0, currentLength = 0;
792 		int32 groupLargestStart = -1;
793 		int32 groupLargestLength = -1;
794 		int32 currentBit = start;
795 		bool canFindGroupLargest = start == 0;
796 
797 		for (; block < group.NumBitmapBlocks(); block++) {
798 			if (cached.SetTo(group, block) < B_OK)
799 				RETURN_ERROR(B_ERROR);
800 
801 			T(Block("alloc-in", group.Start() + block, cached.Block(),
802 				fVolume->BlockSize(), groupIndex, currentStart));
803 
804 			// find a block large enough to hold the allocation
805 			for (uint32 bit = start % bitsPerFullBlock;
806 					bit < cached.NumBlockBits(); bit++) {
807 				if (!cached.IsUsed(bit)) {
808 					if (currentLength == 0) {
809 						// start new range
810 						currentStart = currentBit;
811 					}
812 
813 					// have we found a range large enough to hold numBlocks?
814 					if (++currentLength >= maximum) {
815 						bestGroup = groupIndex;
816 						bestStart = currentStart;
817 						bestLength = currentLength;
818 						break;
819 					}
820 				} else {
821 					if (currentLength) {
822 						// end of a range
823 						if (currentLength > bestLength) {
824 							bestGroup = groupIndex;
825 							bestStart = currentStart;
826 							bestLength = currentLength;
827 						}
828 						if (currentLength > groupLargestLength) {
829 							groupLargestStart = currentStart;
830 							groupLargestLength = currentLength;
831 						}
832 						currentLength = 0;
833 					}
834 					if (((int32)group.NumBits() - currentBit)
835 							<= groupLargestLength) {
836 						// We can't find a bigger block in this group anymore,
837 						// let's skip the rest.
838 						block = group.NumBitmapBlocks();
839 						break;
840 					}
841 				}
842 				currentBit++;
843 			}
844 
845 			T(Block("alloc-out", block, cached.Block(),
846 				fVolume->BlockSize(), groupIndex, currentStart));
847 
848 			if (bestLength >= maximum) {
849 				canFindGroupLargest = false;
850 				break;
851 			}
852 
853 			// start from the beginning of the next block
854 			start = 0;
855 		}
856 
857 		if (currentBit == (int32)group.NumBits()) {
858 			if (currentLength > bestLength) {
859 				bestGroup = groupIndex;
860 				bestStart = currentStart;
861 				bestLength = currentLength;
862 			}
863 			if (canFindGroupLargest && currentLength > groupLargestLength) {
864 				groupLargestStart = currentStart;
865 				groupLargestLength = currentLength;
866 			}
867 		}
868 
869 		if (canFindGroupLargest && !group.fLargestValid
870 			&& groupLargestLength >= 0) {
871 			group.fLargestStart = groupLargestStart;
872 			group.fLargestLength = groupLargestLength;
873 			group.fLargestValid = true;
874 		}
875 
876 		if (bestLength >= maximum)
877 			break;
878 	}
879 
880 	// If we found a suitable range, mark the blocks as in use, and
881 	// write the updated block bitmap back to disk
882 	if (bestLength < minimum)
883 		return B_DEVICE_FULL;
884 
885 	if (bestLength > maximum)
886 		bestLength = maximum;
887 	else if (minimum > 1) {
888 		// make sure bestLength is a multiple of minimum
889 		bestLength = round_down(bestLength, minimum);
890 	}
891 
892 	if (fGroups[bestGroup].Allocate(transaction, bestStart, bestLength) != B_OK)
893 		RETURN_ERROR(B_IO_ERROR);
894 
895 	CHECK_ALLOCATION_GROUP(bestGroup);
896 
897 	run.allocation_group = HOST_ENDIAN_TO_BFS_INT32(bestGroup);
898 	run.start = HOST_ENDIAN_TO_BFS_INT16(bestStart);
899 	run.length = HOST_ENDIAN_TO_BFS_INT16(bestLength);
900 
901 	fVolume->SuperBlock().used_blocks
902 		= HOST_ENDIAN_TO_BFS_INT64(fVolume->UsedBlocks() + bestLength);
903 		// We are not writing back the disk's superblock - it's
904 		// either done by the journaling code, or when the disk
905 		// is unmounted.
906 		// If the value is not correct at mount time, it will be
907 		// fixed anyway.
908 
909 	// We need to flush any remaining blocks in the new allocation to make sure
910 	// they won't interfere with the file cache.
911 	block_cache_discard(fVolume->BlockCache(), fVolume->ToBlock(run),
912 		run.Length());
913 
914 	T(Allocate(run));
915 	return B_OK;
916 }
917 
918 
919 status_t
920 BlockAllocator::AllocateForInode(Transaction& transaction,
921 	const block_run* parent, mode_t type, block_run& run)
922 {
923 	// Apply some allocation policies here (AllocateBlocks() will break them
924 	// if necessary) - we will start with those described in Dominic Giampaolo's
925 	// "Practical File System Design", and see how good they work
926 
927 	// Files are going in the same allocation group as its parent,
928 	// sub-directories will be inserted 8 allocation groups after
929 	// the one of the parent
930 	uint16 group = parent->AllocationGroup();
931 	if ((type & (S_DIRECTORY | S_INDEX_DIR | S_ATTR_DIR)) == S_DIRECTORY)
932 		group += 8;
933 
934 	return AllocateBlocks(transaction, group, 0, 1, 1, run);
935 }
936 
937 
938 status_t
939 BlockAllocator::Allocate(Transaction& transaction, Inode* inode,
940 	off_t numBlocks, block_run& run, uint16 minimum)
941 {
942 	if (numBlocks <= 0)
943 		return B_ERROR;
944 
945 	// one block_run can't hold more data than there is in one allocation group
946 	if (numBlocks > fGroups[0].NumBits())
947 		numBlocks = fGroups[0].NumBits();
948 
949 	// since block_run.length is uint16, the largest number of blocks that
950 	// can be covered by a block_run is 65535
951 	// TODO: if we drop compatibility, couldn't we do this any better?
952 	// There are basically two possibilities:
953 	// a) since a length of zero doesn't have any sense, take that for 65536 -
954 	//    but that could cause many problems (bugs) in other areas
955 	// b) reduce the maximum amount of blocks per block_run, so that the
956 	//    remaining number of free blocks can be used in a useful manner
957 	//    (like 4 blocks) - but that would also reduce the maximum file size
958 	// c) have BlockRun::Length() return (length + 1).
959 	if (numBlocks > MAX_BLOCK_RUN_LENGTH)
960 		numBlocks = MAX_BLOCK_RUN_LENGTH;
961 
962 	// Apply some allocation policies here (AllocateBlocks() will break them
963 	// if necessary)
964 	uint16 group = inode->BlockRun().AllocationGroup();
965 	uint16 start = 0;
966 
967 	// Are there already allocated blocks? (then just try to allocate near the
968 	// last one)
969 	if (inode->Size() > 0) {
970 		const data_stream& data = inode->Node().data;
971 		// TODO: we currently don't care for when the data stream
972 		// is already grown into the indirect ranges
973 		if (data.max_double_indirect_range == 0
974 			&& data.max_indirect_range == 0) {
975 			// Since size > 0, there must be a valid block run in this stream
976 			int32 last = 0;
977 			for (; last < NUM_DIRECT_BLOCKS - 1; last++)
978 				if (data.direct[last + 1].IsZero())
979 					break;
980 
981 			group = data.direct[last].AllocationGroup();
982 			start = data.direct[last].Start() + data.direct[last].Length();
983 		}
984 	} else if (inode->IsContainer() || inode->IsSymLink()) {
985 		// directory and symbolic link data will go in the same allocation
986 		// group as the inode is in but after the inode data
987 		start = inode->BlockRun().Start();
988 	} else {
989 		// file data will start in the next allocation group
990 		group = inode->BlockRun().AllocationGroup() + 1;
991 	}
992 
993 	return AllocateBlocks(transaction, group, start, numBlocks, minimum, run);
994 }
995 
996 
997 status_t
998 BlockAllocator::Free(Transaction& transaction, block_run run)
999 {
1000 	RecursiveLocker lock(fLock);
1001 
1002 	int32 group = run.AllocationGroup();
1003 	uint16 start = run.Start();
1004 	uint16 length = run.Length();
1005 
1006 	FUNCTION_START(("group = %" B_PRId32 ", start = %" B_PRIu16
1007 		", length = %" B_PRIu16 "\n", group, start, length))
1008 	T(Free(run));
1009 
1010 	// doesn't use Volume::IsValidBlockRun() here because it can check better
1011 	// against the group size (the last group may have a different length)
1012 	if (group < 0 || group >= fNumGroups
1013 		|| start > fGroups[group].NumBits()
1014 		|| uint32(start + length) > fGroups[group].NumBits()
1015 		|| length == 0) {
1016 		FATAL(("tried to free an invalid block_run"
1017 			" (%" B_PRId32 ", %" B_PRIu16 ", %" B_PRIu16")\n",
1018 			group, start, length));
1019 		DEBUGGER(("tried to free invalid block_run"));
1020 		return B_BAD_VALUE;
1021 	}
1022 	// check if someone tries to free reserved areas at the beginning of the
1023 	// drive
1024 	if (group < fVolume->Log().AllocationGroup()
1025 		|| (group == fVolume->Log().AllocationGroup()
1026 			&& start < uint32(fVolume->Log().Start()) + fVolume->Log().Length())) {
1027 		FATAL(("tried to free a reserved block_run"
1028 			" (%" B_PRId32 ", %" B_PRIu16 ", %" B_PRIu16")\n",
1029 			group, start, length));
1030 		DEBUGGER(("tried to free reserved block"));
1031 		return B_BAD_VALUE;
1032 	}
1033 #ifdef DEBUG
1034 	if (CheckBlockRun(run) != B_OK)
1035 		return B_BAD_DATA;
1036 #endif
1037 
1038 	CHECK_ALLOCATION_GROUP(group);
1039 
1040 	if (fGroups[group].Free(transaction, start, length) != B_OK)
1041 		RETURN_ERROR(B_IO_ERROR);
1042 
1043 	CHECK_ALLOCATION_GROUP(group);
1044 
1045 #ifdef DEBUG
1046 	if (CheckBlockRun(run, NULL, false) != B_OK) {
1047 		DEBUGGER(("CheckBlockRun() reports allocated blocks (which were just "
1048 			"freed)\n"));
1049 	}
1050 #endif
1051 
1052 	fVolume->SuperBlock().used_blocks =
1053 		HOST_ENDIAN_TO_BFS_INT64(fVolume->UsedBlocks() - run.Length());
1054 	return B_OK;
1055 }
1056 
1057 
1058 #ifdef DEBUG_FRAGMENTER
1059 void
1060 BlockAllocator::Fragment()
1061 {
1062 	AllocationBlock cached(fVolume);
1063 	RecursiveLocker lock(fLock);
1064 
1065 	// only leave 4 block holes
1066 	static const uint32 kMask = 0x0f0f0f0f;
1067 	uint32 valuesPerBlock = fVolume->BlockSize() / 4;
1068 
1069 	for (int32 i = 0; i < fNumGroups; i++) {
1070 		AllocationGroup& group = fGroups[i];
1071 
1072 		for (uint32 block = 0; block < group.NumBlocks(); block++) {
1073 			Transaction transaction(fVolume, 0);
1074 
1075 			if (cached.SetToWritable(transaction, group, block) != B_OK)
1076 				return;
1077 
1078 			for (int32 index = 0; index < valuesPerBlock; index++) {
1079 				cached.Block(index) |= HOST_ENDIAN_TO_BFS_INT32(kMask);
1080 			}
1081 
1082 			transaction.Done();
1083 		}
1084 	}
1085 }
1086 #endif	// DEBUG_FRAGMENTER
1087 
1088 
1089 #ifdef DEBUG_ALLOCATION_GROUPS
1090 void
1091 BlockAllocator::_CheckGroup(int32 groupIndex) const
1092 {
1093 	AllocationBlock cached(fVolume);
1094 	ASSERT_LOCKED_RECURSIVE(&fLock);
1095 
1096 	AllocationGroup& group = fGroups[groupIndex];
1097 
1098 	int32 currentStart = 0, currentLength = 0;
1099 	int32 firstFree = -1;
1100 	int32 largestStart = -1;
1101 	int32 largestLength = 0;
1102 	int32 currentBit = 0;
1103 
1104 	for (uint32 block = 0; block < group.NumBlocks(); block++) {
1105 		if (cached.SetTo(group, block) < B_OK) {
1106 			panic("setting group block %d failed\n", (int)block);
1107 			return;
1108 		}
1109 
1110 		for (uint32 bit = 0; bit < cached.NumBlockBits(); bit++) {
1111 			if (!cached.IsUsed(bit)) {
1112 				if (firstFree < 0) {
1113 					firstFree = currentBit;
1114 					if (!group.fLargestValid) {
1115 						if (firstFree >= 0 && firstFree < group.fFirstFree) {
1116 							// mostly harmless but noteworthy
1117 							dprintf("group %d first free too late: should be "
1118 								"%d, is %d\n", (int)groupIndex, (int)firstFree,
1119 								(int)group.fFirstFree);
1120 						}
1121 						return;
1122 					}
1123 				}
1124 
1125 				if (currentLength == 0) {
1126 					// start new range
1127 					currentStart = currentBit;
1128 				}
1129 				currentLength++;
1130 			} else if (currentLength) {
1131 				// end of a range
1132 				if (currentLength > largestLength) {
1133 					largestStart = currentStart;
1134 					largestLength = currentLength;
1135 				}
1136 				currentLength = 0;
1137 			}
1138 			currentBit++;
1139 		}
1140 	}
1141 
1142 	if (currentLength > largestLength) {
1143 		largestStart = currentStart;
1144 		largestLength = currentLength;
1145 	}
1146 
1147 	if (firstFree >= 0 && firstFree < group.fFirstFree) {
1148 		// mostly harmless but noteworthy
1149 		dprintf("group %d first free too late: should be %d, is %d\n",
1150 			(int)groupIndex, (int)firstFree, (int)group.fFirstFree);
1151 	}
1152 	if (group.fLargestValid
1153 		&& (largestStart != group.fLargestStart
1154 			|| largestLength != group.fLargestLength)) {
1155 		panic("bfs %p: group %d largest differs: %d.%d, checked %d.%d.\n",
1156 			fVolume, (int)groupIndex, (int)group.fLargestStart,
1157 			(int)group.fLargestLength, (int)largestStart, (int)largestLength);
1158 	}
1159 }
1160 #endif	// DEBUG_ALLOCATION_GROUPS
1161 
1162 
1163 status_t
1164 BlockAllocator::Trim(uint64 offset, uint64 size, uint64& trimmedSize)
1165 {
1166 	// TODO: Remove this check when offset and size handling is implemented
1167 	if (offset != 0
1168 		|| fVolume->NumBlocks() < 0
1169 		|| size < (uint64)fVolume->NumBlocks() * fVolume->BlockSize()) {
1170 		INFORM(("BFS Trim: Ranges smaller than the file system size"
1171 			" are not supported yet.\n"));
1172 		return B_UNSUPPORTED;
1173 	}
1174 
1175 	const uint32 kTrimRanges = 128;
1176 	fs_trim_data* trimData = (fs_trim_data*)malloc(sizeof(fs_trim_data)
1177 		+ 2 * sizeof(uint64) * (kTrimRanges - 1));
1178 	if (trimData == NULL)
1179 		return B_NO_MEMORY;
1180 
1181 	MemoryDeleter deleter(trimData);
1182 	RecursiveLocker locker(fLock);
1183 
1184 	// TODO: take given offset and size into account!
1185 	int32 lastGroup = fNumGroups - 1;
1186 	uint32 firstBlock = 0;
1187 	uint32 firstBit = 0;
1188 	uint64 currentBlock = 0;
1189 	uint32 blockShift = fVolume->BlockShift();
1190 
1191 	uint64 firstFree = 0;
1192 	uint64 freeLength = 0;
1193 
1194 	trimData->range_count = 0;
1195 	trimmedSize = 0;
1196 
1197 	AllocationBlock cached(fVolume);
1198 	for (int32 groupIndex = 0; groupIndex <= lastGroup; groupIndex++) {
1199 		AllocationGroup& group = fGroups[groupIndex];
1200 
1201 		for (uint32 block = firstBlock; block < group.NumBitmapBlocks(); block++) {
1202 			cached.SetTo(group, block);
1203 
1204 			for (uint32 i = firstBit; i < cached.NumBlockBits(); i++) {
1205 				if (cached.IsUsed(i)) {
1206 					// Block is in use
1207 					if (freeLength > 0) {
1208 						// Overflow is unlikely to happen, but check it anyway
1209 						if ((firstFree << blockShift) >> blockShift
1210 								!= firstFree
1211 							|| (freeLength << blockShift) >> blockShift
1212 								!= freeLength) {
1213 							FATAL(("BlockAllocator::Trim:"
1214 								" Overflow detected!\n"));
1215 							return B_ERROR;
1216 						}
1217 						status_t status = _TrimNext(*trimData, kTrimRanges,
1218 							firstFree << blockShift, freeLength << blockShift,
1219 							false, trimmedSize);
1220 						if (status != B_OK)
1221 							return status;
1222 
1223 						freeLength = 0;
1224 					}
1225 				} else if (freeLength++ == 0) {
1226 					// Block is free, start new free range
1227 					firstFree = currentBlock;
1228 				}
1229 
1230 				currentBlock++;
1231 			}
1232 		}
1233 
1234 		firstBlock = 0;
1235 		firstBit = 0;
1236 	}
1237 
1238 	return _TrimNext(*trimData, kTrimRanges, firstFree << blockShift,
1239 		freeLength << blockShift, true, trimmedSize);
1240 }
1241 
1242 
1243 //	#pragma mark -
1244 
1245 
1246 /*!	Checks whether or not the specified block range is allocated or not,
1247 	depending on the \a allocated argument.
1248 */
1249 status_t
1250 BlockAllocator::CheckBlocks(off_t start, off_t length, bool allocated,
1251 	off_t* firstError)
1252 {
1253 	if (start < 0 || start + length > fVolume->NumBlocks())
1254 		return B_BAD_VALUE;
1255 
1256 	off_t block = start;
1257 
1258 	int32 group = start >> fVolume->AllocationGroupShift();
1259 	uint32 bitmapBlock = start / (fVolume->BlockSize() << 3);
1260 	uint32 blockOffset = start % (fVolume->BlockSize() << 3);
1261 
1262 	uint32 groupBlock = bitmapBlock % fBlocksPerGroup;
1263 
1264 	AllocationBlock cached(fVolume);
1265 
1266 	while (groupBlock < fGroups[group].NumBitmapBlocks() && length > 0) {
1267 		if (cached.SetTo(fGroups[group], groupBlock) != B_OK)
1268 			RETURN_ERROR(B_IO_ERROR);
1269 
1270 		for (; blockOffset < cached.NumBlockBits() && length > 0;
1271 				blockOffset++, length--, block++) {
1272 			if (cached.IsUsed(blockOffset) != allocated) {
1273 				PRINT(("CheckBlocks: Erroneous block (group = %" B_PRId32
1274 					", groupBlock = %" B_PRIu32
1275 					", blockOffset = %" B_PRIu32 ")!\n",
1276 					group, groupBlock, blockOffset));
1277 
1278 				if (firstError)
1279 					*firstError = block;
1280 
1281 				return B_BAD_DATA;
1282 			}
1283 		}
1284 
1285 		blockOffset = 0;
1286 
1287 		if (++groupBlock >= fGroups[group].NumBitmapBlocks()) {
1288 			groupBlock = 0;
1289 			group++;
1290 		}
1291 	}
1292 
1293 	return B_OK;
1294 }
1295 
1296 
1297 bool
1298 BlockAllocator::IsValidBlockRun(block_run run, const char* type)
1299 {
1300 	if (run.AllocationGroup() < 0 || run.AllocationGroup() >= fNumGroups
1301 		|| run.Start() > fGroups[run.AllocationGroup()].fNumBits
1302 		|| uint32(run.Start() + run.Length())
1303 				> fGroups[run.AllocationGroup()].fNumBits
1304 		|| run.length == 0) {
1305 		PRINT(("%s: block_run(%" B_PRId32 ", %" B_PRIu16 ", %" B_PRIu16")"
1306 			" is invalid!\n", type, run.AllocationGroup(), run.Start(),
1307 			run.Length()));
1308 		return false;
1309 	}
1310 	return true;
1311 }
1312 
1313 
1314 status_t
1315 BlockAllocator::CheckBlockRun(block_run run, const char* type, bool allocated)
1316 {
1317 	if (!IsValidBlockRun(run, type))
1318 		return B_BAD_DATA;
1319 
1320 	status_t status = CheckBlocks(fVolume->ToBlock(run), run.Length(),
1321 		allocated);
1322 	if (status != B_OK) {
1323 		PRINT(("%s: block_run(%" B_PRId32 ", %" B_PRIu16 ", %" B_PRIu16")"
1324 			" is only partially allocated!\n", type, run.AllocationGroup(),
1325 			run.Start(), run.Length()));
1326 	}
1327 
1328 	return status;
1329 }
1330 
1331 
1332 bool
1333 BlockAllocator::_AddTrim(fs_trim_data& trimData, uint32 maxRanges,
1334 	uint64 offset, uint64 size)
1335 {
1336 	ASSERT(trimData.range_count < maxRanges);
1337 	if (size == 0)
1338 		return false;
1339 
1340 	trimData.ranges[trimData.range_count].offset = offset;
1341 	trimData.ranges[trimData.range_count].size = size;
1342 	trimData.range_count++;
1343 
1344 	return (trimData.range_count == maxRanges);
1345 }
1346 
1347 
1348 status_t
1349 BlockAllocator::_TrimNext(fs_trim_data& trimData, uint32 maxRanges,
1350 	uint64 offset, uint64 size, bool force, uint64& trimmedSize)
1351 {
1352 	PRINT(("_TrimNext(index %" B_PRIu32 ", offset %" B_PRIu64 ", size %"
1353 		B_PRIu64 ")\n", trimData.range_count, offset, size));
1354 
1355 	const bool rangesFilled = _AddTrim(trimData, maxRanges, offset, size);
1356 
1357 	if (rangesFilled || force) {
1358 		// Trim now
1359 		trimData.trimmed_size = 0;
1360 #ifdef DEBUG_TRIM
1361 		dprintf("TRIM: BFS: free ranges (bytes):\n");
1362 		for (uint32 i = 0; i < trimData.range_count; i++) {
1363 			dprintf("[%3" B_PRIu32 "] %" B_PRIu64 " : %" B_PRIu64 "\n", i,
1364 				trimData.ranges[i].offset, trimData.ranges[i].size);
1365 		}
1366 #endif
1367 		if (ioctl(fVolume->Device(), B_TRIM_DEVICE, &trimData,
1368 				sizeof(fs_trim_data)
1369 					+ 2 * sizeof(uint64) * (trimData.range_count - 1)) != 0) {
1370 			return errno;
1371 		}
1372 
1373 		trimmedSize += trimData.trimmed_size;
1374 		trimData.range_count = 0;
1375 	}
1376 
1377 	return B_OK;
1378 }
1379 
1380 
1381 //	#pragma mark - debugger commands
1382 
1383 
1384 #ifdef BFS_DEBUGGER_COMMANDS
1385 
1386 void
1387 BlockAllocator::Dump(int32 index)
1388 {
1389 	kprintf("allocation groups: %" B_PRId32 " (base %p)\n", fNumGroups, fGroups);
1390 	kprintf("blocks per group: %" B_PRId32 "\n", fBlocksPerGroup);
1391 
1392 	for (int32 i = 0; i < fNumGroups; i++) {
1393 		if (index != -1 && i != index)
1394 			continue;
1395 
1396 		AllocationGroup& group = fGroups[i];
1397 
1398 		kprintf("[%3" B_PRId32 "] num bits:       %" B_PRIu32 "  (%p)\n", i,
1399 			group.NumBits(), &group);
1400 		kprintf("      num blocks:     %" B_PRIu32 "\n", group.NumBitmapBlocks());
1401 		kprintf("      start:          %" B_PRId32 "\n", group.Start());
1402 		kprintf("      first free:     %" B_PRId32 "\n", group.fFirstFree);
1403 		kprintf("      largest start:  %" B_PRId32 "%s\n", group.fLargestStart,
1404 			group.fLargestValid ? "" : "  (invalid)");
1405 		kprintf("      largest length: %" B_PRId32 "\n", group.fLargestLength);
1406 		kprintf("      free bits:      %" B_PRId32 "\n", group.fFreeBits);
1407 	}
1408 }
1409 
1410 
1411 #if BFS_TRACING
1412 static char kTraceBuffer[256];
1413 
1414 
1415 int
1416 dump_block_allocator_blocks(int argc, char** argv)
1417 {
1418 	if (argc != 3 || !strcmp(argv[1], "--help")) {
1419 		kprintf("usage: %s <ptr-to-volume> <block>\n", argv[0]);
1420 		return 0;
1421 	}
1422 
1423 	Volume* volume = (Volume*)parse_expression(argv[1]);
1424 	off_t block = parse_expression(argv[2]);
1425 
1426 	// iterate over all tracing entries to find overlapping actions
1427 
1428 	using namespace BFSBlockTracing;
1429 
1430 	LazyTraceOutput out(kTraceBuffer, sizeof(kTraceBuffer), 0);
1431 	TraceEntryIterator iterator;
1432 	while (TraceEntry* _entry = iterator.Next()) {
1433 		if (Allocate* entry = dynamic_cast<Allocate*>(_entry)) {
1434 			off_t first = volume->ToBlock(entry->Run());
1435 			off_t last = first - 1 + entry->Run().Length();
1436 			if (block >= first && block <= last) {
1437 				out.Clear();
1438 				const char* dump = out.DumpEntry(entry);
1439 				kprintf("%5ld. %s\n", iterator.Index(), dump);
1440 			}
1441 		} else if (Free* entry = dynamic_cast<Free*>(_entry)) {
1442 			off_t first = volume->ToBlock(entry->Run());
1443 			off_t last = first - 1 + entry->Run().Length();
1444 			if (block >= first && block <= last) {
1445 				out.Clear();
1446 				const char* dump = out.DumpEntry(entry);
1447 				kprintf("%5ld. %s\n", iterator.Index(), dump);
1448 			}
1449 		}
1450 	}
1451 
1452 	return 0;
1453 }
1454 #endif
1455 
1456 
1457 int
1458 dump_block_allocator(int argc, char** argv)
1459 {
1460 	int32 group = -1;
1461 	if (argc == 3) {
1462 		group = parse_expression(argv[2]);
1463 		argc--;
1464 	}
1465 
1466 	if (argc != 2 || !strcmp(argv[1], "--help")) {
1467 		kprintf("usage: %s <ptr-to-volume> [group]\n", argv[0]);
1468 		return 0;
1469 	}
1470 
1471 	Volume* volume = (Volume*)parse_expression(argv[1]);
1472 	BlockAllocator& allocator = volume->Allocator();
1473 
1474 	allocator.Dump(group);
1475 	return 0;
1476 }
1477 
1478 #endif	// BFS_DEBUGGER_COMMANDS
1479 
1480