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:
hyche
2017-12-10 11:02:59 -05:00
committed by Augustin Cavalier
parent 89484dc08d
commit 883b9bf29f
3 changed files with 168 additions and 2 deletions
+139 -2
View File
@@ -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;