xref: /haiku/src/add-ons/kernel/file_systems/bfs/BlockAllocator.cpp (revision 4a55cc230cf7566cadcbb23b1928eefff8aea9a2)
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 %Ld (%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& Block(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 NumBlocks() const { return fNumBlocks; }
206 	int32 Start() const { return fStart; }
207 
208 private:
209 	friend class BlockAllocator;
210 
211 	uint32	fNumBits;
212 	uint32	fNumBlocks;
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 blocks
269 	return Block(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, Block(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 		Block(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, Block(block),
329 			Block(block) & HOST_ENDIAN_TO_BFS_INT32(~mask)));
330 
331 		Block(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 	//fNumBlocks = (fVolume->NumBlocks() + fVolume->BlockSize() * 8 - 1)
528 		/// (fVolume->BlockSize() * 8);
529 	fNumBlocks = fVolume->NumBitmapBlocks();
530 
531 	fGroups = new(std::nothrow) AllocationGroup[fNumGroups];
532 	if (fGroups == NULL)
533 		return B_NO_MEMORY;
534 
535 	if (!full)
536 		return B_OK;
537 
538 	recursive_lock_lock(&fLock);
539 		// the lock will be released by the _Initialize() method
540 
541 	thread_id id = spawn_kernel_thread((thread_func)BlockAllocator::_Initialize,
542 		"bfs block allocator", B_LOW_PRIORITY, this);
543 	if (id < B_OK)
544 		return _Initialize(this);
545 
546 	recursive_lock_transfer_lock(&fLock, id);
547 
548 	return resume_thread(id);
549 }
550 
551 
552 status_t
553 BlockAllocator::InitializeAndClearBitmap(Transaction& transaction)
554 {
555 	status_t status = Initialize(false);
556 	if (status != B_OK)
557 		return status;
558 
559 	uint32 numBits = 8 * fBlocksPerGroup * fVolume->BlockSize();
560 	uint32 blockShift = fVolume->BlockShift();
561 
562 	uint32* buffer = (uint32*)malloc(numBits >> 3);
563 	if (buffer == NULL)
564 		RETURN_ERROR(B_NO_MEMORY);
565 
566 	memset(buffer, 0, numBits >> 3);
567 
568 	off_t offset = 1;
569 		// the bitmap starts directly after the superblock
570 
571 	// initialize the AllocationGroup objects and clear the on-disk bitmap
572 
573 	for (int32 i = 0; i < fNumGroups; i++) {
574 		if (write_pos(fVolume->Device(), offset << blockShift, buffer,
575 				fBlocksPerGroup << blockShift) < B_OK) {
576 			free(buffer);
577 			return B_ERROR;
578 		}
579 
580 		// the last allocation group may contain less blocks than the others
581 		if (i == fNumGroups - 1) {
582 			fGroups[i].fNumBits = fVolume->NumBlocks() - i * numBits;
583 			fGroups[i].fNumBlocks = 1 + ((fGroups[i].NumBits() - 1)
584 				>> (blockShift + 3));
585 		} else {
586 			fGroups[i].fNumBits = numBits;
587 			fGroups[i].fNumBlocks = fBlocksPerGroup;
588 		}
589 		fGroups[i].fStart = offset;
590 		fGroups[i].fFirstFree = fGroups[i].fLargestStart = 0;
591 		fGroups[i].fFreeBits = fGroups[i].fLargestLength = fGroups[i].fNumBits;
592 		fGroups[i].fLargestValid = true;
593 
594 		offset += fBlocksPerGroup;
595 	}
596 	free(buffer);
597 
598 	// reserve the boot block, the log area, and the block bitmap itself
599 	uint32 reservedBlocks = fVolume->ToBlock(fVolume->Log()) + fVolume->Log().Length();
600 	uint32 blocksToReserve = reservedBlocks;
601 	for (int32 i = 0; i < fNumGroups; i++) {
602 		int32 reservedBlocksInGroup = min_c(blocksToReserve, numBits);
603 		if (fGroups[i].Allocate(transaction, 0, reservedBlocksInGroup) < B_OK) {
604 			FATAL(("could not allocate reserved space for block bitmap/log!\n"));
605 			return B_ERROR;
606 		}
607 		blocksToReserve -= reservedBlocksInGroup;
608 		if (blocksToReserve == 0)
609 			break;
610 	}
611 	fVolume->SuperBlock().used_blocks
612 		= HOST_ENDIAN_TO_BFS_INT64(reservedBlocks);
613 
614 	return B_OK;
615 }
616 
617 
618 status_t
619 BlockAllocator::_Initialize(BlockAllocator* allocator)
620 {
621 	// The lock must already be held at this point
622 	RecursiveLocker locker(allocator->fLock, true);
623 
624 	Volume* volume = allocator->fVolume;
625 	uint32 blocks = allocator->fBlocksPerGroup;
626 	uint32 blockShift = volume->BlockShift();
627 	off_t freeBlocks = 0;
628 
629 	uint32* buffer = (uint32*)malloc(blocks << blockShift);
630 	if (buffer == NULL)
631 		RETURN_ERROR(B_NO_MEMORY);
632 
633 	AllocationGroup* groups = allocator->fGroups;
634 	off_t offset = 1;
635 	uint32 bitsPerGroup = 8 * (blocks << blockShift);
636 	int32 numGroups = allocator->fNumGroups;
637 
638 	for (int32 i = 0; i < numGroups; i++) {
639 		if (read_pos(volume->Device(), offset << blockShift, buffer,
640 				blocks << blockShift) < B_OK)
641 			break;
642 
643 		// the last allocation group may contain less blocks than the others
644 		if (i == numGroups - 1) {
645 			groups[i].fNumBits = volume->NumBlocks() - i * bitsPerGroup;
646 			groups[i].fNumBlocks = 1 + ((groups[i].NumBits() - 1)
647 				>> (blockShift + 3));
648 		} else {
649 			groups[i].fNumBits = bitsPerGroup;
650 			groups[i].fNumBlocks = blocks;
651 		}
652 		groups[i].fStart = offset;
653 
654 		// finds all free ranges in this allocation group
655 		int32 start = -1, range = 0;
656 		int32 numBits = groups[i].fNumBits, bit = 0;
657 		int32 count = (numBits + 31) / 32;
658 
659 		for (int32 k = 0; k < count; k++) {
660 			for (int32 j = 0; j < 32 && bit < numBits; j++, bit++) {
661 				if (buffer[k] & (1UL << j)) {
662 					// block is in use
663 					if (range > 0) {
664 						groups[i].AddFreeRange(start, range);
665 						range = 0;
666 					}
667 				} else if (range++ == 0) {
668 					// block is free, start new free range
669 					start = bit;
670 				}
671 			}
672 		}
673 		if (range)
674 			groups[i].AddFreeRange(start, range);
675 
676 		freeBlocks += groups[i].fFreeBits;
677 
678 		offset += blocks;
679 	}
680 	free(buffer);
681 
682 	// check if block bitmap and log area are reserved
683 	uint32 reservedBlocks = volume->ToBlock(volume->Log()) + volume->Log().Length();
684 
685 	if (allocator->CheckBlocks(0, reservedBlocks) != B_OK) {
686 		if (volume->IsReadOnly()) {
687 			FATAL(("Space for block bitmap or log area is not reserved "
688 				"(volume is mounted read-only)!\n"));
689 		} else {
690 			Transaction transaction(volume, 0);
691 			if (groups[0].Allocate(transaction, 0, reservedBlocks) != B_OK) {
692 				FATAL(("Could not allocate reserved space for block "
693 					"bitmap/log!\n"));
694 				volume->Panic();
695 			} else {
696 				transaction.Done();
697 				FATAL(("Space for block bitmap or log area was not "
698 					"reserved!\n"));
699 			}
700 		}
701 	}
702 
703 	off_t usedBlocks = volume->NumBlocks() - freeBlocks;
704 	if (volume->UsedBlocks() != usedBlocks) {
705 		// If the disk in a dirty state at mount time, it's
706 		// normal that the values don't match
707 		INFORM(("volume reports %" B_PRIdOFF " used blocks, correct is %"
708 			B_PRIdOFF "\n", volume->UsedBlocks(), usedBlocks));
709 		volume->SuperBlock().used_blocks = HOST_ENDIAN_TO_BFS_INT64(usedBlocks);
710 	}
711 
712 	return B_OK;
713 }
714 
715 
716 void
717 BlockAllocator::Uninitialize()
718 {
719 	// We only have to make sure that the initializer thread isn't running
720 	// anymore.
721 	recursive_lock_lock(&fLock);
722 }
723 
724 
725 /*!	Tries to allocate between \a minimum, and \a maximum blocks starting
726 	at group \a groupIndex with offset \a start. The resulting allocation
727 	is put into \a run.
728 
729 	The number of allocated blocks is always a multiple of \a minimum which
730 	has to be a power of two value.
731 */
732 status_t
733 BlockAllocator::AllocateBlocks(Transaction& transaction, int32 groupIndex,
734 	uint16 start, uint16 maximum, uint16 minimum, block_run& run)
735 {
736 	if (maximum == 0)
737 		return B_BAD_VALUE;
738 
739 	FUNCTION_START(("group = %" B_PRId32 ", start = %" B_PRIu16
740 		", maximum = %" B_PRIu16 ", minimum = %" B_PRIu16 "\n",
741 		groupIndex, start, maximum, minimum));
742 
743 	AllocationBlock cached(fVolume);
744 	RecursiveLocker lock(fLock);
745 
746 	uint32 bitsPerFullBlock = fVolume->BlockSize() << 3;
747 
748 	// Find the block_run that can fulfill the request best
749 	int32 bestGroup = -1;
750 	int32 bestStart = -1;
751 	int32 bestLength = -1;
752 
753 	for (int32 i = 0; i < fNumGroups + 1; i++, groupIndex++, start = 0) {
754 		groupIndex = groupIndex % fNumGroups;
755 		AllocationGroup& group = fGroups[groupIndex];
756 
757 		CHECK_ALLOCATION_GROUP(groupIndex);
758 
759 		if (start >= group.NumBits() || group.IsFull())
760 			continue;
761 
762 		// The wanted maximum is smaller than the largest free block in the
763 		// group or already smaller than the minimum
764 
765 		if (start < group.fFirstFree)
766 			start = group.fFirstFree;
767 
768 		if (group.fLargestValid) {
769 			if (group.fLargestLength < bestLength)
770 				continue;
771 
772 			if (group.fLargestStart >= start) {
773 				if (group.fLargestLength >= bestLength) {
774 					bestGroup = groupIndex;
775 					bestStart = group.fLargestStart;
776 					bestLength = group.fLargestLength;
777 
778 					if (bestLength >= maximum)
779 						break;
780 				}
781 
782 				// We know everything about this group we have to, let's skip
783 				// to the next
784 				continue;
785 			}
786 		}
787 
788 		// There may be more than one block per allocation group - and
789 		// we iterate through it to find a place for the allocation.
790 		// (one allocation can't exceed one allocation group)
791 
792 		uint32 block = start / (fVolume->BlockSize() << 3);
793 		int32 currentStart = 0, currentLength = 0;
794 		int32 groupLargestStart = -1;
795 		int32 groupLargestLength = -1;
796 		int32 currentBit = start;
797 		bool canFindGroupLargest = start == 0;
798 
799 		for (; block < group.NumBlocks(); block++) {
800 			if (cached.SetTo(group, block) < B_OK)
801 				RETURN_ERROR(B_ERROR);
802 
803 			T(Block("alloc-in", group.Start() + block, cached.Block(),
804 				fVolume->BlockSize(), groupIndex, currentStart));
805 
806 			// find a block large enough to hold the allocation
807 			for (uint32 bit = start % bitsPerFullBlock;
808 					bit < cached.NumBlockBits(); bit++) {
809 				if (!cached.IsUsed(bit)) {
810 					if (currentLength == 0) {
811 						// start new range
812 						currentStart = currentBit;
813 					}
814 
815 					// have we found a range large enough to hold numBlocks?
816 					if (++currentLength >= maximum) {
817 						bestGroup = groupIndex;
818 						bestStart = currentStart;
819 						bestLength = currentLength;
820 						break;
821 					}
822 				} else {
823 					if (currentLength) {
824 						// end of a range
825 						if (currentLength > bestLength) {
826 							bestGroup = groupIndex;
827 							bestStart = currentStart;
828 							bestLength = currentLength;
829 						}
830 						if (currentLength > groupLargestLength) {
831 							groupLargestStart = currentStart;
832 							groupLargestLength = currentLength;
833 						}
834 						currentLength = 0;
835 					}
836 					if ((int32)group.NumBits() - currentBit
837 							<= groupLargestLength) {
838 						// We can't find a bigger block in this group anymore,
839 						// let's skip the rest.
840 						block = group.NumBlocks();
841 						break;
842 					}
843 				}
844 				currentBit++;
845 			}
846 
847 			T(Block("alloc-out", block, cached.Block(),
848 				fVolume->BlockSize(), groupIndex, currentStart));
849 
850 			if (bestLength >= maximum) {
851 				canFindGroupLargest = false;
852 				break;
853 			}
854 
855 			// start from the beginning of the next block
856 			start = 0;
857 		}
858 
859 		if (currentBit == (int32)group.NumBits()) {
860 			if (currentLength > bestLength) {
861 				bestGroup = groupIndex;
862 				bestStart = currentStart;
863 				bestLength = currentLength;
864 			}
865 			if (canFindGroupLargest && currentLength > groupLargestLength) {
866 				groupLargestStart = currentStart;
867 				groupLargestLength = currentLength;
868 			}
869 		}
870 
871 		if (canFindGroupLargest && !group.fLargestValid
872 			&& groupLargestLength >= 0) {
873 			group.fLargestStart = groupLargestStart;
874 			group.fLargestLength = groupLargestLength;
875 			group.fLargestValid = true;
876 		}
877 
878 		if (bestLength >= maximum)
879 			break;
880 	}
881 
882 	// If we found a suitable range, mark the blocks as in use, and
883 	// write the updated block bitmap back to disk
884 	if (bestLength < minimum)
885 		return B_DEVICE_FULL;
886 
887 	if (bestLength > maximum)
888 		bestLength = maximum;
889 	else if (minimum > 1) {
890 		// make sure bestLength is a multiple of minimum
891 		bestLength = round_down(bestLength, minimum);
892 	}
893 
894 	if (fGroups[bestGroup].Allocate(transaction, bestStart, bestLength) != B_OK)
895 		RETURN_ERROR(B_IO_ERROR);
896 
897 	CHECK_ALLOCATION_GROUP(bestGroup);
898 
899 	run.allocation_group = HOST_ENDIAN_TO_BFS_INT32(bestGroup);
900 	run.start = HOST_ENDIAN_TO_BFS_INT16(bestStart);
901 	run.length = HOST_ENDIAN_TO_BFS_INT16(bestLength);
902 
903 	fVolume->SuperBlock().used_blocks
904 		= HOST_ENDIAN_TO_BFS_INT64(fVolume->UsedBlocks() + bestLength);
905 		// We are not writing back the disk's superblock - it's
906 		// either done by the journaling code, or when the disk
907 		// is unmounted.
908 		// If the value is not correct at mount time, it will be
909 		// fixed anyway.
910 
911 	// We need to flush any remaining blocks in the new allocation to make sure
912 	// they won't interfere with the file cache.
913 	block_cache_discard(fVolume->BlockCache(), fVolume->ToBlock(run),
914 		run.Length());
915 
916 	T(Allocate(run));
917 	return B_OK;
918 }
919 
920 
921 status_t
922 BlockAllocator::AllocateForInode(Transaction& transaction,
923 	const block_run* parent, mode_t type, block_run& run)
924 {
925 	// Apply some allocation policies here (AllocateBlocks() will break them
926 	// if necessary) - we will start with those described in Dominic Giampaolo's
927 	// "Practical File System Design", and see how good they work
928 
929 	// Files are going in the same allocation group as its parent,
930 	// sub-directories will be inserted 8 allocation groups after
931 	// the one of the parent
932 	uint16 group = parent->AllocationGroup();
933 	if ((type & (S_DIRECTORY | S_INDEX_DIR | S_ATTR_DIR)) == S_DIRECTORY)
934 		group += 8;
935 
936 	return AllocateBlocks(transaction, group, 0, 1, 1, run);
937 }
938 
939 
940 status_t
941 BlockAllocator::Allocate(Transaction& transaction, Inode* inode,
942 	off_t numBlocks, block_run& run, uint16 minimum)
943 {
944 	if (numBlocks <= 0)
945 		return B_ERROR;
946 
947 	// one block_run can't hold more data than there is in one allocation group
948 	if (numBlocks > fGroups[0].NumBits())
949 		numBlocks = fGroups[0].NumBits();
950 
951 	// since block_run.length is uint16, the largest number of blocks that
952 	// can be covered by a block_run is 65535
953 	// TODO: if we drop compatibility, couldn't we do this any better?
954 	// There are basically two possibilities:
955 	// a) since a length of zero doesn't have any sense, take that for 65536 -
956 	//    but that could cause many problems (bugs) in other areas
957 	// b) reduce the maximum amount of blocks per block_run, so that the
958 	//    remaining number of free blocks can be used in a useful manner
959 	//    (like 4 blocks) - but that would also reduce the maximum file size
960 	// c) have BlockRun::Length() return (length + 1).
961 	if (numBlocks > MAX_BLOCK_RUN_LENGTH)
962 		numBlocks = MAX_BLOCK_RUN_LENGTH;
963 
964 	// Apply some allocation policies here (AllocateBlocks() will break them
965 	// if necessary)
966 	uint16 group = inode->BlockRun().AllocationGroup();
967 	uint16 start = 0;
968 
969 	// Are there already allocated blocks? (then just try to allocate near the
970 	// last one)
971 	if (inode->Size() > 0) {
972 		const data_stream& data = inode->Node().data;
973 		// TODO: we currently don't care for when the data stream
974 		// is already grown into the indirect ranges
975 		if (data.max_double_indirect_range == 0
976 			&& data.max_indirect_range == 0) {
977 			// Since size > 0, there must be a valid block run in this stream
978 			int32 last = 0;
979 			for (; last < NUM_DIRECT_BLOCKS - 1; last++)
980 				if (data.direct[last + 1].IsZero())
981 					break;
982 
983 			group = data.direct[last].AllocationGroup();
984 			start = data.direct[last].Start() + data.direct[last].Length();
985 		}
986 	} else if (inode->IsContainer() || inode->IsSymLink()) {
987 		// directory and symbolic link data will go in the same allocation
988 		// group as the inode is in but after the inode data
989 		start = inode->BlockRun().Start();
990 	} else {
991 		// file data will start in the next allocation group
992 		group = inode->BlockRun().AllocationGroup() + 1;
993 	}
994 
995 	return AllocateBlocks(transaction, group, start, numBlocks, minimum, run);
996 }
997 
998 
999 status_t
1000 BlockAllocator::Free(Transaction& transaction, block_run run)
1001 {
1002 	RecursiveLocker lock(fLock);
1003 
1004 	int32 group = run.AllocationGroup();
1005 	uint16 start = run.Start();
1006 	uint16 length = run.Length();
1007 
1008 	FUNCTION_START(("group = %" B_PRId32 ", start = %" B_PRIu16
1009 		", length = %" B_PRIu16 "\n", group, start, length))
1010 	T(Free(run));
1011 
1012 	// doesn't use Volume::IsValidBlockRun() here because it can check better
1013 	// against the group size (the last group may have a different length)
1014 	if (group < 0 || group >= fNumGroups
1015 		|| start > fGroups[group].NumBits()
1016 		|| uint32(start + length) > fGroups[group].NumBits()
1017 		|| length == 0) {
1018 		FATAL(("tried to free an invalid block_run"
1019 			" (%" B_PRId32 ", %" B_PRIu16 ", %" B_PRIu16")\n",
1020 			group, start, length));
1021 		DEBUGGER(("tried to free invalid block_run"));
1022 		return B_BAD_VALUE;
1023 	}
1024 	// check if someone tries to free reserved areas at the beginning of the
1025 	// drive
1026 	if (group < fVolume->Log().AllocationGroup()
1027 		|| (group == fVolume->Log().AllocationGroup()
1028 			&& start < uint32(fVolume->Log().Start()) + fVolume->Log().Length())) {
1029 		FATAL(("tried to free a reserved block_run"
1030 			" (%" B_PRId32 ", %" B_PRIu16 ", %" B_PRIu16")\n",
1031 			group, start, length));
1032 		DEBUGGER(("tried to free reserved block"));
1033 		return B_BAD_VALUE;
1034 	}
1035 #ifdef DEBUG
1036 	if (CheckBlockRun(run) != B_OK)
1037 		return B_BAD_DATA;
1038 #endif
1039 
1040 	CHECK_ALLOCATION_GROUP(group);
1041 
1042 	if (fGroups[group].Free(transaction, start, length) != B_OK)
1043 		RETURN_ERROR(B_IO_ERROR);
1044 
1045 	CHECK_ALLOCATION_GROUP(group);
1046 
1047 #ifdef DEBUG
1048 	if (CheckBlockRun(run, NULL, false) != B_OK) {
1049 		DEBUGGER(("CheckBlockRun() reports allocated blocks (which were just "
1050 			"freed)\n"));
1051 	}
1052 #endif
1053 
1054 	fVolume->SuperBlock().used_blocks =
1055 		HOST_ENDIAN_TO_BFS_INT64(fVolume->UsedBlocks() - run.Length());
1056 	return B_OK;
1057 }
1058 
1059 
1060 #ifdef DEBUG_FRAGMENTER
1061 void
1062 BlockAllocator::Fragment()
1063 {
1064 	AllocationBlock cached(fVolume);
1065 	RecursiveLocker lock(fLock);
1066 
1067 	// only leave 4 block holes
1068 	static const uint32 kMask = 0x0f0f0f0f;
1069 	uint32 valuesPerBlock = fVolume->BlockSize() / 4;
1070 
1071 	for (int32 i = 0; i < fNumGroups; i++) {
1072 		AllocationGroup& group = fGroups[i];
1073 
1074 		for (uint32 block = 0; block < group.NumBlocks(); block++) {
1075 			Transaction transaction(fVolume, 0);
1076 
1077 			if (cached.SetToWritable(transaction, group, block) != B_OK)
1078 				return;
1079 
1080 			for (int32 index = 0; index < valuesPerBlock; index++) {
1081 				cached.Block(index) |= HOST_ENDIAN_TO_BFS_INT32(kMask);
1082 			}
1083 
1084 			transaction.Done();
1085 		}
1086 	}
1087 }
1088 #endif	// DEBUG_FRAGMENTER
1089 
1090 
1091 #ifdef DEBUG_ALLOCATION_GROUPS
1092 void
1093 BlockAllocator::_CheckGroup(int32 groupIndex) const
1094 {
1095 	AllocationBlock cached(fVolume);
1096 	ASSERT_LOCKED_RECURSIVE(&fLock);
1097 
1098 	AllocationGroup& group = fGroups[groupIndex];
1099 
1100 	int32 currentStart = 0, currentLength = 0;
1101 	int32 firstFree = -1;
1102 	int32 largestStart = -1;
1103 	int32 largestLength = 0;
1104 	int32 currentBit = 0;
1105 
1106 	for (uint32 block = 0; block < group.NumBlocks(); block++) {
1107 		if (cached.SetTo(group, block) < B_OK) {
1108 			panic("setting group block %d failed\n", (int)block);
1109 			return;
1110 		}
1111 
1112 		for (uint32 bit = 0; bit < cached.NumBlockBits(); bit++) {
1113 			if (!cached.IsUsed(bit)) {
1114 				if (firstFree < 0) {
1115 					firstFree = currentBit;
1116 					if (!group.fLargestValid) {
1117 						if (firstFree >= 0 && firstFree < group.fFirstFree) {
1118 							// mostly harmless but noteworthy
1119 							dprintf("group %d first free too late: should be "
1120 								"%d, is %d\n", (int)groupIndex, (int)firstFree,
1121 								(int)group.fFirstFree);
1122 						}
1123 						return;
1124 					}
1125 				}
1126 
1127 				if (currentLength == 0) {
1128 					// start new range
1129 					currentStart = currentBit;
1130 				}
1131 				currentLength++;
1132 			} else if (currentLength) {
1133 				// end of a range
1134 				if (currentLength > largestLength) {
1135 					largestStart = currentStart;
1136 					largestLength = currentLength;
1137 				}
1138 				currentLength = 0;
1139 			}
1140 			currentBit++;
1141 		}
1142 	}
1143 
1144 	if (currentLength > largestLength) {
1145 		largestStart = currentStart;
1146 		largestLength = currentLength;
1147 	}
1148 
1149 	if (firstFree >= 0 && firstFree < group.fFirstFree) {
1150 		// mostly harmless but noteworthy
1151 		dprintf("group %d first free too late: should be %d, is %d\n",
1152 			(int)groupIndex, (int)firstFree, (int)group.fFirstFree);
1153 	}
1154 	if (group.fLargestValid
1155 		&& (largestStart != group.fLargestStart
1156 			|| largestLength != group.fLargestLength)) {
1157 		panic("bfs %p: group %d largest differs: %d.%d, checked %d.%d.\n",
1158 			fVolume, (int)groupIndex, (int)group.fLargestStart,
1159 			(int)group.fLargestLength, (int)largestStart, (int)largestLength);
1160 	}
1161 }
1162 #endif	// DEBUG_ALLOCATION_GROUPS
1163 
1164 
1165 status_t
1166 BlockAllocator::Trim(uint64 offset, uint64 size, uint64& trimmedSize)
1167 {
1168 	// TODO: Remove this check when offset and size handling is implemented
1169 	if (offset != 0
1170 		|| fVolume->NumBlocks() < 0
1171 		|| size < (uint64)fVolume->NumBlocks() * fVolume->BlockSize()) {
1172 		INFORM(("BFS Trim: Ranges smaller than the file system size"
1173 			" are not supported yet.\n"));
1174 		return B_UNSUPPORTED;
1175 	}
1176 
1177 	const uint32 kTrimRanges = 128;
1178 	fs_trim_data* trimData = (fs_trim_data*)malloc(sizeof(fs_trim_data)
1179 		+ 2 * sizeof(uint64) * (kTrimRanges - 1));
1180 	if (trimData == NULL)
1181 		return B_NO_MEMORY;
1182 
1183 	MemoryDeleter deleter(trimData);
1184 	RecursiveLocker locker(fLock);
1185 
1186 	// TODO: take given offset and size into account!
1187 	int32 lastGroup = fNumGroups - 1;
1188 	uint32 firstBlock = 0;
1189 	uint32 firstBit = 0;
1190 	uint64 currentBlock = 0;
1191 	uint32 blockShift = fVolume->BlockShift();
1192 
1193 	uint64 firstFree = 0;
1194 	uint64 freeLength = 0;
1195 
1196 	trimData->range_count = 0;
1197 	trimmedSize = 0;
1198 
1199 	AllocationBlock cached(fVolume);
1200 	for (int32 groupIndex = 0; groupIndex <= lastGroup; groupIndex++) {
1201 		AllocationGroup& group = fGroups[groupIndex];
1202 
1203 		for (uint32 block = firstBlock; block < group.NumBlocks(); block++) {
1204 			cached.SetTo(group, block);
1205 
1206 			for (uint32 i = firstBit; i < cached.NumBlockBits(); i++) {
1207 				if (cached.IsUsed(i)) {
1208 					// Block is in use
1209 					if (freeLength > 0) {
1210 						// Overflow is unlikely to happen, but check it anyway
1211 						if ((firstFree << blockShift) >> blockShift
1212 								!= firstFree
1213 							|| (freeLength << blockShift) >> blockShift
1214 								!= freeLength) {
1215 							FATAL(("BlockAllocator::Trim:"
1216 								" Overflow detected!\n"));
1217 							return B_ERROR;
1218 						}
1219 						status_t status = _TrimNext(*trimData, kTrimRanges,
1220 							firstFree << blockShift, freeLength << blockShift,
1221 							false, trimmedSize);
1222 						if (status != B_OK)
1223 							return status;
1224 
1225 						freeLength = 0;
1226 					}
1227 				} else if (freeLength++ == 0) {
1228 					// Block is free, start new free range
1229 					firstFree = currentBlock;
1230 				}
1231 
1232 				currentBlock++;
1233 			}
1234 		}
1235 
1236 		firstBlock = 0;
1237 		firstBit = 0;
1238 	}
1239 
1240 	return _TrimNext(*trimData, kTrimRanges, firstFree << blockShift,
1241 		freeLength << blockShift, true, trimmedSize);
1242 }
1243 
1244 
1245 //	#pragma mark -
1246 
1247 
1248 /*!	Checks whether or not the specified block range is allocated or not,
1249 	depending on the \a allocated argument.
1250 */
1251 status_t
1252 BlockAllocator::CheckBlocks(off_t start, off_t length, bool allocated,
1253 	off_t* firstError)
1254 {
1255 	if (start < 0 || start + length > fVolume->NumBlocks())
1256 		return B_BAD_VALUE;
1257 
1258 	off_t block = start;
1259 
1260 	int32 group = start >> fVolume->AllocationGroupShift();
1261 	uint32 bitmapBlock = start / (fVolume->BlockSize() << 3);
1262 	uint32 blockOffset = start % (fVolume->BlockSize() << 3);
1263 
1264 	uint32 groupBlock = bitmapBlock % fBlocksPerGroup;
1265 
1266 	AllocationBlock cached(fVolume);
1267 
1268 	while (groupBlock < fGroups[group].NumBlocks() && length > 0) {
1269 		if (cached.SetTo(fGroups[group], groupBlock) != B_OK)
1270 			RETURN_ERROR(B_IO_ERROR);
1271 
1272 		for (; blockOffset < cached.NumBlockBits() && length > 0;
1273 				blockOffset++, length--, block++) {
1274 			if (cached.IsUsed(blockOffset) != allocated) {
1275 				PRINT(("CheckBlocks: Erroneous block (group = %" B_PRId32
1276 					", groupBlock = %" B_PRIu32
1277 					", blockOffset = %" B_PRIu32 ")!\n",
1278 					group, groupBlock, blockOffset));
1279 
1280 				if (firstError)
1281 					*firstError = block;
1282 
1283 				return B_BAD_DATA;
1284 			}
1285 		}
1286 
1287 		blockOffset = 0;
1288 
1289 		if (++groupBlock >= fGroups[group].NumBlocks()) {
1290 			groupBlock = 0;
1291 			group++;
1292 		}
1293 	}
1294 
1295 	return B_OK;
1296 }
1297 
1298 
1299 bool
1300 BlockAllocator::IsValidBlockRun(block_run run, const char* type)
1301 {
1302 	if (run.AllocationGroup() < 0 || run.AllocationGroup() >= fNumGroups
1303 		|| run.Start() > fGroups[run.AllocationGroup()].fNumBits
1304 		|| uint32(run.Start() + run.Length())
1305 				> fGroups[run.AllocationGroup()].fNumBits
1306 		|| run.length == 0) {
1307 		PRINT(("%s: block_run(%" B_PRId32 ", %" B_PRIu16 ", %" B_PRIu16")"
1308 			" is invalid!\n", type, run.AllocationGroup(), run.Start(),
1309 			run.Length()));
1310 		return false;
1311 	}
1312 	return true;
1313 }
1314 
1315 
1316 status_t
1317 BlockAllocator::CheckBlockRun(block_run run, const char* type, bool allocated)
1318 {
1319 	if (!IsValidBlockRun(run, type))
1320 		return B_BAD_DATA;
1321 
1322 	status_t status = CheckBlocks(fVolume->ToBlock(run), run.Length(),
1323 		allocated);
1324 	if (status != B_OK) {
1325 		PRINT(("%s: block_run(%" B_PRId32 ", %" B_PRIu16 ", %" B_PRIu16")"
1326 			" is only partially allocated!\n", type, run.AllocationGroup(),
1327 			run.Start(), run.Length()));
1328 	}
1329 
1330 	return status;
1331 }
1332 
1333 
1334 bool
1335 BlockAllocator::_AddTrim(fs_trim_data& trimData, uint32 maxRanges,
1336 	uint64 offset, uint64 size)
1337 {
1338 	if (trimData.range_count < maxRanges && size > 0) {
1339 		trimData.ranges[trimData.range_count].offset = offset;
1340 		trimData.ranges[trimData.range_count].size = size;
1341 		trimData.range_count++;
1342 		return true;
1343 	}
1344 
1345 	return false;
1346 }
1347 
1348 
1349 status_t
1350 BlockAllocator::_TrimNext(fs_trim_data& trimData, uint32 maxRanges,
1351 	uint64 offset, uint64 size, bool force, uint64& trimmedSize)
1352 {
1353 	PRINT(("_TrimNext(index %" B_PRIu32 ", offset %" B_PRIu64 ", size %"
1354 		B_PRIu64 ")\n", trimData.range_count, offset, size));
1355 
1356 	bool pushed = _AddTrim(trimData, maxRanges, offset, size);
1357 
1358 	if (!pushed || force) {
1359 		// Trim now
1360 		trimData.trimmed_size = 0;
1361 #ifdef DEBUG_TRIM
1362 		dprintf("TRIM: BFS: free ranges (bytes):\n");
1363 		for (uint32 i = 0; i < trimData.range_count; i++) {
1364 			dprintf("[%3" B_PRIu32 "] %" B_PRIu64 " : %" B_PRIu64 "\n", i,
1365 				trimData.ranges[i].offset, trimData.ranges[i].size);
1366 		}
1367 #endif
1368 		if (ioctl(fVolume->Device(), B_TRIM_DEVICE, &trimData,
1369 				sizeof(fs_trim_data)
1370 					+ 2 * sizeof(uint64) * (trimData.range_count - 1)) != 0) {
1371 			return errno;
1372 		}
1373 
1374 		trimmedSize += trimData.trimmed_size;
1375 		trimData.range_count = 0;
1376 	}
1377 
1378 	if (!pushed)
1379 		_AddTrim(trimData, maxRanges, offset, size);
1380 
1381 	return B_OK;
1382 }
1383 
1384 
1385 //	#pragma mark - debugger commands
1386 
1387 
1388 #ifdef BFS_DEBUGGER_COMMANDS
1389 
1390 void
1391 BlockAllocator::Dump(int32 index)
1392 {
1393 	kprintf("allocation groups: %" B_PRId32 " (base %p)\n", fNumGroups, fGroups);
1394 	kprintf("blocks per group: %" B_PRId32 "\n", fBlocksPerGroup);
1395 
1396 	for (int32 i = 0; i < fNumGroups; i++) {
1397 		if (index != -1 && i != index)
1398 			continue;
1399 
1400 		AllocationGroup& group = fGroups[i];
1401 
1402 		kprintf("[%3" B_PRId32 "] num bits:       %" B_PRIu32 "  (%p)\n", i,
1403 			group.NumBits(), &group);
1404 		kprintf("      num blocks:     %" B_PRIu32 "\n", group.NumBlocks());
1405 		kprintf("      start:          %" B_PRId32 "\n", group.Start());
1406 		kprintf("      first free:     %" B_PRId32 "\n", group.fFirstFree);
1407 		kprintf("      largest start:  %" B_PRId32 "%s\n", group.fLargestStart,
1408 			group.fLargestValid ? "" : "  (invalid)");
1409 		kprintf("      largest length: %" B_PRId32 "\n", group.fLargestLength);
1410 		kprintf("      free bits:      %" B_PRId32 "\n", group.fFreeBits);
1411 	}
1412 }
1413 
1414 
1415 #if BFS_TRACING
1416 static char kTraceBuffer[256];
1417 
1418 
1419 int
1420 dump_block_allocator_blocks(int argc, char** argv)
1421 {
1422 	if (argc != 3 || !strcmp(argv[1], "--help")) {
1423 		kprintf("usage: %s <ptr-to-volume> <block>\n", argv[0]);
1424 		return 0;
1425 	}
1426 
1427 	Volume* volume = (Volume*)parse_expression(argv[1]);
1428 	off_t block = parse_expression(argv[2]);
1429 
1430 	// iterate over all tracing entries to find overlapping actions
1431 
1432 	using namespace BFSBlockTracing;
1433 
1434 	LazyTraceOutput out(kTraceBuffer, sizeof(kTraceBuffer), 0);
1435 	TraceEntryIterator iterator;
1436 	while (TraceEntry* _entry = iterator.Next()) {
1437 		if (Allocate* entry = dynamic_cast<Allocate*>(_entry)) {
1438 			off_t first = volume->ToBlock(entry->Run());
1439 			off_t last = first - 1 + entry->Run().Length();
1440 			if (block >= first && block <= last) {
1441 				out.Clear();
1442 				const char* dump = out.DumpEntry(entry);
1443 				kprintf("%5ld. %s\n", iterator.Index(), dump);
1444 			}
1445 		} else if (Free* entry = dynamic_cast<Free*>(_entry)) {
1446 			off_t first = volume->ToBlock(entry->Run());
1447 			off_t last = first - 1 + entry->Run().Length();
1448 			if (block >= first && block <= last) {
1449 				out.Clear();
1450 				const char* dump = out.DumpEntry(entry);
1451 				kprintf("%5ld. %s\n", iterator.Index(), dump);
1452 			}
1453 		}
1454 	}
1455 
1456 	return 0;
1457 }
1458 #endif
1459 
1460 
1461 int
1462 dump_block_allocator(int argc, char** argv)
1463 {
1464 	int32 group = -1;
1465 	if (argc == 3) {
1466 		group = parse_expression(argv[2]);
1467 		argc--;
1468 	}
1469 
1470 	if (argc != 2 || !strcmp(argv[1], "--help")) {
1471 		kprintf("usage: %s <ptr-to-volume> [group]\n", argv[0]);
1472 		return 0;
1473 	}
1474 
1475 	Volume* volume = (Volume*)parse_expression(argv[1]);
1476 	BlockAllocator& allocator = volume->Allocator();
1477 
1478 	allocator.Dump(group);
1479 	return 0;
1480 }
1481 
1482 #endif	// BFS_DEBUGGER_COMMANDS
1483 
1484