Block does now register writable blocks with Transaction. On commit

Transaction sets the check sums for the changed blocks on the underlying
device, respectively reverts them on roll-back.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@37639 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold
2010-07-20 23:21:26 +00:00
parent 8d591ecc41
commit 9bc87c767b
4 changed files with 254 additions and 14 deletions
@@ -20,7 +20,7 @@ Block::TransferFrom(Block& other)
fVolume = other.fVolume; fVolume = other.fVolume;
fData = other.fData; fData = other.fData;
fIndex = other.fIndex; fIndex = other.fIndex;
fWritable = other.fWritable; fTransaction = other.fTransaction;
other.fVolume = NULL; other.fVolume = NULL;
other.fData = NULL; other.fData = NULL;
@@ -33,7 +33,7 @@ Block::GetReadable(Volume* volume, uint64 blockIndex)
Put(); Put();
return _Init(volume, blockIndex, return _Init(volume, blockIndex,
block_cache_get(volume->BlockCache(), blockIndex), false); block_cache_get(volume->BlockCache(), blockIndex), NULL);
} }
@@ -42,10 +42,14 @@ Block::GetWritable(Volume* volume, uint64 blockIndex, Transaction& transaction)
{ {
Put(); Put();
status_t error = transaction.RegisterBlock(blockIndex);
if (error != B_OK)
return error;
return _Init(volume, blockIndex, return _Init(volume, blockIndex,
block_cache_get_writable(volume->BlockCache(), blockIndex, block_cache_get_writable(volume->BlockCache(), blockIndex,
transaction.ID()), transaction.ID()),
true); &transaction);
} }
@@ -54,10 +58,14 @@ Block::GetZero(Volume* volume, uint64 blockIndex, Transaction& transaction)
{ {
Put(); Put();
status_t error = transaction.RegisterBlock(blockIndex);
if (error != B_OK)
return error;
return _Init(volume, blockIndex, return _Init(volume, blockIndex,
block_cache_get_empty(volume->BlockCache(), blockIndex, block_cache_get_empty(volume->BlockCache(), blockIndex,
transaction.ID()), transaction.ID()),
true); &transaction);
} }
@@ -66,15 +74,21 @@ Block::MakeWritable(Transaction& transaction)
{ {
if (fVolume == NULL) if (fVolume == NULL)
return B_BAD_VALUE; return B_BAD_VALUE;
if (fWritable) if (fTransaction != NULL)
return B_OK; return B_OK;
status_t error = block_cache_make_writable(fVolume->BlockCache(), status_t error = transaction.RegisterBlock(fIndex);
fIndex, transaction.ID());
if (error != B_OK) if (error != B_OK)
return error; return error;
fWritable = true; error = block_cache_make_writable(fVolume->BlockCache(), fIndex,
transaction.ID());
if (error != B_OK) {
transaction.PutBlock(fIndex, NULL);
return error;
}
fTransaction = &transaction;
return B_OK; return B_OK;
} }
@@ -83,6 +97,9 @@ void
Block::Put() Block::Put()
{ {
if (fVolume != NULL) { if (fVolume != NULL) {
if (fTransaction != NULL)
fTransaction->PutBlock(fIndex, fData);
block_cache_put(fVolume->BlockCache(), fIndex); block_cache_put(fVolume->BlockCache(), fIndex);
fVolume = NULL; fVolume = NULL;
fData = NULL; fData = NULL;
@@ -91,7 +108,8 @@ Block::Put()
bool bool
Block::_Init(Volume* volume, uint64 blockIndex, const void* data, bool writable) Block::_Init(Volume* volume, uint64 blockIndex, const void* data,
Transaction* transaction)
{ {
if (data == NULL) if (data == NULL)
return false; return false;
@@ -99,7 +117,7 @@ Block::_Init(Volume* volume, uint64 blockIndex, const void* data, bool writable)
fVolume = volume; fVolume = volume;
fData = const_cast<void*>(data); fData = const_cast<void*>(data);
fIndex = blockIndex; fIndex = blockIndex;
fWritable = writable; fTransaction = transaction;
return true; return true;
} }
@@ -35,13 +35,13 @@ public:
private: private:
bool _Init(Volume* volume, uint64 blockIndex, bool _Init(Volume* volume, uint64 blockIndex,
const void* data, bool writable); const void* data, Transaction* transaction);
private: private:
Volume* fVolume; Volume* fVolume;
void* fData; void* fData;
uint64 fIndex; uint64 fIndex;
bool fWritable; Transaction* fTransaction;
}; };
@@ -6,8 +6,12 @@
#include "Transaction.h" #include "Transaction.h"
#include <errno.h>
#include <algorithm> #include <algorithm>
#include <AutoDeleter.h>
#include "BlockAllocator.h" #include "BlockAllocator.h"
#include "Volume.h" #include "Volume.h"
@@ -29,6 +33,8 @@ swap_if_greater(Node*& a, Node*& b)
Transaction::Transaction(Volume* volume) Transaction::Transaction(Volume* volume)
: :
fVolume(volume), fVolume(volume),
fSHA256(NULL),
fCheckSum(NULL),
fID(-1) fID(-1)
{ {
} }
@@ -37,6 +43,9 @@ Transaction::Transaction(Volume* volume)
Transaction::~Transaction() Transaction::~Transaction()
{ {
Abort(); Abort();
delete fCheckSum;
delete fSHA256;
} }
@@ -45,6 +54,22 @@ Transaction::Start()
{ {
ASSERT(fID < 0); ASSERT(fID < 0);
status_t error = fBlockInfos.Init();
if (error != B_OK)
return error;
if (fSHA256 == NULL) {
fSHA256 = new(std::nothrow) SHA256;
if (fSHA256 == NULL)
return B_NO_MEMORY;
}
if (fCheckSum == NULL) {
fCheckSum = new(std::nothrow) checksum_device_ioctl_check_sum;
if (fCheckSum == NULL)
return B_NO_MEMORY;
}
fVolume->TransactionStarted(); fVolume->TransactionStarted();
fID = cache_start_transaction(fVolume->BlockCache()); fID = cache_start_transaction(fVolume->BlockCache());
@@ -87,9 +112,25 @@ Transaction::Commit(const PostCommitNotification* notification1,
} }
} }
// Make sure the previous transaction is on disk. This is not particularly
// performance friendly, but prevents race conditions between us setting
// the new block check sums and the block writer deciding to write back the
// old block data.
status_t error = block_cache_sync(fVolume->BlockCache());
if (error != B_OK) {
Abort();
return error;
}
// compute the new block check sums
error = _UpdateBlockCheckSums();
if (error != B_OK) {
Abort();
return error;
}
// commit the cache transaction // commit the cache transaction
status_t error = cache_end_transaction(fVolume->BlockCache(), fID, NULL, error = cache_end_transaction(fVolume->BlockCache(), fID, NULL, NULL);
NULL);
if (error != B_OK) { if (error != B_OK) {
Abort(); Abort();
return error; return error;
@@ -128,9 +169,24 @@ Transaction::Abort()
info->node->RevertNodeData(info->oldNodeData); info->node->RevertNodeData(info->oldNodeData);
} }
// revert the block check sums
_RevertBlockCheckSums();
// clean up // clean up
// delete the node infos
_DeleteNodeInfosAndUnlock(true); _DeleteNodeInfosAndUnlock(true);
// delete the block infos
BlockInfo* blockInfo = fBlockInfos.Clear(true);
while (blockInfo != NULL) {
BlockInfo* nextInfo = blockInfo->hashNext;
block_cache_put(fVolume->BlockCache(),
blockInfo->indexAndCheckSum.blockIndex);
delete nextInfo;
blockInfo = nextInfo;
}
fVolume->GetBlockAllocator()->ResetFreeBlocks(fOldFreeBlockCount); fVolume->GetBlockAllocator()->ResetFreeBlocks(fOldFreeBlockCount);
fVolume->TransactionFinished(); fVolume->TransactionFinished();
@@ -228,6 +284,77 @@ Transaction::KeepNode(Node* node)
} }
status_t
Transaction::RegisterBlock(uint64 blockIndex)
{
ASSERT(fID >= 0);
// look it up -- maybe it's already registered
BlockInfo* info = fBlockInfos.Lookup(blockIndex);
if (info != NULL) {
info->refCount++;
return B_OK;
}
// nope, create a new one
info = new(std::nothrow) BlockInfo;
if (info == NULL)
return B_NO_MEMORY;
ObjectDeleter<BlockInfo> infoDeleter(info);
info->indexAndCheckSum.blockIndex = blockIndex;
info->refCount = 1;
info->dirty = false;
// get the old check sum
if (ioctl(fVolume->FD(), CHECKSUM_DEVICE_IOCTL_GET_CHECK_SUM,
&info->indexAndCheckSum, sizeof(info->indexAndCheckSum)) < 0) {
return errno;
}
// get the data (we're fine with read-only)
info->data = block_cache_get(fVolume->BlockCache(), blockIndex);
if (info->data == NULL) {
delete info;
return B_ERROR;
}
fBlockInfos.Insert(infoDeleter.Detach());
return B_OK;
}
void
Transaction::PutBlock(uint64 blockIndex, const void* data)
{
ASSERT(fID >= 0);
BlockInfo* info = fBlockInfos.Lookup(blockIndex);
if (info == NULL) {
panic("checksumfs: Transaction::PutBlock(): unknown block %" B_PRIu64,
blockIndex);
return;
}
if (info->refCount == 0) {
panic("checksumfs: Unbalanced Transaction::PutBlock(): for block %"
B_PRIu64, blockIndex);
return;
}
info->dirty |= data != NULL;
if (--info->refCount == 0 && !info->dirty) {
// block wasn't got successfully -- remove the info
fBlockInfos.Remove(info);
block_cache_put(fVolume->BlockCache(),
info->indexAndCheckSum.blockIndex);
delete info;
}
}
Transaction::NodeInfo* Transaction::NodeInfo*
Transaction::_GetNodeInfo(Node* node) const Transaction::_GetNodeInfo(Node* node) const
{ {
@@ -267,6 +394,54 @@ Transaction::_DeleteNodeInfoAndUnlock(NodeInfo* info, bool failed)
} }
status_t
Transaction::_UpdateBlockCheckSums()
{
for (BlockInfoTable::Iterator it = fBlockInfos.GetIterator();
BlockInfo* info = it.Next();) {
if (info->refCount > 0) {
panic("checksumfs: Transaction::Commit(): block %" B_PRIu64
" still referenced", info->indexAndCheckSum.blockIndex);
}
if (!info->dirty)
continue;
// compute the check sum
fSHA256->Init();
fSHA256->Update(info->data, B_PAGE_SIZE);
fCheckSum->blockIndex = info->indexAndCheckSum.blockIndex;
fCheckSum->checkSum = fSHA256->Digest();
// set it
if (ioctl(fVolume->FD(), CHECKSUM_DEVICE_IOCTL_SET_CHECK_SUM, fCheckSum,
sizeof(*fCheckSum)) < 0) {
return errno;
}
}
return B_OK;
}
status_t
Transaction::_RevertBlockCheckSums()
{
for (BlockInfoTable::Iterator it = fBlockInfos.GetIterator();
BlockInfo* info = it.Next();) {
if (!info->dirty)
continue;
// set the old check sum
if (ioctl(fVolume->FD(), CHECKSUM_DEVICE_IOCTL_SET_CHECK_SUM,
&info->indexAndCheckSum, sizeof(info->indexAndCheckSum)) < 0) {
return errno;
}
}
return B_OK;
}
// #pragma mark - PostCommitNotification // #pragma mark - PostCommitNotification
@@ -9,7 +9,10 @@
#include <fs_cache.h> #include <fs_cache.h>
#include <util/DoublyLinkedList.h> #include <util/DoublyLinkedList.h>
#include <util/OpenHashTable.h>
#include "CheckSum.h"
#include "driver/checksum_device.h"
#include "Node.h" #include "Node.h"
@@ -59,6 +62,9 @@ public:
bool IsNodeLocked(Node* node) const bool IsNodeLocked(Node* node) const
{ return _GetNodeInfo(node) != NULL; } { return _GetNodeInfo(node) != NULL; }
status_t RegisterBlock(uint64 blockIndex);
void PutBlock(uint64 blockIndex, const void* data);
private: private:
struct NodeInfo : DoublyLinkedListLinkImpl<NodeInfo> { struct NodeInfo : DoublyLinkedListLinkImpl<NodeInfo> {
Node* node; Node* node;
@@ -68,16 +74,57 @@ private:
typedef DoublyLinkedList<NodeInfo> NodeInfoList; typedef DoublyLinkedList<NodeInfo> NodeInfoList;
struct BlockInfo {
checksum_device_ioctl_check_sum indexAndCheckSum;
BlockInfo* hashNext;
const void* data;
int32 refCount;
bool dirty;
};
struct BlockInfoHashDefinition {
typedef uint64 KeyType;
typedef BlockInfo ValueType;
size_t HashKey(uint64 key) const
{
return (size_t)key;
}
size_t Hash(const BlockInfo* value) const
{
return HashKey(value->indexAndCheckSum.blockIndex);
}
bool Compare(uint64 key, const BlockInfo* value) const
{
return value->indexAndCheckSum.blockIndex == key;
}
BlockInfo*& GetLink(BlockInfo* value) const
{
return value->hashNext;
}
};
typedef BOpenHashTable<BlockInfoHashDefinition> BlockInfoTable;
private: private:
NodeInfo* _GetNodeInfo(Node* node) const; NodeInfo* _GetNodeInfo(Node* node) const;
void _DeleteNodeInfosAndUnlock(bool failed); void _DeleteNodeInfosAndUnlock(bool failed);
void _DeleteNodeInfoAndUnlock(NodeInfo* info, void _DeleteNodeInfoAndUnlock(NodeInfo* info,
bool failed); bool failed);
status_t _UpdateBlockCheckSums();
status_t _RevertBlockCheckSums();
private: private:
Volume* fVolume; Volume* fVolume;
SHA256* fSHA256;
checksum_device_ioctl_check_sum* fCheckSum;
int32 fID; int32 fID;
NodeInfoList fNodeInfos; NodeInfoList fNodeInfos;
BlockInfoTable fBlockInfos;
uint64 fOldFreeBlockCount; uint64 fOldFreeBlockCount;
}; };