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

View File

@ -20,7 +20,7 @@ Block::TransferFrom(Block& other)
fVolume = other.fVolume;
fData = other.fData;
fIndex = other.fIndex;
fWritable = other.fWritable;
fTransaction = other.fTransaction;
other.fVolume = NULL;
other.fData = NULL;
@ -33,7 +33,7 @@ Block::GetReadable(Volume* volume, uint64 blockIndex)
Put();
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();
status_t error = transaction.RegisterBlock(blockIndex);
if (error != B_OK)
return error;
return _Init(volume, blockIndex,
block_cache_get_writable(volume->BlockCache(), blockIndex,
transaction.ID()),
true);
&transaction);
}
@ -54,10 +58,14 @@ Block::GetZero(Volume* volume, uint64 blockIndex, Transaction& transaction)
{
Put();
status_t error = transaction.RegisterBlock(blockIndex);
if (error != B_OK)
return error;
return _Init(volume, blockIndex,
block_cache_get_empty(volume->BlockCache(), blockIndex,
transaction.ID()),
true);
&transaction);
}
@ -66,15 +74,21 @@ Block::MakeWritable(Transaction& transaction)
{
if (fVolume == NULL)
return B_BAD_VALUE;
if (fWritable)
if (fTransaction != NULL)
return B_OK;
status_t error = block_cache_make_writable(fVolume->BlockCache(),
fIndex, transaction.ID());
status_t error = transaction.RegisterBlock(fIndex);
if (error != B_OK)
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;
}
@ -83,6 +97,9 @@ void
Block::Put()
{
if (fVolume != NULL) {
if (fTransaction != NULL)
fTransaction->PutBlock(fIndex, fData);
block_cache_put(fVolume->BlockCache(), fIndex);
fVolume = NULL;
fData = NULL;
@ -91,7 +108,8 @@ Block::Put()
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)
return false;
@ -99,7 +117,7 @@ Block::_Init(Volume* volume, uint64 blockIndex, const void* data, bool writable)
fVolume = volume;
fData = const_cast<void*>(data);
fIndex = blockIndex;
fWritable = writable;
fTransaction = transaction;
return true;
}

View File

@ -35,13 +35,13 @@ public:
private:
bool _Init(Volume* volume, uint64 blockIndex,
const void* data, bool writable);
const void* data, Transaction* transaction);
private:
Volume* fVolume;
void* fData;
uint64 fIndex;
bool fWritable;
Transaction* fTransaction;
};

View File

@ -6,8 +6,12 @@
#include "Transaction.h"
#include <errno.h>
#include <algorithm>
#include <AutoDeleter.h>
#include "BlockAllocator.h"
#include "Volume.h"
@ -29,6 +33,8 @@ swap_if_greater(Node*& a, Node*& b)
Transaction::Transaction(Volume* volume)
:
fVolume(volume),
fSHA256(NULL),
fCheckSum(NULL),
fID(-1)
{
}
@ -37,6 +43,9 @@ Transaction::Transaction(Volume* volume)
Transaction::~Transaction()
{
Abort();
delete fCheckSum;
delete fSHA256;
}
@ -45,6 +54,22 @@ Transaction::Start()
{
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();
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
status_t error = cache_end_transaction(fVolume->BlockCache(), fID, NULL,
NULL);
error = cache_end_transaction(fVolume->BlockCache(), fID, NULL, NULL);
if (error != B_OK) {
Abort();
return error;
@ -128,9 +169,24 @@ Transaction::Abort()
info->node->RevertNodeData(info->oldNodeData);
}
// revert the block check sums
_RevertBlockCheckSums();
// clean up
// delete the node infos
_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->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::_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

View File

@ -9,7 +9,10 @@
#include <fs_cache.h>
#include <util/DoublyLinkedList.h>
#include <util/OpenHashTable.h>
#include "CheckSum.h"
#include "driver/checksum_device.h"
#include "Node.h"
@ -59,6 +62,9 @@ public:
bool IsNodeLocked(Node* node) const
{ return _GetNodeInfo(node) != NULL; }
status_t RegisterBlock(uint64 blockIndex);
void PutBlock(uint64 blockIndex, const void* data);
private:
struct NodeInfo : DoublyLinkedListLinkImpl<NodeInfo> {
Node* node;
@ -68,16 +74,57 @@ private:
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:
NodeInfo* _GetNodeInfo(Node* node) const;
void _DeleteNodeInfosAndUnlock(bool failed);
void _DeleteNodeInfoAndUnlock(NodeInfo* info,
bool failed);
status_t _UpdateBlockCheckSums();
status_t _RevertBlockCheckSums();
private:
Volume* fVolume;
SHA256* fSHA256;
checksum_device_ioctl_check_sum* fCheckSum;
int32 fID;
NodeInfoList fNodeInfos;
BlockInfoTable fBlockInfos;
uint64 fOldFreeBlockCount;
};