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 "BTree.h"
|
||||||
|
#include "Journal.h"
|
||||||
|
|
||||||
|
|
||||||
//#define TRACE_BTRFS
|
//#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
|
// #pragma mark - BTree implementation
|
||||||
|
|
||||||
|
|
||||||
BTree::BTree(Volume* volume)
|
BTree::BTree(Volume* volume)
|
||||||
:
|
:
|
||||||
fRootBlock(0),
|
fRootBlock(0),
|
||||||
|
fRootLevel(0),
|
||||||
fVolume(volume)
|
fVolume(volume)
|
||||||
{
|
{
|
||||||
mutex_init(&fIteratorLock, "btrfs b+tree iterator");
|
mutex_init(&fIteratorLock, "btrfs b+tree iterator");
|
||||||
@@ -434,6 +554,7 @@ BTree::BTree(Volume* volume)
|
|||||||
BTree::BTree(Volume* volume, btrfs_stream* stream)
|
BTree::BTree(Volume* volume, btrfs_stream* stream)
|
||||||
:
|
:
|
||||||
fRootBlock(0),
|
fRootBlock(0),
|
||||||
|
fRootLevel(0),
|
||||||
fVolume(volume)
|
fVolume(volume)
|
||||||
{
|
{
|
||||||
mutex_init(&fIteratorLock, "btrfs b+tree iterator");
|
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
|
status_t
|
||||||
BTree::SetRoot(off_t logical, fsblock_t* block)
|
BTree::SetRoot(off_t logical, fsblock_t* block)
|
||||||
{
|
{
|
||||||
if (block != NULL) {
|
if (block != NULL) {
|
||||||
fRootBlock = *block;
|
fRootBlock = *block;
|
||||||
//TODO: mapping physical block -> logical address
|
|
||||||
} else {
|
} else {
|
||||||
fLogicalRoot = logical;
|
|
||||||
if (fVolume->FindBlock(logical, fRootBlock) != B_OK) {
|
if (fVolume->FindBlock(logical, fRootBlock) != B_OK) {
|
||||||
ERROR("Find() unmapped block %" B_PRId64 "\n", fRootBlock);
|
ERROR("Find() unmapped block %" B_PRId64 "\n", fRootBlock);
|
||||||
return B_ERROR;
|
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;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
BTree::SetRoot(Node* root)
|
||||||
|
{
|
||||||
|
fRootBlock = root->BlockNum();
|
||||||
|
fLogicalRoot = root->LogicalAddress();
|
||||||
|
fRootLevel = root->Level();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
BTree::_AddIterator(TreeIterator* iterator)
|
BTree::_AddIterator(TreeIterator* iterator)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -27,6 +27,9 @@ enum btree_traversing {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class Transaction;
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark - in-memory structures
|
// #pragma mark - in-memory structures
|
||||||
|
|
||||||
|
|
||||||
@@ -44,6 +47,7 @@ struct node_and_key {
|
|||||||
class BTree {
|
class BTree {
|
||||||
public:
|
public:
|
||||||
class Path;
|
class Path;
|
||||||
|
class Node;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
BTree(Volume* volume);
|
BTree(Volume* volume);
|
||||||
@@ -71,8 +75,10 @@ public:
|
|||||||
|
|
||||||
Volume* SystemVolume() const { return fVolume; }
|
Volume* SystemVolume() const { return fVolume; }
|
||||||
status_t SetRoot(off_t logical, fsblock_t* block);
|
status_t SetRoot(off_t logical, fsblock_t* block);
|
||||||
|
void SetRoot(Node* root);
|
||||||
fsblock_t RootBlock() const { return fRootBlock; }
|
fsblock_t RootBlock() const { return fRootBlock; }
|
||||||
off_t LogicalRoot() const { return fLogicalRoot; }
|
off_t LogicalRoot() const { return fLogicalRoot; }
|
||||||
|
uint8 RootLevel() const { return fRootLevel; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
BTree(const BTree& other);
|
BTree(const BTree& other);
|
||||||
@@ -90,6 +96,7 @@ private:
|
|||||||
|
|
||||||
fsblock_t fRootBlock;
|
fsblock_t fRootBlock;
|
||||||
off_t fLogicalRoot;
|
off_t fLogicalRoot;
|
||||||
|
uint8 fRootLevel;
|
||||||
Volume* fVolume;
|
Volume* fVolume;
|
||||||
mutex fIteratorLock;
|
mutex fIteratorLock;
|
||||||
SinglyLinkedList<TreeIterator> fIterators;
|
SinglyLinkedList<TreeIterator> fIterators;
|
||||||
@@ -114,6 +121,12 @@ public:
|
|||||||
{ return fNode->header.ItemCount(); }
|
{ return fNode->header.ItemCount(); }
|
||||||
uint8 Level() const
|
uint8 Level() const
|
||||||
{ return fNode->header.Level(); }
|
{ 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
|
btrfs_index* Index(uint32 i) const
|
||||||
{ return &fNode->index[i]; }
|
{ return &fNode->index[i]; }
|
||||||
@@ -171,6 +184,10 @@ public:
|
|||||||
|
|
||||||
int Move(int level, int step);
|
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; }
|
BTree* Tree() const { return fTree; }
|
||||||
private:
|
private:
|
||||||
Path(const Path&);
|
Path(const Path&);
|
||||||
|
|||||||
@@ -110,6 +110,13 @@ struct btrfs_header {
|
|||||||
uint32 ItemCount() const
|
uint32 ItemCount() const
|
||||||
{ return B_LENDIAN_TO_HOST_INT32(item_count); }
|
{ return B_LENDIAN_TO_HOST_INT32(item_count); }
|
||||||
uint8 Level() const { return level; }
|
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;
|
} _PACKED;
|
||||||
|
|
||||||
|
|
||||||
@@ -121,6 +128,11 @@ struct btrfs_index {
|
|||||||
{ return B_LENDIAN_TO_HOST_INT64(logical_address); }
|
{ return B_LENDIAN_TO_HOST_INT64(logical_address); }
|
||||||
uint64 Generation() const
|
uint64 Generation() const
|
||||||
{ return B_LENDIAN_TO_HOST_INT64(generation); }
|
{ 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;
|
} _PACKED;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user