BTRFS: Implement CopyOnWrite relevant functions, and BTree holds new attribute that record its root level.
CopyOnWrite works like this: * Cache original node * Allocating new block * Cache new block to be writable * Copy original node to new node, and changing. Also if a node is already be COW-ed it cannot be COW-ed again, it will be changed in-place instead. InternalCopy does CopyOnWrite all the nodes that we don't need to change anything on them. Signed-off-by: Augustin Cavalier <[email protected]>
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
|
||||
|
||||
#include "BTree.h"
|
||||
#include "Journal.h"
|
||||
|
||||
|
||||
//#define TRACE_BTRFS
|
||||
@@ -419,12 +420,131 @@ BTree::Path::GetEntry(int slot, btrfs_key* _key, void** _value, uint32* _size,
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Allocate and copy block and do all the changes that it can.
|
||||
* for now, we only copy-on-write tree block,
|
||||
* file data is "nocow" by default.
|
||||
*
|
||||
* o parent o
|
||||
* | ===> \
|
||||
* o x o
|
||||
*/
|
||||
status_t
|
||||
BTree::Path::CopyOnWrite(Transaction& transaction, int level, uint32 start,
|
||||
int num, int length)
|
||||
{
|
||||
Node* node = fNodes[level];
|
||||
if (node == NULL)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
status_t status;
|
||||
if (transaction.HasBlock(node->BlockNum())) {
|
||||
// cow-ed block can not be cow-ed again
|
||||
status = node->MoveEntries(start, start + num - 1, length);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
node->SetGeneration(transaction.SystemID());
|
||||
if (length < 0)
|
||||
node->SetItemCount(node->ItemCount() - num);
|
||||
else if (length > 0)
|
||||
node->SetItemCount(node->ItemCount() + num);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
uint64 address;
|
||||
fsblock_t block;
|
||||
status = fTree->SystemVolume()->GetNewBlock(address, block);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
fNodes[level] = new(std::nothrow) BTree::Node(fTree->SystemVolume());
|
||||
if (fNodes[level] == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
fNodes[level]->SetToWritable(block, transaction.ID(), true);
|
||||
|
||||
status = fNodes[level]->Copy(node, start, start + num - 1, length);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
fNodes[level]->SetGeneration(transaction.SystemID());
|
||||
fNodes[level]->SetLogicalAddress(address);
|
||||
if (length < 0)
|
||||
fNodes[level]->SetItemCount(node->ItemCount() - num);
|
||||
else if (length > 0)
|
||||
fNodes[level]->SetItemCount(node->ItemCount() + num);
|
||||
else
|
||||
fNodes[level]->SetItemCount(num);
|
||||
|
||||
// change pointer of this node in parent
|
||||
int parentSlot;
|
||||
Node* parentNode = GetNode(level + 1, &parentSlot);
|
||||
if (parentNode != NULL)
|
||||
parentNode->Index(parentSlot)->SetLogicalAddress(address);
|
||||
|
||||
if (level == fTree->RootLevel())
|
||||
fTree->SetRoot(fNodes[level]);
|
||||
|
||||
delete node;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/* Copy-On-Write all internal nodes start from a specific level.
|
||||
* level > 0: to root
|
||||
* level <= 0: to leaf
|
||||
*
|
||||
* path cow-path path cow-path
|
||||
* =================================================
|
||||
* root cow-root root level < 0
|
||||
* | | |
|
||||
* n1 cow-n1 ...______
|
||||
* | | | \
|
||||
* n2 cow-n2 n1 cow-n1
|
||||
* | / | |
|
||||
* ...____/ n2 cow-n2
|
||||
* | | |
|
||||
* leaf level > 0 leaf cow-leaf
|
||||
*/
|
||||
status_t
|
||||
BTree::Path::InternalCopy(Transaction& transaction, int level)
|
||||
{
|
||||
if (std::abs(level) >= fTree->RootLevel())
|
||||
return B_OK;
|
||||
|
||||
TRACE("Path::InternalCopy() level %i\n", level);
|
||||
int from, to;
|
||||
if (level > 0) {
|
||||
from = level;
|
||||
to = fTree->RootLevel();
|
||||
} else {
|
||||
from = 0;
|
||||
to = std::abs(level);
|
||||
}
|
||||
|
||||
Node* node = NULL;
|
||||
status_t status;
|
||||
while (from <= to) {
|
||||
node = fNodes[from];
|
||||
status = CopyOnWrite(transaction, from, 0, node->ItemCount(), 0);
|
||||
from++;
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - BTree implementation
|
||||
|
||||
|
||||
BTree::BTree(Volume* volume)
|
||||
:
|
||||
fRootBlock(0),
|
||||
fRootLevel(0),
|
||||
fVolume(volume)
|
||||
{
|
||||
mutex_init(&fIteratorLock, "btrfs b+tree iterator");
|
||||
@@ -434,6 +554,7 @@ BTree::BTree(Volume* volume)
|
||||
BTree::BTree(Volume* volume, btrfs_stream* stream)
|
||||
:
|
||||
fRootBlock(0),
|
||||
fRootLevel(0),
|
||||
fVolume(volume)
|
||||
{
|
||||
mutex_init(&fIteratorLock, "btrfs b+tree iterator");
|
||||
@@ -660,23 +781,39 @@ BTree::NextLeaf(Path* path) const
|
||||
}
|
||||
|
||||
|
||||
/* Set root infomation, to use this function root must be valid and
|
||||
* exists on disk.
|
||||
*/
|
||||
status_t
|
||||
BTree::SetRoot(off_t logical, fsblock_t* block)
|
||||
{
|
||||
if (block != NULL) {
|
||||
fRootBlock = *block;
|
||||
//TODO: mapping physical block -> logical address
|
||||
} else {
|
||||
fLogicalRoot = logical;
|
||||
if (fVolume->FindBlock(logical, fRootBlock) != B_OK) {
|
||||
ERROR("Find() unmapped block %" B_PRId64 "\n", fRootBlock);
|
||||
return B_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
btrfs_header header;
|
||||
read_pos(fVolume->Device(), fRootBlock * fVolume->BlockSize(), &header,
|
||||
sizeof(btrfs_header));
|
||||
fRootLevel = header.Level();
|
||||
fLogicalRoot = header.LogicalAddress();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
BTree::SetRoot(Node* root)
|
||||
{
|
||||
fRootBlock = root->BlockNum();
|
||||
fLogicalRoot = root->LogicalAddress();
|
||||
fRootLevel = root->Level();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
BTree::_AddIterator(TreeIterator* iterator)
|
||||
{
|
||||
|
||||
@@ -27,6 +27,9 @@ enum btree_traversing {
|
||||
};
|
||||
|
||||
|
||||
class Transaction;
|
||||
|
||||
|
||||
// #pragma mark - in-memory structures
|
||||
|
||||
|
||||
@@ -44,6 +47,7 @@ struct node_and_key {
|
||||
class BTree {
|
||||
public:
|
||||
class Path;
|
||||
class Node;
|
||||
|
||||
public:
|
||||
BTree(Volume* volume);
|
||||
@@ -71,8 +75,10 @@ public:
|
||||
|
||||
Volume* SystemVolume() const { return fVolume; }
|
||||
status_t SetRoot(off_t logical, fsblock_t* block);
|
||||
void SetRoot(Node* root);
|
||||
fsblock_t RootBlock() const { return fRootBlock; }
|
||||
off_t LogicalRoot() const { return fLogicalRoot; }
|
||||
uint8 RootLevel() const { return fRootLevel; }
|
||||
|
||||
private:
|
||||
BTree(const BTree& other);
|
||||
@@ -90,6 +96,7 @@ private:
|
||||
|
||||
fsblock_t fRootBlock;
|
||||
off_t fLogicalRoot;
|
||||
uint8 fRootLevel;
|
||||
Volume* fVolume;
|
||||
mutex fIteratorLock;
|
||||
SinglyLinkedList<TreeIterator> fIterators;
|
||||
@@ -114,6 +121,12 @@ public:
|
||||
{ return fNode->header.ItemCount(); }
|
||||
uint8 Level() const
|
||||
{ return fNode->header.Level(); }
|
||||
void SetLogicalAddress(uint64 address)
|
||||
{ fNode->header.SetLogicalAddress(address); }
|
||||
void SetGeneration(uint64 generation)
|
||||
{ fNode->header.SetGeneration(generation); }
|
||||
void SetItemCount(uint32 itemCount)
|
||||
{ fNode->header.SetItemCount(itemCount); }
|
||||
|
||||
btrfs_index* Index(uint32 i) const
|
||||
{ return &fNode->index[i]; }
|
||||
@@ -171,6 +184,10 @@ public:
|
||||
|
||||
int Move(int level, int step);
|
||||
|
||||
status_t CopyOnWrite(Transaction& transaction, int level,
|
||||
uint32 start, int num, int length);
|
||||
status_t InternalCopy(Transaction& transaction, int level);
|
||||
|
||||
BTree* Tree() const { return fTree; }
|
||||
private:
|
||||
Path(const Path&);
|
||||
|
||||
@@ -110,6 +110,13 @@ struct btrfs_header {
|
||||
uint32 ItemCount() const
|
||||
{ return B_LENDIAN_TO_HOST_INT32(item_count); }
|
||||
uint8 Level() const { return level; }
|
||||
|
||||
void SetLogicalAddress(uint64 logical)
|
||||
{ logical_address = B_HOST_TO_LENDIAN_INT64(logical); }
|
||||
void SetGeneration(uint64 gen)
|
||||
{ generation = B_HOST_TO_LENDIAN_INT64(gen); }
|
||||
void SetItemCount(uint32 itemCount)
|
||||
{ item_count = B_HOST_TO_LENDIAN_INT32(itemCount); }
|
||||
} _PACKED;
|
||||
|
||||
|
||||
@@ -121,6 +128,11 @@ struct btrfs_index {
|
||||
{ return B_LENDIAN_TO_HOST_INT64(logical_address); }
|
||||
uint64 Generation() const
|
||||
{ return B_LENDIAN_TO_HOST_INT64(generation); }
|
||||
|
||||
void SetLogicalAddress(uint64 address)
|
||||
{ logical_address = B_HOST_TO_LENDIAN_INT64(address); }
|
||||
void SetGeneration(uint64 gen)
|
||||
{ generation = B_HOST_TO_LENDIAN_INT64(gen); }
|
||||
} _PACKED;
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user