BTRFS: Implement BTree::Path and change _Find.

Remove attribute fCurrentSlot in BTree::Node as it will be handled by Path explicitly. BTree control Path by passing its type in
BTree's method, Path also hold BTree type as its attribute to do some internal actions.
Add constant BTREE_KEY_TYPE_ANY, find search key has this type will success regardless of the found key's type.

Split the  the _Find function into Traverse and GetEntry. Traverse will fill in the Path (nodes and slots) along way its finding,
GetEntry will get the item data, item size, key from leaf, if the slot is valid, that we have from Traverse. The _Find function also
check type if is correct and then retrieve. Doing this way there will be more flexible, the "read" flag is not needed as we only
need Path to manipulate tree, and it also enhance the performance at some points, because Path caches all the nodes from root to leaf,
so that we don't have to block_cache_put and block_cache_get after each finding.

Signed-off-by: Augustin Cavalier <waddlesplash@gmail.com>
This commit is contained in:
hyche 2017-08-13 00:30:06 +07:00 committed by Augustin Cavalier
parent 8160f31fc1
commit 3216460dec
7 changed files with 337 additions and 89 deletions

View File

@ -151,10 +151,11 @@ Attribute::_Lookup(const char* name, size_t nameLength,
key.SetType(BTRFS_KEY_TYPE_XATTR_ITEM);
key.SetObjectID(fInode->ID());
key.SetOffset(hash);
BTree::Path path(fInode->GetVolume()->FSTree());
btrfs_dir_entry* entries;
uint32 length;
status_t status = fInode->GetVolume()->FSTree()->FindExact(key,
status_t status = fInode->GetVolume()->FSTree()->FindExact(&path, key,
(void**)&entries, &length);
if (status != B_OK) {
TRACE("AttributeIterator::Lookup(): Couldn't find entry with hash %"

View File

@ -25,7 +25,6 @@ BTree::Node::Node(Volume* volume)
fNode(NULL),
fVolume(volume),
fBlockNumber(0),
fCurrentSlot(0),
fWritable(false)
{
}
@ -36,7 +35,6 @@ BTree::Node::Node(Volume* volume, off_t block)
fNode(NULL),
fVolume(volume),
fBlockNumber(0),
fCurrentSlot(0),
fWritable(false)
{
SetTo(block);
@ -142,7 +140,109 @@ BTree::Node::SearchSlot(const btrfs_key& key, int* slot, btree_traversing type)
}
//-pragma mark
// #pragma mark - BTree::Path implementation
BTree::Path::Path(BTree* tree)
:
fTree(tree)
{
for (int i = 0; i < BTRFS_MAX_TREE_DEPTH; ++i) {
fNodes[i] = NULL;
fSlots[i] = 0;
}
}
BTree::Path::~Path()
{
for (int i = 0; i < BTRFS_MAX_TREE_DEPTH; ++i) {
delete fNodes[i];
fNodes[i] = NULL;
fSlots[i] = 0;
}
}
BTree::Node*
BTree::Path::GetNode(int level, int* _slot) const
{
if (_slot != NULL)
*_slot = fSlots[level];
return fNodes[level];
}
BTree::Node*
BTree::Path::SetNode(off_t block, int slot)
{
Node node(fTree->SystemVolume(), block);
return SetNode(&node, slot);
}
BTree::Node*
BTree::Path::SetNode(const Node* node, int slot)
{
uint8 level = node->Level();
if (fNodes[level] == NULL) {
fNodes[level] = new Node(fTree->SystemVolume(), node->BlockNum());
if (fNodes[level] == NULL)
return NULL;
} else
fNodes[level]->SetTo(node->BlockNum());
if (slot == -1)
fSlots[level] = fNodes[level]->ItemCount() - 1;
else
fSlots[level] = slot;
return fNodes[level];
}
int
BTree::Path::Move(int level, int step)
{
fSlots[level] += step;
if (fSlots[level] < 0)
return -1;
if (fSlots[level] >= fNodes[level]->ItemCount())
return 1;
return 0;
}
status_t
BTree::Path::GetEntry(int slot, btrfs_key* _key, void** _value, uint32* _size,
uint32* _offset)
{
BTree::Node* leaf = fNodes[0];
if (slot < 0 || slot >= leaf->ItemCount())
return B_ENTRY_NOT_FOUND;
if (_key != NULL)
*_key = leaf->Item(slot)->key;
uint32 itemSize = leaf->Item(slot)->Size();
if (_value != NULL) {
*_value = malloc(itemSize);
if (*_value == NULL)
return B_NO_MEMORY;
memcpy(*_value, leaf->ItemData(slot), itemSize);
}
if (_size != NULL)
*_size = itemSize;
if (_offset != NULL)
*_offset = leaf->Item(slot)->Offset();
return B_OK;
}
// #pragma mark - BTree implementation
BTree::BTree(Volume* volume)
@ -206,85 +306,180 @@ btrfs_key::Compare(const btrfs_key& key) const
}
/* Traverse from root to fill in the path along way its finding.
* Return current slot at leaf if successful.
*/
status_t
BTree::Traverse(btree_traversing type, Path* path, const btrfs_key& key)
const
{
TRACE("BTree::Traverse() objectid %" B_PRId64 " type %d offset %"
B_PRId64 " \n", key.ObjectID(), key.Type(), key.Offset());
fsblock_t physicalBlock = fRootBlock;
Node node(fVolume, physicalBlock);
int slot;
status_t status = B_OK;
while (node.Level() != 0) {
TRACE("BTree::Traverse() level %d count %d\n", node.Level(),
node.ItemCount());
status = node.SearchSlot(key, &slot, BTREE_BACKWARD);
if (status != B_OK)
return status;
if (path->SetNode(&node, slot) == NULL)
return B_NO_MEMORY;
TRACE("BTree::Traverse() getting index %" B_PRIu32 "\n", slot);
status = fVolume->FindBlock(node.Index(slot)->LogicalAddress(),
physicalBlock);
if (status != B_OK) {
ERROR("BTree::Traverse() unmapped block %" B_PRId64 "\n",
node.Index(slot)->LogicalAddress());
return status;
}
node.SetTo(physicalBlock);
}
TRACE("BTree::Traverse() dump count %" B_PRId32 "\n", node.ItemCount());
status = node.SearchSlot(key, &slot, type);
if (status != B_OK)
return status;
if (path->SetNode(&node, slot) == NULL)
return B_NO_MEMORY;
TRACE("BTree::Traverse() found %" B_PRIu32 " %" B_PRIu32 "\n",
node.Item(slot)->Offset(), node.Item(slot)->Size());
return slot;
}
/*! Searches the key in the tree, and stores the allocated found item in
_value, if successful.
Returns B_OK when the key could be found, B_ENTRY_NOT_FOUND if not.
It can also return other errors to indicate that something went wrong.
*/
status_t
BTree::_Find(btrfs_key& key, void** _value, uint32* _size,
bool read, btree_traversing type)
BTree::_Find(Path* path, btrfs_key& wanted, void** _value, uint32* _size,
uint32* _offset, btree_traversing type) const
{
TRACE("Find() objectid %" B_PRId64 " type %d offset %" B_PRId64 " \n",
key.ObjectID(), key.Type(), key.Offset());
BTree::Node node(fVolume, fRootBlock);
int slot, ret;
fsblock_t physicalBlock;
status_t status = Traverse(type, path, wanted);
if (status < B_OK)
return status;
while (node.Level() != 0) {
TRACE("Find() level %d\n", node.Level());
ret = node.SearchSlot(key, &slot, BTREE_BACKWARD);
if (ret != B_OK)
return ret;
TRACE("Find() getting index %" B_PRIu32 "\n", slot);
btrfs_key found;
status = path->GetCurrentEntry(&found, _value, _size, _offset);
if (status != B_OK)
return status;
if (fVolume->FindBlock(node.Index(slot)->LogicalAddress(), physicalBlock)
!= B_OK) {
ERROR("Find() unmapped block %" B_PRId64 "\n",
node.Index(slot)->LogicalAddress());
return B_ERROR;
}
node.SetTo(physicalBlock);
}
TRACE("Find() dump count %" B_PRId32 "\n", node.ItemCount());
ret = node.SearchSlot(key, &slot, type);
if ((slot >= node.ItemCount() || node.Item(slot)->key.Type() != key.Type())
&& read == true
|| ret != B_OK) {
TRACE("Find() not found %" B_PRId64 " %" B_PRId64 "\n", key.Offset(),
key.ObjectID());
if (found.Type() != wanted.Type() && wanted.Type() != BTRFS_KEY_TYPE_ANY)
return B_ENTRY_NOT_FOUND;
}
if (read == true) {
TRACE("Find() found %" B_PRIu32 " %" B_PRIu32 "\n",
node.Item(slot)->Offset(), node.Item(slot)->Size());
if (_value != NULL) {
*_value = malloc(node.Item(slot)->Size());
memcpy(*_value, node.ItemData(slot),
node.Item(slot)->Size());
key.SetOffset(node.Item(slot)->key.Offset());
key.SetObjectID(node.Item(slot)->key.ObjectID());
if (_size != NULL)
*_size = node.Item(slot)->Size();
}
} else {
*_value = (void*)&slot;
}
wanted = found;
return B_OK;
}
status_t
BTree::FindNext(btrfs_key& key, void** _value, uint32* _size, bool read)
BTree::FindNext(Path* path, btrfs_key& key, void** _value, uint32* _size,
uint32* _offset) const
{
return _Find(key, _value, _size, read, BTREE_FORWARD);
return _Find(path, key, _value, _size, _offset, BTREE_FORWARD);
}
status_t
BTree::FindPrevious(btrfs_key& key, void** _value, uint32* _size, bool read)
BTree::FindPrevious(Path* path, btrfs_key& key, void** _value, uint32* _size,
uint32* _offset) const
{
return _Find(key, _value, _size, read, BTREE_BACKWARD);
return _Find(path, key, _value, _size, _offset, BTREE_BACKWARD);
}
status_t
BTree::FindExact(btrfs_key& key, void** _value, uint32* _size, bool read)
BTree::FindExact(Path* path, btrfs_key& key, void** _value, uint32* _size,
uint32* _offset) const
{
return _Find(key, _value, _size, read, BTREE_EXACT);
return _Find(path, key, _value, _size, _offset, BTREE_EXACT);
}
status_t
BTree::PreviousLeaf(Path* path) const
{
// TODO: use Traverse() ???
int level = 0;
int slot;
Node* node = NULL;
// iterate to the root until satisfy the condition
while (true) {
node = path->GetNode(level, &slot);
if (node == NULL || slot != 0)
break;
level++;
}
// the current leaf is already the left most leaf or
// path was not initialized
if (node == NULL)
return B_ENTRY_NOT_FOUND;
path->Move(level, BTREE_BACKWARD);
fsblock_t physicalBlock;
// change all nodes below this level and slot to the ending
do {
status_t status = fVolume->FindBlock(
node->Index(slot)->LogicalAddress(), physicalBlock);
if (status != B_OK)
return status;
node = path->SetNode(physicalBlock, -1);
if (node == NULL)
return B_NO_MEMORY;
slot = node->ItemCount() - 1;
level--;
} while(level != 0);
return B_OK;
}
status_t
BTree::NextLeaf(Path* path) const
{
int level = 0;
int slot;
Node* node = NULL;
// iterate to the root until satisfy the condition
while (true) {
node = path->GetNode(level, &slot);
if (node == NULL || slot < node->ItemCount() - 1)
break;
level++;
}
// the current leaf is already the right most leaf or
// path was not initialized
if (node == NULL)
return B_ENTRY_NOT_FOUND;
path->Move(level, BTREE_FORWARD);
fsblock_t physicalBlock;
// change all nodes below this level and slot to the beginning
do {
status_t status = fVolume->FindBlock(
node->Index(slot)->LogicalAddress(), physicalBlock);
if (status != B_OK)
return status;
node = path->SetNode(physicalBlock, 0);
if (node == NULL)
return B_NO_MEMORY;
slot = 0;
level--;
} while(level != 0);
return B_OK;
}
@ -351,8 +546,8 @@ TreeIterator::Traverse(btree_traversing direction, btrfs_key& key,
return B_INTERRUPTED;
fCurrentKey.SetOffset(fCurrentKey.Offset() + direction);
status_t status = fTree->_Find(fCurrentKey, value, size,
true, direction);
BTree::Path path(fTree);
status_t status = fTree->_Find(&path, fCurrentKey, value, size, direction);
if (status != B_OK) {
TRACE("TreeIterator::Traverse() Find failed\n");
return B_ENTRY_NOT_FOUND;

View File

@ -42,6 +42,9 @@ struct node_and_key {
class BTree {
public:
class Path;
public:
BTree(Volume* volume);
BTree(Volume* volume,
@ -49,15 +52,24 @@ public:
BTree(Volume* volume,
fsblock_t rootBlock);
~BTree();
status_t FindExact(btrfs_key& key, void** value,
uint32* size = NULL, bool read = true);
status_t FindNext(btrfs_key& key, void** value,
uint32* size = NULL, bool read = true);
status_t FindPrevious(btrfs_key& key, void** value,
uint32* size = NULL, bool read = true);
status_t FindExact(Path* path, btrfs_key& key,
void** _value, uint32* _size = NULL,
uint32* _offset = NULL) const;
status_t FindNext(Path* path, btrfs_key& key,
void** _value, uint32* _size = NULL,
uint32* _offset = NULL) const;
status_t FindPrevious(Path* path, btrfs_key& key,
void** _value, uint32* _size = NULL,
uint32* _offset = NULL) const;
status_t Traverse(btree_traversing type, Path* path,
const btrfs_key& key) const;
status_t PreviousLeaf(Path* path) const;
status_t NextLeaf(Path* path) const;
Volume* SystemVolume() const { return fVolume; }
status_t SetRoot(off_t logical, fsblock_t* block);
fsblock_t RootBlock() const { return fRootBlock; }
off_t LogicalRoot() const { return fLogicalRoot; }
@ -67,8 +79,10 @@ private:
BTree& operator=(const BTree& other);
// no implementation
status_t _Find(btrfs_key& key, void** value, uint32* size,
bool read, btree_traversing type);
status_t _Find(Path* path, btrfs_key& key,
void** _value, uint32* _size,
uint32* _offset, btree_traversing type)
const;
void _AddIterator(TreeIterator* iterator);
void _RemoveIterator(TreeIterator* iterator);
private:
@ -113,8 +127,7 @@ public:
void Unset();
void SetTo(off_t block);
void SetToWritable(off_t block,
int32 transactionId, bool empty);
void SetToWritable(off_t block, int32 transactionId, bool empty);
off_t BlockNum() const { return fBlockNumber;}
bool IsWritable() const { return fWritable; }
@ -129,18 +142,33 @@ public:
btrfs_stream* fNode;
Volume* fVolume;
off_t fBlockNumber;
uint32 fCurrentSlot;
bool fWritable;
};
class Path {
public:
Path();
Path(BTree* tree);
~Path();
Node* GetNode(int level, int* _slot = NULL) const;
Node* SetNode(off_t block, int slot);
Node* SetNode(const Node* node, int slot);
status_t GetCurrentEntry(btrfs_key* _key, void** _value,
uint32* _size = NULL, uint32* _offset = NULL);
status_t GetEntry(int slot, btrfs_key* _key, void** _value,
uint32* _size = NULL, uint32* _offset = NULL);
int Move(int level, int step);
BTree* Tree() const { return fTree; }
private:
Path(const Path&);
Path operator=(const Path&);
Node* nodes[BTRFS_MAX_TREE_DEPTH];
private:
Node* fNodes[BTRFS_MAX_TREE_DEPTH];
int fSlots[BTRFS_MAX_TREE_DEPTH];
BTree* fTree;
};
}; // class BTree
@ -176,6 +204,17 @@ private:
};
// #pragma mark - BTree::Path inline functions
inline status_t
BTree::Path::GetCurrentEntry(btrfs_key* _key, void** _value, uint32* _size,
uint32* _offset)
{
return GetEntry(fSlots[0], _key, _value, _size, _offset);
}
// #pragma mark - TreeIterator inline functions

View File

@ -123,10 +123,11 @@ DirectoryIterator::Lookup(const char* name, size_t nameLength, ino_t* _id)
key.SetType(BTRFS_KEY_TYPE_DIR_ITEM);
key.SetObjectID(fInode->ID());
key.SetOffset(hash);
BTree::Path path(fInode->GetVolume()->FSTree());
btrfs_dir_entry* entries;
uint32 length;
status_t status = fInode->GetVolume()->FSTree()->FindExact(key,
status_t status = fInode->GetVolume()->FSTree()->FindExact(&path, key,
(void**)&entries, &length);
if (status != B_OK) {
TRACE("DirectoryIterator::Lookup(): Couldn't find entry with hash %" B_PRIu32

View File

@ -78,9 +78,11 @@ Inode::UpdateNodeFromDisk()
search_key.SetType(BTRFS_KEY_TYPE_INODE_ITEM);
search_key.SetObjectID(fID);
search_key.SetOffset(0);
BTree::Path path(fVolume->FSTree());
btrfs_inode* node;
if (fVolume->FSTree()->FindExact(search_key, (void**)&node) != B_OK) {
if (fVolume->FSTree()->FindExact(&path, search_key, (void**)&node)
!= B_OK) {
ERROR("Inode::UpdateNodeFromDisk(): Couldn't find inode %"
B_PRIdINO "\n", fID);
return B_ENTRY_NOT_FOUND;
@ -111,9 +113,10 @@ Inode::FindBlock(off_t pos, off_t& physical, off_t* _length)
search_key.SetType(BTRFS_KEY_TYPE_EXTENT_DATA);
search_key.SetObjectID(fID);
search_key.SetOffset(pos + 1);
BTree::Path path(fVolume->FSTree());
btrfs_extent_data* extent_data;
status_t status = fVolume->FSTree()->FindPrevious(search_key,
status_t status = fVolume->FSTree()->FindPrevious(&path, search_key,
(void**)&extent_data);
if (status != B_OK) {
ERROR("Inode::FindBlock(): Couldn't find extent_data 0x%" B_PRIx32
@ -166,10 +169,11 @@ Inode::ReadAt(off_t pos, uint8* buffer, size_t* _length)
search_key.SetType(BTRFS_KEY_TYPE_EXTENT_DATA);
search_key.SetObjectID(fID);
search_key.SetOffset(pos + 1);
BTree::Path path(fVolume->FSTree());
uint32 item_size;
btrfs_extent_data* extent_data;
status_t status = fVolume->FSTree()->FindPrevious(search_key,
status_t status = fVolume->FSTree()->FindPrevious(&path, search_key,
(void**)&extent_data, &item_size);
if (status != B_OK) {
ERROR("Inode::FindBlock(): Couldn't find extent_data 0x%" B_PRIx32
@ -292,9 +296,10 @@ Inode::FindParent(ino_t* id)
search_key.SetType(BTRFS_KEY_TYPE_INODE_REF);
search_key.SetObjectID(fID);
search_key.SetOffset(-1);
BTree::Path path(fVolume->FSTree());
void* node_ref;
if (fVolume->FSTree()->FindPrevious(search_key, &node_ref) != B_OK) {
if (fVolume->FSTree()->FindPrevious(&path, search_key, &node_ref) != B_OK) {
ERROR("Inode::FindParent(): Couldn't find inode for %" B_PRIdINO "\n",
fID);
return B_ERROR;

View File

@ -317,15 +317,18 @@ Volume::Mount(const char* deviceName, uint32 flags)
TRACE("Volume::Mount() root: %" B_PRIu64 " (physical block %" B_PRIu64 ")\n",
fSuperBlock.Root(), fRootTree->RootBlock());
BTree::Path path(fRootTree);
TRACE("Volume::Mount(): Searching extent root\n");
btrfs_key search_key;
search_key.SetOffset(0);
search_key.SetType(BTRFS_KEY_TYPE_ROOT_ITEM);
search_key.SetObjectID(BTRFS_OBJECT_ID_EXTENT_TREE);
btrfs_root* root;
if (fRootTree->FindExact(search_key, (void**)&root) != B_OK) {
status = fRootTree->FindExact(&path, search_key, (void**)&root);
if (status != B_OK) {
ERROR("Volume::Mount(): Couldn't find extent root\n");
return B_ERROR;
return status;
}
TRACE("Volume::Mount(): Found extent root: %" B_PRIu64 "\n",
root->LogicalAddress());
@ -338,9 +341,10 @@ Volume::Mount(const char* deviceName, uint32 flags)
TRACE("Volume::Mount(): Searching fs root\n");
search_key.SetOffset(0);
search_key.SetObjectID(BTRFS_OBJECT_ID_FS_TREE);
if (fRootTree->FindExact(search_key, (void**)&root) != B_OK) {
status = fRootTree->FindExact(&path, search_key, (void**)&root);
if (status != B_OK) {
ERROR("Volume::Mount(): Couldn't find fs root\n");
return B_ERROR;
return status;
}
TRACE("Volume::Mount(): Found fs root: %" B_PRIu64 "\n",
root->LogicalAddress());
@ -353,9 +357,10 @@ Volume::Mount(const char* deviceName, uint32 flags)
TRACE("Volume::Mount(): Searching dev root\n");
search_key.SetOffset(0);
search_key.SetObjectID(BTRFS_OBJECT_ID_DEV_TREE);
if (fRootTree->FindExact(search_key, (void**)&root) != B_OK) {
status = fRootTree->FindExact(&path, search_key, (void**)&root);
if (status != B_OK) {
ERROR("Volume::Mount(): Couldn't find dev root\n");
return B_ERROR;
return status;
}
TRACE("Volume::Mount(): Found dev root: %" B_PRIu64 "\n",
root->LogicalAddress());
@ -368,9 +373,10 @@ Volume::Mount(const char* deviceName, uint32 flags)
TRACE("Volume::Mount(): Searching checksum root\n");
search_key.SetOffset(0);
search_key.SetObjectID(BTRFS_OBJECT_ID_CHECKSUM_TREE);
if (fRootTree->FindExact(search_key, (void**)&root) != B_OK) {
status = fRootTree->FindExact(&path, search_key, (void**)&root);
if (status != B_OK) {
ERROR("Volume::Mount(): Couldn't find checksum root\n");
return B_ERROR;
return status;
}
TRACE("Volume::Mount(): Found checksum root: %" B_PRIu64 "\n",
root->LogicalAddress());
@ -484,11 +490,11 @@ Volume::FindBlock(off_t logical, off_t& physical)
btrfs_key search_key;
search_key.SetOffset(logical);
search_key.SetType(BTRFS_KEY_TYPE_CHUNK_ITEM);
search_key.SetObjectID(BTRFS_OBJECT_ID_FIRST_CHUNK_TREE);
search_key.SetObjectID(BTRFS_OBJECT_ID_FIRST_CHUNK_TREE);
btrfs_chunk* chunk;
uint32 chunk_length;
status_t status = fChunkTree->FindPrevious(search_key, (void**)&chunk,
&chunk_length);
BTree::Path path(fChunkTree);
status_t status = fChunkTree->FindPrevious(&path, search_key,
(void**)&chunk);
if (status != B_OK)
return status;

View File

@ -427,6 +427,7 @@ struct btrfs_extent_data_ref {
#define BTRFS_OBJECT_ID_CHECKSUM_TREE 7
#define BTRFS_OBJECT_ID_FIRST_CHUNK_TREE 256
#define BTRFS_KEY_TYPE_ANY 0
#define BTRFS_KEY_TYPE_INODE_ITEM 1
#define BTRFS_KEY_TYPE_INODE_REF 12
#define BTRFS_KEY_TYPE_XATTR_ITEM 24