2 * Copyright 2017, Chế Vũ Gia Hy, cvghy116@gmail.com.
3 * This file may be used under the terms of the MIT License.
7 #include "ExtentAllocator.h"
12 CachedExtent::Create(uint64 offset
, uint64 length
, uint64 flags
)
14 CachedExtent
* self
= new(std::nothrow
) CachedExtent();
18 self
->offset
= offset
;
19 self
->length
= length
;
21 if ((flags
& BTRFS_EXTENT_FLAG_ALLOCATED
) != 0)
30 CachedExtent::Delete()
40 CachedExtent::IsAllocated() const
42 return (flags
& BTRFS_EXTENT_FLAG_ALLOCATED
) != 0;
47 CachedExtent::IsData() const
49 return (flags
& BTRFS_EXTENT_FLAG_DATA
) != 0;
54 CachedExtent::Info() const
56 const char* extentType
= "allocated tree block";
57 if (IsAllocated() == false && IsData() == false)
58 extentType
= "free tree block";
59 else if (IsAllocated() == false && IsData() == true)
60 extentType
= "free data extent";
61 else if (IsAllocated() == true && IsData() == true)
62 extentType
= "allocated data extent";
64 TRACE("%s at %" B_PRIu64
" length %" B_PRIu64
" refCount %i\n",
65 extentType
, offset
, length
, refCount
);
69 // ExtentTree Implementation
72 CachedExtentTree::CachedExtentTree(const TreeDefinition
& definition
)
74 AVLTree
<TreeDefinition
>(definition
)
79 CachedExtentTree::~CachedExtentTree()
85 /* Find extent that cover or after "offset" and has length >= "size"
86 * it must also satisfy the condition "type".
89 CachedExtentTree::FindNext(CachedExtent
** chosen
, uint64 offset
, uint64 size
,
92 CachedExtent
* found
= Find(offset
);
93 while (found
!= NULL
&& (found
->flags
!= type
|| found
->length
< size
))
97 return B_ENTRY_NOT_FOUND
;
103 /* this will insert all free extents that are holes,
104 * created by existed allocated extents in the tree
105 * from lowerBound to upperBound
108 CachedExtentTree::FillFreeExtents(uint64 lowerBound
, uint64 upperBound
)
110 CachedExtent
* node
= FindClosest(lowerBound
, false);
111 CachedExtent
* hole
= NULL
;
112 status_t status
= B_OK
;
113 uint64 flags
= node
->flags
& (~BTRFS_EXTENT_FLAG_ALLOCATED
);
114 if (lowerBound
< node
->offset
) {
115 hole
= CachedExtent::Create(lowerBound
, node
->offset
- lowerBound
,
117 status
= _AddFreeExtent(hole
);
122 CachedExtent
* next
= NULL
;
123 while ((next
= Next(node
)) != NULL
&& next
->End() < upperBound
) {
124 if (node
->End() == next
->offset
) {
129 hole
= CachedExtent::Create(node
->End(), next
->offset
- node
->End(),
131 status
= _AddFreeExtent(hole
);
137 // final node should be a right most node
138 if (upperBound
> node
->End()) {
139 hole
= CachedExtent::Create(node
->End(), upperBound
- node
->End(),
141 status
= _AddFreeExtent(hole
);
149 CachedExtentTree::_RemoveExtent(CachedExtent
* node
)
152 if (node
->refCount
<= 0) {
160 CachedExtentTree::_AddAllocatedExtent(CachedExtent
* node
)
162 if (node
== NULL
|| node
->IsAllocated() == false)
165 CachedExtent
* found
= Find(node
->offset
);
171 if ((found
->IsData() && !node
->IsData())
172 || (!found
->IsData() && node
->IsData())) {
173 // this shouldn't happen as because different type has
174 // its different region for locating.
179 if (found
->IsAllocated() == false) {
180 // split free extents (found)
181 if (node
->End() > found
->End()) {
182 // TODO: when to handle this ?
187 // |----- found ------|
189 uint64 diff
= node
->offset
- found
->offset
;
190 found
->offset
+= diff
+ node
->length
;
191 found
->length
-= diff
+ node
->length
;
192 // diff < 0 couldn't happen because of the Compare function
194 CachedExtent
* leftEmpty
= CachedExtent::Create(
195 node
->offset
- diff
, diff
, found
->flags
);
196 _AddFreeExtent(leftEmpty
);
199 if (found
->length
== 0) {
200 // free-extent that has no space
201 _RemoveExtent(found
);
208 if (found
->length
== node
->length
) {
211 // TODO: What should we do in this case ?
221 CachedExtentTree::_AddFreeExtent(CachedExtent
* node
)
223 if (node
== NULL
|| node
->IsAllocated() == true)
226 CachedExtent
* found
= Find(node
->offset
);
229 _CombineFreeExtent(node
);
233 if ((found
->IsData() && !node
->IsData())
234 || (!found
->IsData() && node
->IsData())) {
235 // this shouldn't happen as because different type has
236 // its different region for locating.
241 if (found
->End() < node
->End()) {
244 CachedExtent
* rightEmpty
= CachedExtent::Create(found
->End(),
245 node
->End() - found
->End(), node
->flags
);
246 _AddFreeExtent(rightEmpty
);
247 node
->length
-= node
->End() - found
->End();
250 if (found
->IsAllocated() == true) {
251 // free part of the allocated extents(found)
252 // |----- found ------|
254 uint64 diff
= node
->offset
- found
->offset
;
255 found
->offset
+= diff
+ node
->length
;
256 found
->length
-= diff
+ node
->length
;
257 // diff < 0 couldn't happen because of the Compare function
259 CachedExtent
* left
= CachedExtent::Create(node
->offset
- diff
,
261 _AddAllocatedExtent(left
);
264 if (found
->length
== 0)
265 _RemoveExtent(found
);
268 _CombineFreeExtent(node
);
276 CachedExtentTree::AddExtent(CachedExtent
* extent
)
278 if (extent
->IsAllocated() == true)
279 return _AddAllocatedExtent(extent
);
280 return _AddFreeExtent(extent
);
285 CachedExtentTree::_CombineFreeExtent(CachedExtent
* node
)
287 // node should be inserted first to call this function,
288 // otherwise it will overdelete.
289 if (node
->IsAllocated() == true)
292 CachedExtent
* other
= Next(node
);
294 if (node
->End() == other
->offset
&& node
->flags
== other
->flags
) {
295 node
->length
+= other
->length
;
296 _RemoveExtent(other
);
300 other
= Previous(node
);
302 if (other
->End() == node
->offset
&& node
->flags
== other
->flags
) {
303 other
->length
+= node
->length
;
311 CachedExtentTree::_DumpInOrder(CachedExtent
* node
) const
315 _DumpInOrder(_GetValue(node
->left
));
317 _DumpInOrder(_GetValue(node
->right
));
322 CachedExtentTree::DumpInOrder() const
325 _DumpInOrder(RootNode());
331 CachedExtentTree::Delete()
339 CachedExtentTree::_Delete(CachedExtent
* node
)
343 _Delete(_GetValue(node
->left
));
344 _Delete(_GetValue(node
->right
));
352 BlockGroup::BlockGroup(BTree
* extentTree
)
356 fCurrentExtentTree
= new(std::nothrow
) BTree(extentTree
->SystemVolume(),
357 extentTree
->RootBlock());
361 BlockGroup::~BlockGroup()
368 delete fCurrentExtentTree
;
369 fCurrentExtentTree
= NULL
;
374 BlockGroup::SetExtentTree(off_t rootAddress
)
376 status_t status
= fCurrentExtentTree
->SetRoot(rootAddress
, NULL
);
381 // re-allocate BlockGroup;
382 uint64 flags
= Flags();
383 return Initialize(flags
);
390 /* Initialize BlockGroup that has flag
393 BlockGroup::Initialize(uint64 flag
)
395 if (fCurrentExtentTree
== NULL
)
401 TRACE("BlockGroup::Initialize() find block group has flag: %"
402 B_PRIu64
"\n", flag
);
403 BTree::Path
path(fCurrentExtentTree
);
405 // find first item in extent to get the ObjectID
406 // ignore type because block_group is not always the first item
407 fKey
.SetType(BTRFS_KEY_TYPE_ANY
);
408 fCurrentExtentTree
->FindNext(&path
, fKey
, NULL
);
410 fKey
.SetType(BTRFS_KEY_TYPE_BLOCKGROUP_ITEM
);
415 status
= fCurrentExtentTree
->FindNext(&path
, fKey
, (void**)&fItem
);
416 if ((Flags() & flag
) == flag
|| status
!= B_OK
)
418 fKey
.SetObjectID(End());
423 ERROR("BlockGroup::Initialize() cannot find block group\n");
430 BlockGroup::LoadExtent(CachedExtentTree
* tree
, bool inverse
)
435 uint64 flags
= (Flags() & BTRFS_BLOCKGROUP_FLAG_DATA
) != 0 ?
436 BTRFS_EXTENT_FLAG_DATA
: BTRFS_EXTENT_FLAG_TREE_BLOCK
;
437 if (inverse
== false)
438 flags
|= BTRFS_EXTENT_FLAG_ALLOCATED
;
440 uint64 start
= Start();
441 CachedExtent
* insert
;
443 uint64 extentSize
= fCurrentExtentTree
->SystemVolume()->BlockSize();
446 key
.SetType(BTRFS_KEY_TYPE_METADATA_ITEM
);
447 if ((flags
& BTRFS_EXTENT_FLAG_DATA
) != 0)
448 key
.SetType(BTRFS_KEY_TYPE_EXTENT_ITEM
);
449 key
.SetObjectID(start
);
452 TreeIterator
iterator(fCurrentExtentTree
, key
);
455 status
= iterator
.GetNextEntry(&data
);
456 key
= iterator
.Key();
457 if (status
!= B_OK
) {
458 if (key
.ObjectID() != Start())
460 // When we couldn't find the item and key has
461 // objectid == BlockGroup's objectID, because
462 // key's type < BLOCKGROUP_ITEM
466 if (inverse
== false) {
467 if ((flags
& BTRFS_EXTENT_FLAG_DATA
) != 0)
468 extentSize
= key
.Offset();
469 insert
= CachedExtent::Create(key
.ObjectID(), extentSize
, flags
);
470 insert
->item
= (btrfs_extent
*)data
;
472 extentSize
= key
.ObjectID() - start
;
473 insert
= CachedExtent::Create(start
, extentSize
, flags
);
474 free(data
); // free extent doesn't have data;
476 _InsertExtent(tree
, insert
);
477 start
= key
.ObjectID() + extentSize
;
481 _InsertExtent(tree
, start
, End() - start
, flags
);
488 BlockGroup::_InsertExtent(CachedExtentTree
* tree
, uint64 start
, uint64 length
,
491 CachedExtent
* extent
= CachedExtent::Create(start
, length
, flags
);
492 return _InsertExtent(tree
, extent
);
497 BlockGroup::_InsertExtent(CachedExtentTree
* tree
, CachedExtent
* extent
)
499 if (extent
->length
== 0)
502 status_t status
= tree
->AddExtent(extent
);
503 if (status
!= B_OK
) {
513 ExtentAllocator::ExtentAllocator(Volume
* volume
)
520 fTree
= new(std::nothrow
) CachedExtentTree(TreeDefinition());
524 ExtentAllocator::~ExtentAllocator()
532 ExtentAllocator::_LoadExtentTree(uint64 flags
)
534 TRACE("ExtentAllocator::_LoadExtentTree() flags: %" B_PRIu64
"\n", flags
);
535 BlockGroup
blockGroup(fVolume
->ExtentTree());
536 status_t status
= blockGroup
.Initialize(flags
);
540 for (int i
= 0; i
< BTRFS_NUM_ROOT_BACKUPS
; i
++) {
541 uint64 extentRootAddr
=
542 fVolume
->SuperBlock().backup_roots
[i
].ExtentRoot();
543 if (extentRootAddr
== 0)
544 continue; // new device has 0 root address
546 status
= blockGroup
.SetExtentTree(extentRootAddr
);
549 status
= blockGroup
.LoadExtent(fTree
, false);
554 if (fTree
->IsEmpty()) // 4 backup roots is 0
557 uint64 lowerBound
= blockGroup
.Start();
558 uint64 upperBound
= blockGroup
.End();
559 status
= fTree
->FillFreeExtents(lowerBound
, upperBound
);
560 if (status
!= B_OK
) {
561 ERROR("ExtentAllocator::_LoadExtentTree() could not fill free extents"
562 "start %" B_PRIu64
" end %" B_PRIu64
"\n", lowerBound
,
567 if (fStart
> lowerBound
)
569 if (fEnd
< upperBound
)
576 ExtentAllocator::Initialize()
578 TRACE("ExtentAllocator::Initialize()\n");
582 status_t status
= _LoadExtentTree(BTRFS_BLOCKGROUP_FLAG_DATA
);
583 if (status
!= B_OK
) {
584 ERROR("ExtentAllocator:: could not load exent tree (data)\n");
588 status
= _LoadExtentTree(BTRFS_BLOCKGROUP_FLAG_SYSTEM
);
589 if (status
!= B_OK
) {
590 ERROR("ExtentAllocator:: could not load exent tree (system)\n");
594 status
= _LoadExtentTree(BTRFS_BLOCKGROUP_FLAG_METADATA
);
595 if (status
!= B_OK
) {
596 ERROR("ExtentAllocator:: could not load exent tree (metadata)\n");
600 fTree
->DumpInOrder();
605 /* Allocate extent that
606 * startwith or after "start" and has size >= "size"
607 * For now the allocating strategy is "first fit"
610 ExtentAllocator::_Allocate(uint64
& found
, uint64 start
, uint64 size
,
613 TRACE("ExtentAllocator::_Allocate() start %" B_PRIu64
" size %" B_PRIu64
" type %"
614 B_PRIu64
"\n", start
, size
, type
);
615 CachedExtent
* chosen
;
618 status
= fTree
->FindNext(&chosen
, start
, size
, type
);
622 if (TreeDefinition().Compare(start
, chosen
) == 0) {
623 if (chosen
->End() - start
>= size
) {
624 // if covered and have enough space for allocating
627 start
= chosen
->End(); // set new start and retry
629 start
= chosen
->offset
;
632 TRACE("ExtentAllocator::_Allocate() found %" B_PRIu64
"\n", start
);
633 chosen
= CachedExtent::Create(start
, size
,
634 type
| BTRFS_EXTENT_FLAG_ALLOCATED
);
635 status
= fTree
->AddExtent(chosen
);
644 // Allocate block for metadata
645 // flags is BLOCKGROUP's flags
647 ExtentAllocator::AllocateTreeBlock(uint64
& found
, uint64 start
, uint64 flags
)
649 // TODO: implement more features here with flags, e.g DUP, RAID, etc
651 BlockGroup
blockGroup(fVolume
->ExtentTree());
652 status_t status
= blockGroup
.Initialize(flags
);
655 if (start
== (uint64
)-1)
656 start
= blockGroup
.Start();
659 uint64 remainder
= start
% fVolume
->BlockSize();
661 start
+= fVolume
->BlockSize() - remainder
;
663 status
= _Allocate(found
, start
, fVolume
->BlockSize(),
664 BTRFS_EXTENT_FLAG_TREE_BLOCK
);
668 // check here because tree block locate in 2 blockgroups (system and
669 // metadata), and there might be a case one can get over the limit.
670 if (found
>= blockGroup
.End())
676 // Allocate block for file data
678 ExtentAllocator::AllocateDataBlock(uint64
& found
, uint64 size
, uint64 start
,
681 // TODO: implement more features here with flags, e.g DUP, RAID, etc
683 if (start
== (uint64
)-1) {
684 BlockGroup
blockGroup(fVolume
->ExtentTree());
685 status_t status
= blockGroup
.Initialize(flags
);
688 start
= blockGroup
.Start();
692 uint64 remainder
= start
% fVolume
->SectorSize();
694 start
+= fVolume
->SectorSize() - remainder
;
695 size
= size
/ fVolume
->SectorSize() * fVolume
->SectorSize();
697 return _Allocate(found
, start
, size
, BTRFS_EXTENT_FLAG_DATA
);